web-dev-qa-db-ja.com

EFコードファーストデータベースの子1から多くの関連レコードを削除するにはどうすればよいですか?

さて、私は1対多の関連モデルを持っています:

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

ParentクラスにChildプロパティが追加されています

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

つまり、カスケード削除を正しく構成するにはどうすればよいですか?またはそれらの子エンティティを削除するにはどうすればよいですか?これはカジュアルなタスクだと思いますが、何かが不足しています

60
Dmytro

カスケード削除は、parentを削除するのではなく、InsertOrUpdateを呼び出すだけなので、ここでは効果がありません。正しい手順は、たとえば次のように、子を1つずつ削除することです。

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();
}
59
Slauma

EF6で操作を行うより速い方法は...

 context.Children.RemoveRange(parent.Children)
97
Sam Sippe

これは「孤児の削除」と呼ばれます。

親が削除されていない孤立したデータをEFは自動的に削除できますか?

EF6でどのように機能するのかわかりませんが、EF Coreではうまく機能します https://docs.Microsoft.com/en-us/ef/core/saving/cascade-delete カスケードが機能するために必ずしも親を削除する必要はありません。

孤児の例の削除

3
Konrad

に変更してみてください

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

遅延読み込みには仮想が必要だからです。説明通り こちら

子がロードされていないため、parent.Children.clearは機能していません

2
Kirsten Greed

オブジェクトが自己参照している場合は、以下の方法を使用して、多対多および1対多の両方の子を削除できます。後で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);
    }
}
1
Matthew Hudson