实体框架 TPT 删除基类型

本文关键字:基类 类型 删除 TPT 框架 实体 | 更新日期: 2023-09-27 18:34:36

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

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);
    }
}

这个问题有解决方案吗?还是找不到

实体框架 TPT 删除基类型

我建议更改EF模型设置并设置.HasOptional(t => t.Folder(.这真的很奇怪 EF 行为。但是,当您加载与文件夹相关的信件与不加载时的情况有所不同。这里有一个文章的链接,解释了使用级联删除的不同之处。

无论如何。HasOptional(t => t.Folder( 应该可以解决此问题。而且你不能改变

    public int FolderId {get; set;} 

    public int? FolderId {get; set;}

编辑:

如果 HasOptional(t => t.Folder( 不可接受,则有两个选项:

  1. ====
  2. =========

Set 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. =========

Set WillCascadeOnDelete(true(。不要为应删除的文件夹加载任何字母。删除文件夹。例如:

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

编辑2:我使用相同的 EF 模型类(代码优先(创建一些测试。所有工作都没有错误。我使用最新的 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();
    }

编辑3:我更改了文件夹模型类。

     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.* 中可能是不同的 bihavior。我使用最新的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();
        }