web-dev-qa-db-ja.com

Entity Framework CFで親の前に子エンティティを削除するにはどうすればよいですか?

EFコードファーストを使用してdbレコード(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でこの削除をどのように実行しますか?

23
quakkels

EF6

context.Children.RemoveRange(parent.Children)
11
Eng. Samer T

EFのカスケード削除は、データベース内で設定されたカスケード削除に依存しているため、データベースでカスケード削除を設定していない場合は、最初にすべてのアイテム価格をアプリケーションにロードし、削除済みとしてマークする必要があります。

8
Ladislav Mrnka

最も簡単な解決策は、最初に価格を反復して変更を保存し、次に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

次のようなことをしてください(テストされていませんが、うまくいけばあなたはjistを取得します):

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

エントリ状態を設定する代わりに。しかし、2つのシーンの背後に違いがあるかどうかはわかりません。

5
SventoryMang

エンティティフレームワークのカスケード削除は、エンティティオブジェクトグラフの削除について確認する必要があるため、注意が必要です。これらのカスケード削除の統合テストを常に作成することをお勧めします。

EFで親エンティティを削除しようとすると、現在のdbcontextのすべての子エンティティに対してdeleteステートメントを実行しようとします。その結果、ロードされていない子エンティティは初期化されません。これにより、外部キー制約に違反するRDBMSランタイムエラーが発生します。安全のために、削除する前にすべての依存エンティティが現在のdbcontextにロードされていることを確認してください。

3
marvelTracker

オブジェクトが自己参照している場合は、以下の方法を使用して、多対多および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);
    }
}
0
Matthew Hudson

私には同様の問題があり、私にとっては、それぞれのクラスで親と子の関係を正しく確立していないようでした。

私の修正は、親の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; }
    }
0
Artie Leech