如何在父实体框架CF之前删除子实体?

时间:2012-03-08 15:42:47

标签: database entity-framework entity-framework-4.1

我正在尝试使用EF代码优先删除数据库记录(deleteMe)及其子项(deleteMe.Prices)。

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      db.Entry(deleteMe).State = EntityState.Deleted;

      foreach (var item in deleteMe.Prices)
      {
         db.Entry(item).State = EntityState.Deleted; // cascade delete
      }
   }
}
db.SaveChanges();

但是,Entity Framework似乎无法跟踪在父级之前应删除子级记录的事实。我收到错误:

  

DELETE语句与REFERENCE约束“ItemPrice_Item”冲突   冲突发生在数据库“DEVDB”,表“dbo.ItemPrices”中,   列'Item_ItemId'。
  声明已经终止。

我如何在EF中执行此删除?

9 个答案:

答案 0 :(得分:34)

我最终找到了一条为我做的快速行:

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      ///
      deleteMe.Prices.ToList().ForEach(p => db.ItemPrices.Remove(p));
      ///

      db.Entry(deleteMe).State = EntityState.Deleted;
   }
}
db.SaveChanges();

答案 1 :(得分:10)

EF6

context.Children.RemoveRange(parent.Children)

答案 2 :(得分:8)

EF中的级联删除取决于数据库中关联配置的级联删除,因此如果您没有在数据库中配置级联删除,则必须先将所有项目价格加载到应用程序并将其标记为已删除。

答案 3 :(得分:5)

最简单的解决方案是首先迭代价格并调用保存更改,然后将条目设置为deleteMe删除并再次调用保存更改,但是你检查过这个:Entity framework code first delete with cascade?这似乎是你想要的。

很奇怪,为什么你不是要删除上下文中的实体来删除而是设置条目状态?

另一种选择是设置级联删除http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx

做这样的事情(没有经过测试,但希望你能得到这个):

using (TransactionScope scope = new TransactionScope())
{    
    foreach (var deleteMe in deleteThese)
    { 
   // Delete validation
      if(CanDeleteItem(deleteMe.ItemId))
      {

         foreach (var item in deleteMe.Prices)
         {
            db.Entry(item).State = EntityState.Deleted; // cascade delete
         }
         db.SaveChanges();

         db.Entry(deleteMe).State = EntityState.Deleted;


     }
   }
   db.SaveChanges();
   scope.Complete();
}     

另外你可以打电话:

db.Prices.Remove(item);

db.DeleteMes.Remove(deleteMe);

而不是设置条目状态。不确定两者之间的幕后是否存在差异。

答案 4 :(得分:3)

实体框架中的级联删除是件棘手的事情,因为你需要确定删除实体对象图。最好总是为这些级联删除编写集成测试。

如果您尝试删除EF中的父实体,它将尝试为当前dbcontext中的任何子实体执行delete语句。因此,它不会初始化任何尚未加载的子实体。这将导致RDBMS运行时错误,这违反了外键约束。为了安全起见,请确保在删除之前将所有依赖实体加载到当前dbcontext。

答案 5 :(得分:0)

如果您的对象是自引用的,则可以使用以下方法删除多对多和一对多的子对象。记得以后调用db.SaveChanges():)

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    Object obj = this.db.Objects.Find(id);
    this.DeleteObjectAndChildren(obj);
    this.db.Objects.Remove(obj);
    this.db.SaveChanges();
    return this.Json(new { success = true });
}

/// <summary>
/// This deletes an object and all children, but does not commit changes to the db.
///  - MH @ 2016/08/15 14:42
/// </summary>
/// <param name="parent">
/// The object.
/// </param>
private void DeleteObjectAndChildren(Object parent)
{
    // Deletes One-to-Many Children
    if (parent.Things != null && parent.Things.Count > 0)
    {
        this.db.Things.RemoveRange(parent.Things);
    }

    // Deletes Self Referenced Children
    if (parent.Children != null && parent.Children.Count > 0)
    {
        foreach (var child in parent.Children)
        {
            this.DeleteObjectAndChildren(child);
        }

        this.db.Objects.RemoveRange(parent.Children);
    }
}

答案 6 :(得分:0)

我有一个类似的问题,对我来说,我好像没有正确建立父母和孩子在各自班级之间的关系。

我的解决方法是将以下指定的属性添加到Child类中,以表示代表其父级ID的属性

    public class Child
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        [Key, ForeignKey("Parent"), Column(Order = 2)]  // adding this line fixed things for me
        public string ParentId {get; set;}
    }

    public class Parent
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        ...

        public virtual ICollection<Child> Children{ get; set; }
    }

答案 7 :(得分:0)

以下内容非常有效。 为数据库中的每个关系表添加以下内容(在上下文文件中)。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder
            .Entity<TableA>()
            .HasMany(x => x.TableB)
            .WithRequired(x => x.TableA)
            .WillCascadeOnDelete();

        modelBuilder
            .Entity<TableC>()
            .HasMany(x => x.TableD)
            .WithRequired(x => x.TableC)
            .WillCascadeOnDelete();

        modelBuilder
            .Entity<TableE>()
            .HasMany(x => x.TableF)
            .WithRequired(x => x.TableE)
            .WillCascadeOnDelete(); }

然后在您的代码中,不要忘记删除之前加载这些表

            context.TableA.Load();
            context.TableB.Load();
            context.TableC.Load();
            context.TableD.Load();
            context.TableE.Load();
            context.TableF.Load();

            var tableAEntity= TableA.Where(x => x.Condition == [yourcondition].FirstOrDefault(); 

            context.TableA.Remove(tableAEntity);
            context.SaveChanges();

这将非常快速有效地从主条目表和所有连接的表记录(通过FK相关联)中删除实体(记录)(即使关系在多个层次上深层级联)。

答案 8 :(得分:0)

_context.Remove(parent);
_context.RemoveRange(_context.Childrens
  .Where(p => parent.Childrens
  .Select(c => c.Id).Contains(p.Id)));