【问题标题】:Entity Framework TPT Removing base type实体框架 TPT 删除基本类型
【发布时间】:2016-02-14 16:51:40
【问题描述】:

我的应用中有以下模型:

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

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

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

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

编辑

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

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

这个问题有解决办法吗? 还是找不到

【问题讨论】:

  • 删除“文件夹”时,是否首先删除与该“文件夹”关联的每个“字母”?
  • 起初我尝试不删除它(如果可能的话,我会非常喜欢)之后,我尝试在删除之前删除文件夹中的所有字母(我不喜欢),结果在另一个例外中 - 不可为空的成员的 Null 值。
  • 请发布您用于删除的代码以及您得到的确切异常
  • 如果每个 Letter 行都有一个外键指向 Folder 行,只要还有 Letter 指向它,您就无法删除该 Folder 行。
  • EF 模型看起来不错。 WillCascadeOnDelete(true) 应该可以完成这项工作。您能否显示代码如何删除文件夹?

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


【解决方案1】:

我建议更改 EF 模型设置并设置 .HasOptional(t => t.Folder)。这真是奇怪的 EF 行为。但是,当您加载与文件夹相关的信件时,情况有所不同。这里有一个文章链接,可以解释这种差异Working with Cascade Delete

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

    public int FolderId {get; set;} 

    public int? FolderId {get; set;}

编辑:

如果 HasOptional(t => t.Folder) 不可接受,您有两种选择:

  1. =============

设置 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. =============

设置 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、== 运算符和 GetHashCode。所有旧测试再次成功完成。但是没有一个新函数没有被调用。我知道 EF 的行为在最新版本中发生了变化。在 EF 5.* 中可能存在不同的行为。我使用最新的 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();
        }

【讨论】:

  • .HasOptional 将允许添加没有 FolderId 的字母,不是吗?我宁愿不允许它
  • 第二个选项让我访问密钥冲突
  • 奇怪...我创建完全相同的 EF 模型类并进行测试(除了,我不使用存储库模式)。所有测试都顺利通过。我将更新答案以显示测试代码。
  • 我认为这是因为您没有在文件夹模型中的字母列表之前放置虚拟关键字,因此没有启用延迟加载。您可以尝试添加虚拟并再次运行它吗?谢谢!
  • 好的。我在文件夹类中将public List&lt;Letter&gt; Letters { get; set; } 更改为public virtual List&lt;Letter&gt; Letters { get; set; } 并再次进行测试。所有测试都成功通过。之后,我将 List 更改为 ICollection (public virtual ICollection&lt;Letter&gt; Letters { get; set; }) 并再次成功完成所有测试。我猜是其他地方的问题......
猜你喜欢
  • 2018-07-21
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-11
  • 1970-01-01
相关资源
最近更新 更多