实体框架TPT删除基本类型

时间:2016-02-14 16:51:41

标签: c# entity-framework ef-code-first entity-framework-6

我的应用程序中有以下模型:

public interface IBaseEntityObject 
{
    public int Id {get; set;}
}


public abstract class BaseEntityObject : IBaseEntityObject
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
}


public class Folder : BaseEntityObject
{   
    [DataMember]
    public string Name {get; set;}

    [DataMember]
    public virtual List<Letter> Letters {get; set;} 
}


public abstract class Letter : BaseEntityObject
{   
    [DataMember]
    public string Title {get; set;}

    [DataMember]
    public string Content {get; set;}

    public virtual Folder Folder {get; set;}

    [DataMember]
    public int FolderId {get; set;}

    [DataMember]
    public DateTime CreationDate {get; set;}
}

public class OutgoingLetter : Letter
{
    // .. OutgoingLetter properties
}

public class ReceviedLetter : Letter
{
    // .. ReceviedLetter properties
}


public class MyDbContext : DbContext
{
    public DbSet<Folder> Folders {get; set;}

    public DbSet<Letter> Letters {get; set;}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Folder <-> Letters       
        modelBuilder.Entity<Letter>()
        .HasRequired(t => t.Folder)
        .WithMany(f => f.Letters)
        .HasForeignKey(t => t.FolderId)
        .WillCascadeOnDelete(true);
    }
}

但是,无论我尝试什么,删除文件夹都会导致访问密钥违规。

之后,我尝试删除文件夹中的所有字母,然后删除它(我不喜欢) 它导致了另一个异常 - 非可空成员的空值。

删除文件夹的正确和最有效的方法是什么?

修改

我尝试删除文件夹的代码:

public abstract class EFRepository<T> : IRepository<T>
{
    protected readonly DbContext Context;

    public EFRepository(DbContext context)
    {
        Context = context;
    }

    public abstract List<T> Get();

    public void Add(T item)
    {
        Context.Set<T>().Add(item);
    }

    public virtual Remove(T item)
    {
        Context.Set<T>().Remove(item);
    }

    public void Update(T item)
    {
        Context.Entry(item).State = EntityState.Modified;
    }

    public void Dispose()
    {
        Context.Dispose();
    }

    public int SaveChanges()
    {
        return Context.SaveChanges();
    }

    public T FindById(int id)
    {
        return Context.Set<T>().Find(id); 
    }
}

public FoldersRepository : EFRepository<Folder>
{
    public FoldersRepository(DbContext context) : base(context) {}

    public void override Remove(Folder folder)
    {
        var letters = folder.Letters;

        for (int index = 0 ; index < letters.Count; index++)
        {
            Context.Set<Letter>().Remove(letters[0]);
        }

        base.Remove(folder);
    }
}

这个问题有解决方法吗? 仍然找不到一个

1 个答案:

答案 0 :(得分:1)

我建议更改EF模型设置并设置.HasOptional(t =&gt; t.Folder)。 EF行为真的很奇怪。但是当您加载与文件夹相关的字母时,情况之间存在差异。这是指向解释此差异Working with Cascade Delete的文章的链接。

无论如何.HasOptional(t =&gt; t.Folder)应该解决问题。 你无法改变

    public int FolderId {get; set;} 

    public int? FolderId {get; set;}

修改

