web-dev-qa-db-ja.com

EF4.1でコンテキストからエンティティを正しくアタッチおよびデタッチします

エンティティのキ​​ャッシュメカニズムを実装しようとしています。そして、キャッシュでエンティティを正しくシームレスに使用するには、キャッシュに入れる前に現在のコンテキストからエンティティを切り離し、キャッシュから取得したときに新しいコンテキストに戻す必要があります。 (私のコンテキストの有効期間はhttpリクエストごとです)

要件は次のとおりです-

  1. エンティティがデタッチされたときに、それに関連付けられているすべてのナビゲーションプロパティ(既に入力済み)を削除しないでください。
  2. 必要に応じて、キャッシュされたアイテムを更新できます(したがって、新しいコンテキストに正しくアタッチすることが重要です)。

これは、EntityCacheクラスを作成するための私の試みです-(ここでのServerCacheは、オブジェクトをASP.NETキャッシュにプッシュするラッパークラスです)

public static class EntityCache
    {
        private static DbContext context
        {
            get
            {
                return (DbContext)HttpContext.Current.Items[ObjectKeys.ContextRequestItemKey];
            }
        }

        private static void Detach(object entity)
        {
            var trackedEntity = entity as IEntityWithChangeTracker;
            trackedEntity.SetChangeTracker(null);
            ((IObjectContextAdapter)context).ObjectContext.Detach(entity);
        }

        private static void Attach(object entity)
        {
            ((IObjectContextAdapter)context).ObjectContext.Attach((IEntityWithKey)entity);
        }

        public static void Remove(string key)
        {
            ServerCache.Remove(key);
        }

        public static object Get(string key)
        {
            object output = ServerCache.Get(key);
            if (output != null)
                Attach(output);
            return output;
        }

        public static void ShortCache(String key, object data)
        {
            if (data != null)
            {
                Detach(data);
                ServerCache.ShortCache(key, data);
            }
        }

        public static void LongCache(String key, object data)
        {
            if (data != null)
            {
                Detach(data);
                ServerCache.LongCache(key, data);
            }
        }
    }

キャッシュにエンティティを配置すると、エンティティはDynamicProxyタイプであり、実際のクラスではありません。

接続はまったく機能しません-Dynamic_ {blahblah}型のオブジェクトをIEntityWithKeyにケースできないという例外が発生します。

これらの接続および切断の例をオンラインで見て、試してみましたが、ここでAttach/Detachメソッドの新しい実装を受け入れています。

ありがとうございました。

フォローアップの質問-

context.Entry(entity).State = EntityState.Detached;

動作しますが、ロードされたすべてのナビゲーションプロパティをNULLにします。どのようにナビゲーションプロパティを保持し、コンテキストからデタッチしたときにそれらをNULLで置き換えない(または失わない)ようにします。

31
MoXplod

IEntityWithKeyは、他の種類のエンティティのインターフェイスです。 「大きな」エンティティ用です。たとえば、EntityObjectはこのインターフェイスを実装します。これらのエンティティはPOCOとは見なされず、DbContext AP​​Iによってサポートされません。

IEntityWithKeyを使用する場合は、クラスで実装する必要があります-自動的に行われるものではありません。

DbContext AP​​Iを使用した正しいアタッチは次のとおりです。

dbContext.Set(typeof(entity)).Attach(entity); 

これはうまくいけばうまくいくはずです:

dbContext.Entry(entity).State = EntityState.Unchanged;

DbContext AP​​Iを使用した正しいデタッチは次のとおりです。

dbContext.Entry(entity).State = EntityState.Detached;

また、objectの代わりにジェネリックメソッドを使用することをお勧めします。

30
Ladislav Mrnka

フォローアップの質問:

...どのようにしてナビゲーションプロパティを保持し、コンテキストからデタッチするときにNULLで置き換えない(または失わない)ようにするか...

ナビゲーションプロパティを維持しながら、オブジェクトグラフをコンテキストから切り離すことは不可能だと思います。 MSDNから

独立したアソシエーションでは、切り離されたオブジェクトの関係情報は維持されません。

このステートメントは独立した関連付けに関するものですが、ナビゲーションプロパティが外部キーアソシエーション(モデル内で外部キープロパティを公開する関係)で維持されることを意味するものではありません。はい、「関係情報」は維持されます。これは、外部キープロパティ(スカラープロパティ)が生きており、デタッチ後に正しい外部キー値が含まれるためです。ただし、対応するナビゲーションプロパティは、参照プロパティの場合はnullのままになります。または、ナビゲーションコレクションの場合、参照はコレクションから削除されます。

完全なオブジェクトグラフをコンテキストから切り離す唯一の方法は、コンテキストを完全に破棄するか、元のグラフの切り離しを開始する前にグラフのコピーを作成することです。コピーを作成するには、すべてのプロパティをコピーしてグラフ内を移動するCloneメソッドを記述するか、グラフをバイナリストリームにシリアル化してから逆シリアル化する this のような「トリック」を使用します新しいオブジェクトに戻ります。このため、エンティティはシリアル化可能である必要があります。また、参照サイクル(エンティティ間で双方向のナビゲーションプロパティを使用する場合によくある)が問題を引き起こす可能性があります。 (オブジェクトがEFの内部オブジェクトへの参照を含むプロキシであり、おそらくコピー、シリアライズ、デシリアライズしたくない場合も注意してください。)

この側面では、DetachAttachの対応物ではありません。動作がまったく異なるためです。Attachはオブジェクトグラフ全体をアタッチし、ナビゲーションプロパティを維持します。 Detachは、関連オブジェクトのない単一のエンティティのみを切り離し、ナビゲーションプロパティを破棄します。上にリンクされた同じページから:

分離は、メソッドに渡される特定のオブジェクトにのみ影響します。切り離されるオブジェクトのオブジェクトコンテキストに関連オブジェクトがある場合、それらのオブジェクトは切り離されません。

これが、DbContext AP​​Iが明示的なDetachメソッドを持たない主な理由であると想像できます(ObjectContextとは対照的に)-デタッチは高度な機能と見なされます期待どおりに動作しません。

MSDNは、リソースを節約するために、コンテキストからオブジェクトを切り離す唯一の理由として "(上記の記事でも):

Entity Frameworkアプリケーションでは、オブジェクトコンテキストからオブジェクトをデタッチできます。同じオブジェクトコンテキストでクエリを繰り返し実行すると、オブジェクトコンテキストのメモリ要件が増加するため、リソースを節約するためにオブジェクトをデタッチできます。

このメソッドは、オブジェクトがコンテキストから切り離された後、オブジェクトを操作するために準備および設計されたものではないと思います。主な目的は、即時のガベージコレクションのためにそれらをリリースすることです。

10
Slauma