1. ホーム
  2. asp.net-mvc

[解決済み] EF4 POCOオブジェクトの変更保存時のリレーションシップの更新

2022-11-27 05:41:11

質問

Entity Framework 4、POCOオブジェクトとASP.Net MVC2。私は多対多の関係を持っています、例えば、BlogPostとTagエンティティの間に。これは、私のT4生成POCO BlogPostクラスで私が持っていることを意味します。

public virtual ICollection<Tag> Tags {
    // getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;

ObjectContextのインスタンスからBlogPostと関連するタグを要求し、それを別のレイヤー(MVCアプリケーションのView)に送ります。後で、変更されたプロパティと変更された関係で更新されたBlogPostが戻ってきます。例えば、タグが "A" "B" と "C" で、新しいタグは "C" と "D" になっています。この例では、新しいタグはなく、タグのプロパティも変更されないので、保存されるべきは変更されたリレーションシップのみとなります。そこで、別のObjectContextにこれを保存する必要があります。(更新: 今、同じコンテキストインスタンスで試してみましたが、これも失敗しました)。

問題:リレーションシップを正しく保存することができません。見つけたものはすべて試しました。

  • Controller.UpdateModel と Controller.TryUpdateModel が動作しません。
  • コンテキストから古いBlogPostを取得し、コレクションを修正してもうまくいきません。(次点と異なるメソッドで)
  • これは はおそらく動作するでしょうが、これが単なる回避策であって、解決策ではないことを願っています :(.
  • BlogPost および/または Tags の Attach/Add/ChangeObjectState 関数を、可能なすべての組み合わせで試しました。失敗しました。
  • これは は私が必要とするもののように見えますが、それは動作しません(私はそれを修正しようとしましたが、私の問題のためにすることはできません)。
  • コンテキストの関係オブジェクトをChangeState/Add/Attach/...しようとした。失敗しました。

"Doesn't work" は、ほとんどの場合、与えられた "solution" がエラーを発生させず、少なくとも BlogPost のプロパティが保存されるまで作業したことを意味します。リレーションシップで起こることは様々です。通常、タグは新しいPKでタグ・テーブルに再度追加され、保存されたBlogPostは元のタグではなく、それらを参照するようになります。もちろん、返されたタグはPKを持ち、保存/更新メソッドの前に私はPKをチェックし、それらはデータベース内のものと等しいので、おそらくEFはそれらが新しいオブジェクトであり、それらのPKは一時的なものであると考えています。

私が知っている問題で、自動化された簡単な解決策を見つけることを不可能にするかもしれません。POCO オブジェクトのコレクションが変更されたとき、FixupCollection トリックが多対多の関係のもう一方の端の逆参照を更新するので、それは上記の仮想コレクション プロパティによって起こるはずです。しかし、Viewが更新されたBlogPostオブジェクトをquot;return"すると、そのようなことは起こりません。これは、多分私の問題に対する簡単な解決策がないことを意味しますが、それは私を非常に悲しませ、EF4-POCO-MVCの勝利を嫌います :(. また、それはEFがEF4オブジェクトタイプが使用されているMVC環境でこれを行うことができないことを意味します :(. スナップショットベースの変更追跡は、変更されたBlogPostが既存のPKを持つタグへの関係を持っていることを発見する必要があると思います。

Btw: 1対多のリレーションでも同じ問題が起こると思います(googleと私の同僚がそう言っています)。私は自宅でそれを試してみますが、たとえそれがうまくいっても、私のアプリの 6 つの多対多の関係では役に立ちません :(笑)。

どのように解決するのですか?

この方法で試してみましょう。

  • BlogPostをコンテキストにアタッチする。オブジェクトをコンテキストにアタッチした後、オブジェクト、すべての関連オブジェクト、すべてのリレーションの状態がUnchangedに設定されます。
  • context.ObjectStateManager.ChangeObjectStateを使用して、BlogPostをModifiedに設定する。
  • タグコレクションを繰り返し処理する
  • context.ObjectStateManager.ChangeRelationshipStateを使用して、現在のタグとBlogPostの間の関係の状態を設定する。
  • 変更を保存する

編集します。

私のコメントの一つは、EFがあなたのためにマージしてくれるという誤った希望をあなたに与えたと思います。私はこの問題でたくさん遊んで、私の結論は、EF があなたのためにこれを行うことはないだろうということです。私はあなたがまた私の質問を見つけたと思います MSDN . 現実には、このような質問はインターネット上にたくさんあります。問題は、このシナリオにどのように対処するかが明示されていないことです。そこで、この問題について見てみましょう。

問題の背景

EF は、どのレコードが更新、挿入、または削除されなければならないかを永続性が知っているように、エンティティ上の変更を追跡する必要があります。問題は、変更を追跡するのは ObjectContext の責任であるということです。ObjectContext は、アタッチされたエンティティに対してのみ変更を追跡することができます。ObjectContext の外部で作成されたエンティティは、まったく追跡されません。

問題の説明

上記の説明に基づいて、EF はエンティティが常にコンテキストにアタッチされる接続シナリオに適していることを明確に述べることができます - WinForm アプリケーションの典型です。Web アプリケーションでは、コンテキストが要求処理後に閉じられ、エンティティのコンテンツがクライアントへの HTTP 応答として渡される非接続型シナリオが必要です。次のHTTPリクエストでは、エンティティの修正されたコンテンツが提供され、それを再作成し、新しいコンテキストにアタッチして永続化しなければなりません。再作成は通常、コンテキスト スコープの外側で行われます (永続性を無視した階層型アーキテクチャ)。

解決方法

では、このような切断されたシナリオにどのように対処すればよいのでしょうか。POCOクラスを使用する場合、3つの方法で変更履歴を管理することができます。

  • スナップショット - 同じコンテキストが必要 = 接続が切断されたシナリオには無意味
  • 動的追跡プロキシ - 同じコンテキストが必要 = 接続が切断されたシナリオでは意味がない
  • 手動での同期

単一のエンティティに対する手動同期は簡単な作業です。エンティティをアタッチして、挿入にはAddObject、削除にはDeleteObject、更新にはObjectStateManagerでModifiedに状態を設定する必要があるだけです。本当の痛みは、単一のエンティティではなく、オブジェクト・グラフを扱わなければならないときにやってきます。この問題は、独立したアソシエーション(Foreign Keyプロパティを使用しないもの)や多対多のリレーションを扱わなければならない場合に、さらに悪化します。その場合、オブジェクトグラフの各エンティティだけでなく、オブジェクトグラフの各関係も手動で同期させなければなりません。

手動による同期は、MSDN のドキュメントで解決策として提案されています。 オブジェクトのアタッチとデタッチ にはこう書かれています。

オブジェクトはオブジェクト コンテキストに添付されます。もし オブジェクトの状態を変更する必要がある場合 またはリレーションシップの状態を変更する必要がある場合、その理由は オブジェクトが デタッチドステートで修正されたことがわかっているため、オブジェクトまたはリレーションシップの状態を変更する必要がある場合、次のいずれかの 以下のメソッドのいずれかを使用します。

言及されているメソッドは、ObjectStateManager の ChangeObjectState と ChangeRelationshipState = 手動変更追跡です。同様の提案は、他のMSDNドキュメントの記事にもあります。 リレーションシップの定義と管理 に書いてあります。

切断されたオブジェクトを扱う場合 オブジェクトを扱う場合は、同期を手動で管理する必要があります。 同期を行う必要があります。

さらに ブログ記事 に関連したブログ記事があり、まさにEFのこの動作を批判しています。

解決理由

EFには、以下のような便利な操作や設定があります。 リフレッシュ , ロード , ApplyCurrentValues , 元の値の適用 , マージオプション などがあります。しかし、私の調査では、これらの機能はすべて単一のエンティティに対してのみ機能し、スカラープレパティにのみ影響します(=ナビゲーションプロパティやリレーションではありません)。私はむしろ、エンティティにネストされた複雑なタイプでこのメソッドをテストしていません。

その他の提案された解決策

本当の Merge 機能の代わりに、EF チームは次のようなものを提供します。 自己追跡型エンティティ (STE) と呼ばれるものを提供していますが、これは問題を解決していません。まず第一に、STE は同じインスタンスが全体の処理に使用される場合にのみ機能します。Webアプリケーションでは、ビューステートやセッションにインスタンスを格納しない限り、このケースには該当しません。そのため、私はEFを使うのが非常に嫌で、NHibernateの機能をチェックすることにしました。最初の観察では、NHibernateはおそらく次のような機能を持っていると思います。 機能性 .

結論

この仮定を、別の仮定への単一リンクで終わらせます。 関連する質問 MSDNフォーラムで。Zeeshan Hiraniの答えをチェックしてください。彼は Entity Framework 4.0レシピ . オブジェクトグラフの自動マージがサポートされていないと言うのなら、私は彼を信じます。

しかし、まだ私が完全に間違っていて、EFにいくつかの自動マージ機能が存在する可能性があります。

2を編集します。

ご覧のように、これはすでに MS 接続 に提案として追加されています。MS は次のバージョンで行うべきこととしてこれを閉じましたが、実際には STE 以外にこのギャップを改善することは何も行われていませんでした。