web-dev-qa-db-ja.com

EF 4:コレクションから子オブジェクトを削除しても削除されません-なぜですか?

Entity Framework 4を使用していて、「Cascade Delete」が設定された親子関係があります。そのため、親から子を削除すると、SaveChanges()を呼び出したときに子が削除されることになります。

        cuRepository.Attach(_controlUnit);
        foreach (var recipe in recipes) {
            _controlUnit.Recipes.Remove(recipe);
            //repository.DeleteObject(recipe);
        }

代わりにエラーが発生します:

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

子を明示的に削除すると(コメント行を参照)、すべて問題ありません。何が欠けていますか?

33
H. Kocher

Removeステートメントではオブジェクトを削除していません。代わりに、(外部キーをnullに設定することにより)レコードを変更して孤立化しようとしています。データベースにはその列に非null制約があり、そうすることはできません。

28
Amy B

http://weblogs.asp.net/zeeshanhirani/archive/2010/07/23/removing-entity-from-a-related-collection.aspx は、あなたに起こったことを正確に説明しています。


次のようなクラスデザインがあるとします。

sample class design

Entity Frameworkは必要な外部キー列を生成し、それらにNOT NULL制約を追加します。これは、すべてのレシピが常に正確に1つのControlUnitに関連付けられるためです。

したがって、実行時には、次のレイアウトに似たオブジェクトが作成されます。

object diagram at runtime

これでコードが機能し、RecipeオブジェクトとそのControlUnitの間の関係が削除されました。

objects with deleted relationships

この時点で保存しようとしましたが、データベースには外部キーNOT NULL列に入れるControlUnit IDがありません。現在のオブジェクトの状態は上記のクラス図に違反しており、すべてのレシピが1つのControlUnitに関連付けられているという前提の下で生成されたデータベースレイアウトに保存することはできません。これが、データベースが変更の保存を拒否し、例外が表示される理由です。

これは、エンティティを削除する行のコメントを外したときに機能する理由も説明します。エンティティはデータベースの関係とともに削除されるため、制約に違反しないため、例外も発生しません。

"しかし、関係にON DELETE CASCADEを設定しました..."

はい。ただし、それはオブジェクトの削除時にのみトリガーされ、関係の削除ではトリガーされません。 ON DELETE CASCADEが設定されていれば、これは機能するはずです。

controlUnitRepository.DeleteObject(_controlUnit);
// deletes the ControlUnit and all associated Recipe entities

ControlUnitとの関係が削除されたときにレシピエンティティの削除をトリガーする場合、関係は単純な関連付けではなく、コンポジションでなければなりません。

updated class diagram with composition

EFはこれをネイティブでサポートしていませんが、識別関係を使用して動作をエミュレートできます。エンティティが親エンティティとの識別関係になり、その関係が削除されると、エンティティも削除されます。これは当初からあなたの意図だったようです。関係の特定の詳細については、 EF4との関係の識別の実装 を参照してください。EF4との関係の識別を実装し、より多くの資料にリンクしています。

26
Chris

ループ内にcontext.DeleteObject(recipe)を追加します

11
vittore

子と親の関係を識別できるものにすると、コレクションから子エンティティを削除できます。子のキーを、親のプライマリIDキーを含む複合キーにする必要があります。このようにして、EFは子を削除する必要があることを認識します。

関係を特定することは、基本的に親が存在しない場合、子には意味がないことを示します。これは、関係が削除されたときに子を削除しても安全であることをEFが認識していることを意味します。

この質問を参照してください リレーションシップを識別し、子エンティティを挿入すると、「テーブルのID列に明示的な値を挿入できません」 とこの質問 子をコレクションから削除して、SaveChangesの問題を解決できますか?

6
GraemeMiller

エンティティを削除するためだけにDALにメソッドを追加しないように、この拡張機能を使用します(コードは http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip- 24-how-to-get-the-objectcontext-from-an-entity.aspx ):

_public static void Delete<T>(this EntityCollection<T> collection, T entityToDelete) where T : EntityObject, IEntityWithRelationships
{
    RelationshipManager relationshipManager = entityToDelete.RelationshipManager;

    IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();
    if (relatedEnd == null)
    {
        throw new Exception("No relationships found for the entity to delete. Entity must have at least one relationship.");
    }

    var query = relatedEnd.CreateSourceQuery() as ObjectQuery;
    if (query == null)
    {
        throw new Exception("The entity to delete is detached. Entity must be attached to an ObjectContext.");
    }

    query.Context.DeleteObject(entityToDelete);
    collection.Remove(entityToDelete);
}
_

そこで、Order.Products.Delete(prod)のようなエンティティを削除します。

拡張機能を使用するための制約は次のとおりです。
-エンティティには関係が必要です。
-エンティティはObjectContextにアタッチする必要があります。

3
net_prog