EF 4.1 CodeFirst - 一对多级联删除

时间:2012-03-16 17:19:59

标签: entity-framework-4.1 ef-code-first one-to-many

我正在尝试从父集合导航属性中删除子实体。 b / t父母和孩子之间建立了一对多的关系。删除子项后,我希望数据库删除assoc。来自数据库的子记录,而不是通过使外键无效而孤立的记录。

有没有办法在不必通过DBContext中的子DbSet显式删除子项的情况下执行此操作?

我见过与此主题相关的其他帖子,但我认为我会将代码提炼为更简单的测试用例:

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using NUnit.Framework;

namespace basic_tests
{
[TestFixture]
public class OneToManyTests
{
    #region Setup/Teardown

    [SetUp]
    public void SetUp()
    {
        _context = new Context();
        Database.SetInitializer(new DataInitializer());
    }

    #endregion

    private Context _context;

    [Test]
    public void CanRemoveChildThroughParent()
    {
        /**

        this throws : "System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while saving entities that do not expose foreign key properties for              their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of               exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for                  
         details.System.Data.UpdateException : A relationship from the 'Child_MyParent' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a 
         corresponding 'Child_MyParent_Source' must also in the 'Deleted' state.

         **/

        var parent = _context.Parents.FirstOrDefault();
        var firstChild = parent.MyChildren.FirstOrDefault();
        parent.MyChildren.Remove(firstChild);

        _context.SaveChanges();

        var parentRefresh = new Context().Parents.FirstOrDefault();
        Assert.AreEqual(1, parentRefresh.MyChildren.Count);

        var childrenCount = new Context().Children.Count();
        Assert.AreEqual(1, childrenCount);
    }
}

public class Parent
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Child> MyChildren { get; set; }
}

public class Child
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Parent MyParent { get; set; }
}

public class Context : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Child>()
            .HasRequired(c => c.MyParent)
            .WithMany(p => p.MyChildren)
            .WillCascadeOnDelete(true);
    }
}

public class DataInitializer : DropCreateDatabaseAlways<Context>
{
    protected override void Seed(Context context)
    {
        for (var i = 0; i < 2; i++)
        {
            context.Children.Add(new Child
                                     {
                                         Name = "child" + i
                                     });
        }

        var parent = new Parent { Name = "parent", MyChildren = context.Children.Local.ToList() };

        context.Parents.Add(parent);

        base.Seed(context);
    }
}
}

2 个答案:

答案 0 :(得分:5)

  

有没有办法做到这一点,而无需明确删除   子进程通过DBContext中的子DbSet?

在你的模特中:不,别无他法。你必须打电话:

var parent = _context.Parents.FirstOrDefault();
var firstChild = parent.MyChildren.FirstOrDefault();
_context.Children.Remove(firstChild);

_context.SaveChanges();

我最近了解到,当您从父集合中删除子项时,有一个例外会导致数据库中的自动删除。这就是所谓的识别关系,它要求引用父项的子项中的外键属性必须是子项的(复合)主键的一部分:

public class Child
{
    [Key, Column(Order = 0)]
    public virtual int Id { get; set; }
    [Key, ForeignKey("MyParent"), Column(Order = 1)]
    public virtual int MyParentId { get; set; }

    public virtual string Name { get; set; }
    public virtual Parent MyParent { get; set; }
}

在这种情况下,您的代码确实会从数据库中删除子代。这里解释(最后一节在底部):http://msdn.microsoft.com/en-us/library/ee373856.aspx

答案 1 :(得分:0)

    var parent = context.Parents.Include(p=>p.Children).Where(p=>p.ParentId == 1);

    foreach(Children child in parent.Children.ToList())
    {
      context.Entry(child).State = EntityState.Deleted;
    }
    context.Entry(parent).State = EntityState.Deleted;
    context.SaveChanges();

有关详细信息,请转到此处:https://stackoverflow.com/a/9571108/1241400

编辑:为什么不那么通用

 public void DeleteMany<E>(IQueryable<E> entitiesToDelete) where E : class
        {
            foreach (var entity in entitiesToDelete)
            {
                DataContext.Entry(entity).State = System.Data.EntityState.Deleted;
            }
        } 

你只需要称呼它

var childrenToDelete = someRepository.FindChildrenByParentId<Children>(ParentId);
someRepository.DeleteMany<Children>(childrenToDelete);