【问题标题】:How to remove child one to many related records in EF code first database?如何在 EF 代码优先数据库中删除子一对多相关记录?
【发布时间】:2013-05-15 07:47:11
【问题描述】:

嗯,我有一对多的相关模型:

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

public class Child
{
    public int Id { get; set; }
    public string ChildName { get; set; }
}

我想要做的是清除 Parent.Children 并从数据库中删除相关的子实体。我已经试过了:

数据库上下文类:

modelBuilder.Entity<Parent>()
            .HasMany(p => p.Children)
            .WithOptional()
            .WillCascadeOnDelete(true);

这很好用,但是当我这样做时,我的数据库中仍然有带有Parent_Id = null 字段的冗余记录

parent.Children.Clear();
repository.InsertOrUpdate(parent);

在我的存储库类中。同样的行为是当我这样做时:

modelBuilder.Entity<Parent>()
            .HasMany(pr => pr.Children)
            .WithOptional(ri => ri.Parent)
            .WillCascadeOnDelete(true);

Child 类中有额外的Parent 属性

public class Child
{
    ...
    public Parent Parent { get; set; }
    ...
}

或者当我这样做时

modelBuilder.Entity<Child>()
            .HasOptional(p => p.Parent)
            .WithMany(p => p.Children)
            .HasForeignKey(p => p.Parent_Id)
            .WillCascadeOnDelete(true);

Child 类中具有额外的 Parent_Id 属性

public class Child
{
     ...
     public int Parent_Id { get; set; }
     ...
}

那么,如何正确配置级联删除呢?或者我应该如何删除那些子实体?我认为这是一项临时任务,但我只是错过了一些东西。

【问题讨论】:

  • 你能再贴一些代码吗?也许您的OnModelCreating() 的全部内容?当我复制并粘贴您的实体和您的第一次映射尝试(并将 Id 设置为两个实体的关键属性)时,删除对我来说是正确的级联。

标签: c# entity-framework code-first


【解决方案1】:

试试改成

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

因为需要 virtual 才能获得延迟加载。 如解释 here

我认为您的 parent.Children.clear 不起作用,因为尚未加载孩子

【讨论】:

  • 就我而言(ASP.NET core 2 / EF Core),我已经测试过了,这不是解决方案
【解决方案2】:

级联删除在这里不起作用,因为您没有删除parent,而只是调用InsertOrUpdate。正确的做法是一个一个地删除孩子,例如:

using (var context = new MyContext())
{
    var parent = context.Parents.Include(p => p.Children)
        .SingleOrDefault(p => p.Id == parentId);

    foreach (var child in parent.Children.ToList())
        context.Children.Remove(child);

    context.SaveChanges();
}

【讨论】:

  • 确定级联删除不需要单独删除子节点?
  • @kirsteng:级联删除意味着子级被删除如果您删除父级。但是在这个问题中,没有删除任何父级。所以,级联删除不适用于这种场景。
  • 而不是 foreaching 所有的“子”对象,只需说 context.Children.RemoveRange(parent.Children.ToArray()) 这样 DbContext 就不必在每次调用 Remove 时进行大量工作检查。对于删除而言,这可能不是一个大的性能问题,但是当我在 for 循环中使用 context.Children.Add(child) 测试添加 10 万条记录时,我注意到一次添加一个项目时存在巨大差异。
  • 级联删除在这种情况下有效,至少在使用 EF Core 时:)
  • @Konrad 感谢您的提示。在我的情况下,我必须将 DB-schema 更改为具有 ON DELETE CASCADE 并再次生成 DB-models 以在其中包含 DeleteBehavior.Cascade
【解决方案3】:

在 EF6 中,一种更快的操作方式是...

 context.Children.RemoveRange(parent.Children)

【讨论】:

  • 这是一种更快的编码方式,但就性能而言,它真的更快吗?如果我想用parent.Id == 10 删除父级下的所有子级,而我的Child 类有100 个字段并且尚未加载到内存中,那么使用RemoveRange 似乎效率低下,因为程序将不得不加载parent.Children默认包含所有 100 个字段。
  • @VCD 它比为每个孩子调用 context.Children.Remove(child) 快,但不如运行 sql "DELETE FROM Children where parentId=@parentId"
  • 如果您拥有的唯一信息是孩子 ID,您将如何寻找父母?
  • @guyfromfargo 在 EF 中类似于 var child = context.Children.Single(f=&gt;f.Id==childId); var parent = context.Parents.Single(f=&gt;f.Id==child.ParentId);
【解决方案4】:

如果您的对象是自引用的,您可以使用以下方法删除多对多和一对多的子对象。请记住之后调用 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);
    }
}

【讨论】:

    【解决方案5】:

    这称为“删除孤儿”。

    Can EF automatically delete data that is orphaned, where the parent is not deleted?

    我不知道它在 EF6 中是如何工作的,但在 EF Core 中它可以正常工作https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete,因此您不一定需要删除父级才能使级联工作。

    Delete orphans examples

    【讨论】:

    • 这应该是现在公认的答案,因为第一个链接准确地描述了如何解决问题。 OP 需要将父 ID 作为 FK 添加到子 ID,然后使用该链接中的方法设置识别关系。
    猜你喜欢
    • 2012-01-09
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    相关资源
    最近更新 更多