如果HasOptional(t =&gt; t.Folder)不支持,您有两种选择:

  1. =============
  2. 设置WillCascadeOnDelete( false )。加载所有文件夹的字母,删除所有字母和文件夹。对于考试

        var folder = Context.Set<Folder>().Include(f => f.Letters).First(f => f.Id==id);
    
        foreach(var letter in folder.Letters)
            Context.Set<Letter>().Remove(letter);
    
        Context.Set<Folder>().Remove(folder);
        Context.SaveChanges();
    
    1. =============
    2. 设置WillCascadeOnDelete( true )。不要为要删除的文件夹加载任何字母。删除文件夹。例如:

          var folder = new Folder(){ Id = id };
          Context.Set<Folder>().Attach(folder);
          Context.Set<Folder>().Remove(folder);
          Context.SaveChanges();
      

      <强> EDIT2: 我使用相同的EF模型类(Code First)创建了一些测试。一切正常,没有错误。我使用最新的EntityFramework 6.1.3版本。 以下是测试的源代码:

          public class Folder {
             [Key]
             [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
             public int Id { get; set; }
             public string Name { get; set; }
             public List<Letter> Letters { get; set; } 
          }
      
          public class Letter {
             [Key]
             [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
             public int Id { get; set; }
             public string Title { get; set; }
             public string Content { get; set; }
             public virtual Folder Folder { get; set; }
             public int FolderId { get; set; }
             public DateTime CreationDate { get; set; }
         }
      
         public class OutgoingLetter : Letter {
             public string AddressTo { get; set; }
         }
      
         public class ReceviedLetter : Letter {
             public string AddressFrom { get; set; }
         }
      
         public class MyDbContext : DbContext {
             public virtual DbSet<Folder> Folders { get; set; }
      
             public virtual DbSet<Letter> Letters { get; set; }
      
             protected override void OnModelCreating(DbModelBuilder modelBuilder)
             {
                 base.OnModelCreating(modelBuilder);
      
                 // Folder <-> Letters       
                 modelBuilder.Entity<Letter>()
                 .HasRequired(t => t.Folder)
                 .WithMany(f => f.Letters)
                 .HasForeignKey(t => t.FolderId)
                 .WillCascadeOnDelete(true);
             }
         }
         // ...........................................
      
         // TODO: Insert three Folders and related Letters.
         // Delete Folders and Leterrs in a three different ways.
         // In all cases Letters deleted throught WillCascadeOnDelete constraint.
         static void Main(string[] args)
         {
              using (var dtCntx = new MyDbContext())
              {
                  var folder1 = new Folder() { Name = "Folder1" };
                  var letters1 = new List<Letter>() { 
                      new OutgoingLetter{Title = "Folder1-Letter1", CreationDate=DateTime.Now, Folder=folder1 },
                      new ReceviedLetter{Title = "Folder1-Letter2", CreationDate=DateTime.Now, Folder=folder1 }
                  };
      
                  var folder2 = new Folder() { Name = "Folder2" };
                  var letters2 = new List<Letter>() { 
                      new OutgoingLetter{Title = "Folder2-Letter1", CreationDate=DateTime.Now, Folder=folder2 },
                      new ReceviedLetter{Title = "Folder2-Letter2", CreationDate=DateTime.Now, Folder=folder2 }
                  };
      
                  var folder3 = new Folder() { Name = "Folder3" };
                  var letters3 = new List<Letter>() { 
                      new OutgoingLetter{Title = "Folder3-Letter1", CreationDate=DateTime.Now, Folder=folder3 },
                      new ReceviedLetter{Title = "Folder3-Letter2", CreationDate=DateTime.Now, Folder=folder3 }
                  };
      
                  dtCntx.Folders.Add(folder1);
                  dtCntx.Letters.AddRange(letters1);
      
                  dtCntx.Folders.Add(folder2);
                  dtCntx.Letters.AddRange(letters2);
      
                  dtCntx.Folders.Add(folder3);
                  dtCntx.Letters.AddRange(letters3);
      
                  dtCntx.SaveChanges();
              }
      
              int id = 0;
              using (var dtCntx = new MyDbContext())
                  id = dtCntx.Folders.First().Id;
      
              // Remove [Folder] and related [Letters] without loading [Folder] from DB
              using (var dtCntx = new MyDbContext())
              {
                  var folder = new Folder { Id = id };
                  dtCntx.Folders.Attach(folder);
                  dtCntx.Folders.Remove(folder);
                  dtCntx.SaveChanges();
              }
              // Load [Folder] from DB and delete it
              using (var dtCntx = new MyDbContext())
              {
                  var folder = dtCntx.Folders.FirstOrDefault();
                  dtCntx.Folders.Remove(folder);
                  dtCntx.SaveChanges();
              }
              // Load [Folder] and all related [Letters]. Delete [Folder] and [Letters]
              using (var dtCntx = new MyDbContext())
              {
                  var folder = dtCntx.Folders.Include(f => f.Letters).FirstOrDefault();
                  dtCntx.Folders.Remove(folder);
                  dtCntx.SaveChanges();
              }
      
              Console.WriteLine("Successfully !!!");
              Console.ReadKey();
          }
      

      <强> EDIT3: 我改变了文件夹模型类。

           public class Folder
           {
             [Key]
             [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
             public int Id { get; set; }
             public string Name { get; set; }
             public virtual ICollection<Letter> Letters { get; set; }
      
             public override bool Equals(object obj)
             {
                var folder = obj as Folder;
                return folder.Id == this.Id && folder.Name == this.Name &&
                  folder.Letters.SequenceEqual(this.Letters);
             }
             public override int GetHashCode()
             {
                return String.Concat(GetHashParts()).GetHashCode();
             }
      
             private IEnumerable<string> GetHashParts()
             {
                yield return Id.ToString();
                yield return Name;
                foreach (var letter in Letters) {
                    yield return "_";
                    yield return letter.Id.ToString();
                }
             }
             public static bool operator ==(Folder x, Folder y)
             {
                 return x.Equals(y);
             }
      
             public static bool operator !=(Folder x, Folder y)
             {
                return !x.Equals(y);
             }
         }
      

      我重写了Equals,== opeartor和GetHashCode。所有旧测试再次成功完成。但是没有调用任何新函数。我知道EF的行为在最新版本中发生了变化。在EF 5. *中可能是不同的行为。我使用lates EF 6.1.3。我稍微更改了测试并添加了一个代码以引起Equals函数调用。但代码可以再次正常工作。

           using (var dtCntx = new MyDbContext())
           {
                  var folder = dtCntx.Folders.Include(f => f.Letters).FirstOrDefault();
      
                  var folder2 = dtCntx.Folders.FirstOrDefault(f => f.Id == folder.Id);
                  if (folder == folder2) Console.WriteLine("Equals"); // Call Folder.Equals
      
                  dtCntx.Folders.Remove(folder);
                  dtCntx.SaveChanges();
              }