web-dev-qa-db-ja.com

コレクションから子を削除し、SaveChangesの問題を解決することは可能ですか?

外部キー関係で最初にEntityFrameworkコードを使用しています。アプリケーションのエンティティICollectionからオブジェクトを削除する方法を調査しています。

子関係を持つエンティティがある場合、Addメソッドを使用してオブジェクトをICollectionに直接追加できます。これで、removeを使用すると、エラーが発生します

System.InvalidOperationExceptionが発生しましたメッセージ=操作に失敗しました:1つ以上の外部キープロパティがNULL不可であるため、関係を変更できませんでした。リレーションシップに変更が加えられると、関連する外部キープロパティがnull値に設定されます。外部キーがnull値をサポートしていない場合は、新しい関係を定義するか、外部キープロパティに別の非null値を割り当てるか、関連のないオブジェクトを削除する必要があります。

これは、コレクションのRemoveが、外部キーをnullにすることによってのみ関係を削除するためであると理解しています。エンティティにビジネスロジックを記述し、削除できるようにしたかったのです。

したがって、ルートエンティティをそのRepostioryから取得します(例:OrderRepositoryからの注文)。次に、エンティティの特定のメソッドを呼び出します。 Order.AddOrderline(Orderline orderline)これにより、OrderLineがOrdersに追加されます_virtual ICollection<OrderLine> OrderLines_

ただし、ICollectionから削除するだけで節約の変更でエラーが発生するため、Order.CancelOrderline(int orderLineId)のようなコードを記述できません。

オブジェクトコレクションを操作するだけでは、これを実現する方法はないようです。明らかに、コンテキストから直接削除できます。しかし、私はそれをエンティティの一部にしたいのです。 Entity FrameworkのSaveChangesイベントで外部キーのない特定のエンティティをクリーンアップできますか?明らかに、外部キーがnullの場合に削除できるエンティティをEFに通知する必要があります。

現在、リポジトリパターンを使用しているため、コントローラーはコンテキストにアクセスできません。明らかに、OrderLineリポジトリまたはOrderリポジトリのremoveOrderLineメソッドを使用できます。ただし、永続化メカニズムを参照せずにエンティティにコードを記述できるかどうか疑問に思っています。

考え?私たちはこれについてすべて間違っていますか?他のORMでは、子コレクションから削除するだけで済みますか?

22
GraemeMiller

以下があなたのための解決策であるかどうかはわかりませんが、Entity Frameworkは 関係の識別 をサポートしています。このような関係では、親(プリンシパル)に対する子エンティティ(依存)の外部キーは、子エンティティの(複合)プライマリキーの一部である必要があります。たとえば、DbContextデータアノテーションを使用すると、モデルクラスは次のようになります。

public class Order
{
    [Key]
    public int OrderId { get; set; }

    public ICollection<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    [Key, ForeignKey("Order"), Column(Order = 1)]
    public int OrderId { get; set; }

    [Key, Column(Order = 2)]
    public int OrderLineId { get; set; }

    public Order Order { get; set; }
}

必要に応じて、OrderLineIdを自動生成されたIDにすることができます。重要なのは、OrderへのFKがPKの一部であるということだけです。

たとえば、このようなコード...

using (var ctx = new MyContext())
{
    var order = ctx.Orders.Include("OrderLines").Single(o => o.OrderId == 1);
    var orderLineToDelete = order.OrderLines
        .FirstOrDefault(ol => ol.OrderLineId == 5);
    if (orderLineToDelete != null)
        order.OrderLines.Remove(orderLineToDelete);

    ctx.SaveChanges();
}

...確かにdeleteデータベースからorderLineToDeleteを削除します。

詳細については、セクション「関係の特定と非特定に関する考慮事項」の ここ を参照してください。

39
Slauma

お気づきのとおり、コレクションからエンティティを削除すると、エンティティはオブジェクトコンテキストにアタッチされてハングし、SaveChanges()を呼び出すとエラーが発生します。ドメインイベントを使用して、リポジトリを介してオブジェクトコンテキストからエンティティを削除する整然とした方法を有効にしました。

私の答えでこのアプローチを詳しく説明しました この質問へ

2
Steve Wilkes