1

n 層の切断されたエンティティ クラスの個々の変更されたプロパティを追跡するために、カスタマイズされた方法を使用しています。から抽出しました

プログラミング エンティティ フレームワーク: Julia Lerman と Rowan Miller (O'Reilly) による DbContext。Copyright 2012 Julia Lerman and Rowan Miller, 978-1-449-31296-1.

コードは次のとおりです。

public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
        // bind the entity back into the context
        dbContext.Set<TEntity>().Add(root);
        // throw exception if entity does not implement IObjectWithState
        CheckForEntitiesWithoutStateInterface(dbContext);

        foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
            IObjectWithState stateInfo = entry.Entity;
            if (stateInfo.State == RecordState.Modified) {
                // revert the Modified state of the entity
                entry.State = EntityState.Unchanged;
                foreach (var property in stateInfo.ModifiedProperties) {
                    // mark only the desired fields as modified
                    entry.Property(property).IsModified = true;
                }
            } else {
                entry.State = ConvertState(stateInfo.State);
            }
        }
        dbContext.SaveChanges();
    }

このメソッドの目的は、SaveChanges() の次の呼び出しで定義済みのエンティティ フィールドのセットのみを更新する準備ができていることを EF に知らせることです。これは、エンティティが ASP.NET MVC 3 で次のように機能することを回避するために必要です。

  • 最初のページ読み込み時: コントローラーの Get アクションがエンティティ オブジェクトを読み込み、それをパラメーターとしてビューに渡します。

  • ビューは、エンティティの 2 つのフィールドを編集するためのコントロールを生成し、隠しフィールドにレコードの ID を保持します。

  • [保存] を押してエンティティをコントローラーに戻すと、ビューに保存されている 3 つのフィールドを除くすべてのフィールドに null 値が含まれます。これは、MVC バインディング マネージャーの既定の動作です。

変更をデータベースに保存すると、更新クエリはもちろん、マップされていないフィールドを次のような文で上書きします。

UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3

これが、フィールドを個別に追跡し、関心のあるフィールドのみを更新しようとしている理由です。ApplyChanges() でカスタム メソッドを呼び出す前に、更新に含めたいフィールドのリストを追加しています。 IObjectWithState.ModifiedProperties リスト。次のように SQL ステートメントを取得します。

UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3

問題は、フィールドの 1 つを ApplyChanges で変更済みとしてマークする場合です。つまり、次のようになります。

entry.Property(property).IsModified = true;

システムは次の例外をスローしています:

{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.

at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)

at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)

at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)

...

そこで質問です。この EF 検証をバイパスする方法、または変更しようとしているこのシステム プロパティ (IsModified) の存在をコンテキストに知らせる方法はありますか?

アーキテクチャの概要:

  • 最初に EF コード (注釈 + Fluent API)
  • Oracle .NET EF データ プロバイダー (ODAC)
  • コンテキストは、nInject.MVC を使用してカスタム ビジネス コンテキストに注入されます => これが、ApplyChanges() メソッドをカスタマイズした理由です。

    using (var context = new BreakAwayContext()){ context.Set().Add(root);

    すでに初期化されている dbcontext への単純な呼び出し

    dbContext.Set().Add(ルート);

  • Oracle データベースは、EF の助けを借りずに手動で作成されるため、EF メタデータ テーブルは使用されません。

ありがとう、イワン。

4

1 に答える 1

0

非常に良い説明ですが、オブジェクトに「IsModified」と呼ばれる一時的なプロパティが必要な理由や、変更についてEFに通知する必要がある理由に関する情報が見つかりません(EFはとにかくそれを永続化できません) )。

とにかくプロパティがビューに含まれている場合は、IsModifiedプロパティの値をモデルバインダーで設定する必要があります。

ApplyChangesメソッドにコードを追加して、「IsModified」という名前のプロパティをスキップするか、entry.CurrentValues.PropertyNamesを使用して既知のプロパティのみをフィルタリングすることもできます。例:

foreach (var property in stateInfo.ModifiedProperties) { 
    // mark only the desired fields as modified 
    if (entry.CurrentValues.PropertyNames.Contains(property)) {
        entry.Property(property).IsModified = true; 
    }
} 

更新:Ivan、非常に申し訳ありませんが、数か月前に投稿したときに問題をよく理解できず、これらの明確なコメントを追加した後、フォローアップしませんでした。今はよく理解できたと思います。そうは言っても、私が提供したコードスニペットはソリューションの一部になると思います。もう一度発生する例外を見ると、EFが検出している問題は、NotifyCEDDealineが永続的なプロパティではない(つまり、Code Firstモデルでデータベースの列にマップされていない)ことであることがわかりました。IsModifiedはマップされたプロパティに対してのみ使用できるため、2つのオプションがあります。エンティティ内のIObjectWithStateの実装のコードを変更して、マップされていないプロパティがModifiedPropertiesに記録されないようにするか、コードスニペットを使用してIsModifiedを呼び出さないようにします。それらの。

ちなみに、これをすべて行う代わりに、Controller.TryUpdateModel APIを使用して、エンティティに変更されたプロパティのみを設定することもできます。

これがお役に立てば幸いです(私はそれが非常に遅いことを理解していますが)。

于 2012-04-27T19:52:03.597 に答える