0

MVC4 と EF コードを最初に使用して Web サイトを開発しています。

1対多の関係を持つエンティティの子を削除する際に問題があります。

明確にするために編集:私の編集ビューでは、親の子コレクションに既存の子を追加/削除/更新します。追加/削除は JavaScript を使用して行われます。コントローラー メソッドのポスト リクエストで更新された親を受け取ったら、データベース内の親エンティティと子エンティティを同期/更新したいと考えています。ビューで更新されると、親オブジェクトは切り離された状態になります。そのため、親を再度アタッチするときに、デタッチされた状態で行われたすべての更新を行うようにします。

エンティティ関係は、親コレクションから子エンティティを削除すると、子エンティティも子テーブルから削除されるように設定され (カスケード削除のようなものですか?)、これはアタッチされた状態で機能します。

ただし、親をアタッチして変更を保存すると、追加/更新された子のみがデータベースに追加/変更されます。しかし、親コレクションから削除された子は、データベースで削除されません(私はそれらを削除したいのです)。

これはどうすれば解決できますか??

エンティティは次のとおりです。

class Parent
{
    public virtual ICollection<Child> Children { get; set; }
}
class Child
{
    public string Text { get; set; }
}

これは機能し、データベースから子を削除します:

void RemoveChildFromCollection()
{
    // get the first parent and remove the first child in collection
    var context = new DatabaseContext();
    var parent = context.Parents.First();
    parent.Children.Remove(parent.Children.First());
    context.SaveChanges();
}

ControllerMethod:これは上記のようには機能しません。削除された子は子テーブルから削除されません。

public ActionResult Edit(Parent parent)
{
    var context = new DatabaseContext();
    context.Entry(parent).State = System.Data.EntityState.Modified;
    context.SaveChanges();
    return View();
}

モデルビルダーは、親コレクションから子エンティティを削除するときに、子テーブルから子エンティティを削除するように設定されています

// Use Identifying relation. Define complex key for ChildObject containing both Id and 

ParentObjectId
modelBuilder.Entity<Child>()
    .HasKey(c => new {c.ChildID, c.ParentID});

// Because defining such key will remove default convention for auto incremented Id you must redefine it manually

modelBuilder.Entity<Child>()
    .Property(c => c.ChildID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

// Set cascade delete

modelBuilder.Entity<Parent>()
    .HasMany(p => p.Children)
    .WithRequired()
    .HasForeignKey(c => c.ParentID)
    .WillCascadeOnDelete();
4

2 に答える 2

2

カスケード削除は、関係を切断するときではなく、親エンティティも削除されるときに子エンティティのみを削除します。

コンテキストでオーバーライドSaveChanges()して、孤立したChildエンティティを次のようにクリーンアップできます。

public override int SaveChanges()
{
    Children
        .Local
        .Where(c => c.Parent == null)
        .ToList()
        .ForEach(child => Children.Remove(child));

    return base.SaveChanges();
}

このブログ投稿には、孤立したエンティティの処理に関する詳細が記載されています。

于 2012-11-08T16:35:09.530 に答える
1

ビューで変更を行うと、エンティティ (親と子) は切り離された状態になります。そのため、EF はこれらの変更を追跡できませんでした。オブジェクト グラフをコンテキストにアタッチすると (親の状態を に設定することにより)、ModifiedEF はこのアタッチされたオブジェクト グラフを現在の状態として取得し、ビューのデタッチ フェーズ中に発生した子の削除について何も認識しません。

この問題を解決するには、現在のオブジェクト グラフ (子を含む親) をデータベースから読み込み、それをビューのオブジェクト グラフと比較して、読み込まれたグラフに変更をマージする必要があります。次に、変更を保存します。いくつかの変更が考えられます。

  • 親のスカラー プロパティが変更されている可能性があります
  • 子のスカラー プロパティを変更できます
  • 新しい子がChildrenコレクションに追加された可能性があります
  • Children子がコレクションから削除された可能性があります

親の状態をに設定する現在のコードModifiedは、最初のケースのみを正しく処理し、他の 3 つのケースは処理しません。

4 つのケースすべてに対処するには、上記の手順に従う必要があります。その方法の例をここに示します(その回答の編集セクションを参照してください)。

Edit post アクションのコードは次のようになります。

public ActionResult Edit(Parent parent)
{
    using (var context = new DatabaseContext())
    {
        var parentInDb = context.Parents
            .Include(p => p.Children)
            .Single(p => p.ParentId == parent.ParentId);

        context.Entry(parentInDb).CurrentValues.SetValues(parent);

        foreach (var childInDb in parentInDb.Children.ToList())
            if (!parent.Children.Any(c => 
                    c.ChildId == childInDb.ChildId &&
                    c.ParentId == childInDb.ParentId)) // or == parent.ParentId
                context.Children.Remove(childInDb);
                // here
                // parentInDb.Children.Remove(childInDb);
                // should work too because you have an identifying relationship

        foreach (var child in parent.Children)
        {
            var childInDb = parentInDb.Children.SingleOrDefault(c =>
                c.ChildId == child.ChildId &&
                c.ParentId == child.ParentId); // or == parent.ParentId
            if (childInDb != null)
                context.Entry(childInDb).CurrentValues.SetValues(child);
            else
                parentInDb.Children.Add(child);
        }
        context.SaveChanges();

        return View();
    }
}
于 2012-11-08T16:36:10.073 に答える