オブジェクトがEntityFrameworkのデータコンテキストに既にアタッチされているかどうかを確認することはできますか?


86

特定のコンテキストにすでにアタッチされているオブジェクトを次の方法でアタッチしようとすると、次のエラーが発生しますcontext.AttachTo(...)

同じキーを持つオブジェクトは、ObjectStateManagerにすでに存在します。ObjectStateManagerは、同じキーを持つ複数のオブジェクトを追跡することはできません。

次のようなことを達成する方法はありますか?

context.IsAttachedTo(...)

乾杯!

編集:

ジェイソンが概説した拡張方法は近いですが、私の状況では機能しません。

私は別の質問への回答で概説されている方法を使用していくつかの作業をしようとしています:

Linq to Entitiesを使用して、最初に行を取得せずにテーブルから1つ以上の行を削除するにはどうすればよいですか?

私のコードは次のようになります。

var user = new User() { Id = 1 };
context.AttachTo("Users", user);
comment.User = user;
context.SaveChanges();

同じメソッドを使用してダミーUserオブジェクトをアタッチしようとするそのユーザーのために何か他のことをする場合を除いて、これは正常に機能します。以前にそのダミーユーザーオブジェクトを添付したため、これは失敗します。どうすればこれを確認できますか?

回答:


57

これが私が最終的に得たもので、非常にうまく機能します:

public static void AttachToOrGet<T>(this ObjectContext context, string entitySetName, ref T entity)
    where T : IEntityWithKey
{
    ObjectStateEntry entry;
    // Track whether we need to perform an attach
    bool attach = false;
    if (
        context.ObjectStateManager.TryGetObjectStateEntry
            (
                context.CreateEntityKey(entitySetName, entity),
                out entry
            )
        )
    {
        // Re-attach if necessary
        attach = entry.State == EntityState.Detached;
        // Get the discovered entity to the ref
        entity = (T)entry.Entity;
    }
    else
    {
        // Attach for the first time
        attach = true;
    }
    if (attach)
        context.AttachTo(entitySetName, entity);
}

あなたはそれを次のように呼ぶことができます:

User user = new User() { Id = 1 };
II.AttachToOrGet<Users>("Users", ref user);

これcontext.AttachTo(...)は、上記で引用したIDトリックを毎回使用できることを除けば、非常にうまく機能します。最終的には、以前にアタッチされたオブジェクトまたは独自のオブジェクトがアタッチされます。CreateEntityKeyコンテキストを呼び出すことで、それが素晴らしく一般的であり、それ以上のコーディングがなくても複合キーでも機能することが確認されます(EFはすでにそれを実行できるためです!)。


私は同様の問題を抱えていました、そしてこれはちょうど私のものを解決しました-素晴らしい、乾杯!+1
RPM1984 2010年

4
文字列パラメーターが、エンティティが属するコレクションのセレクター関数に置き換えられると、さらに良いでしょう。
ジャスパー

1
なぜ(T)entry.Entity時々nullを返すのか考えはありますか?
tr1stan 2011年

1
entitySetNameを何に設定すべきか理解できません。例外が発生し続けます。私がやりたいのは、アプリケーションを爆破するほど多くの隠されたナンセンスに対処する必要がないユーザーを削除することだけなので、私は本当にイライラしています。
ジュリー

1
Tがすでにある場合IEntityWithKeyentity.EntityKeyそれを再構築するのではなく、単にそのプロパティを使用することはできませんか、または推測/提供する必要がありEntitySetNameますか?
drzaus 2013年

54

より簡単なアプローチは次のとおりです。

 bool isDetached = context.Entry(user).State == EntityState.Detached;
 if (isDetached)
     context.Users.Attach(user);

1
これ、ただ私だけではなく、「外れ」の「EntityState.Detached」を使用していた...私のために働いた
マルセロMyara

22
うーん、あなたの解決策を試しましたが、私にとってはisDetachedは本当ですが、エントリをコンテキストにアタッチしようとすると同じエラーが発生します
Prokurors 2014年

優れたアプローチ。それでも私の汎用リポジトリで動作しました。
ATHER 2015年

5
その@Prokurors私は最近、モッシュのチェックは常にエラーを防ぐのに十分ではない理由を学んだ、された.Include()「エド・ナビゲーションのプロパティはまた、あなたが呼び出すときに接続しようとしている.Attachか、そのセットのEntityStateためにEntityState.Unchanged-エンティティのいずれかがを参照している場合、それらが競合します同じエンティティ。基本エンティティのみをアタッチする方法がわからないため、プロジェクトを少し再設計して、設計どおりに「ビジネストランザクション」ごとに個別のコンテキストを使用する必要がありました。これは将来のプロジェクトのためにメモしておきます。
AskeB.18年

18

この拡張方法を試してください(これはテストされておらず、すぐに使用できます)。

public static bool IsAttachedTo(this ObjectContext context, object entity) {
    if(entity == null) {
        throw new ArgumentNullException("entity");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

編集で説明する状況を考えるEntityKeyと、オブジェクトの代わりに受け入れる次のオーバーロードを使用する必要がある場合があります。

public static bool IsAttachedTo(this ObjectContext, EntityKey key) {
    if(key == null) {
        throw new ArgumentNullException("key");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(key, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

EntityKey状況に合わせて構築するには、ガイドラインとして以下を使用します。

EntityKey key = new EntityKey("MyEntities.User", "Id", 1);

プロパティ(インターフェイスから)を使用してEntityKey、の既存のインスタンスからを取得できます。UserUser.EntityKeyIEntityWithKey


これは非常に良いですが、私の状況ではうまくいきません...質問を詳細で更新します。psは、ブール値ではなく静的なブール値が必要ですが、それ以外の非常に優れた拡張メソッドです。
joshcomley 2009

@joshcomley:の代わりにTryGetObjectStateEntryを受け入れるのオーバーロードを使用して対処できると思います。それに応じて編集しました。これで問題が解決しない場合はお知らせください。設計図に戻ります。EntityKeyobject
ジェイソン

ああ、これを見たばかりです-私は投稿したばかりの回答で概説されている解決策に取り組んでいました。あなたの助けとポインタのために+1 !!
joshcomley 2009

私はエラーを取得していますなぜすべてのアイデア、詳細はこちらstackoverflow.com/questions/6653050/...
Joper

6

チェックしようとしているオブジェクトのエンティティキーを使用する:

var entry = context.ObjectStateManager.GetObjectStateEntry("EntityKey");
if (entry.State == EntityState.Detached)
{
  // Do Something
}

親切、

ダン


0

これはOPの質問に直接答えるものではありませんが、これが私の解決方法です。

これは、のDbContext代わりにを使用している人向けですObjectContext

    public TEntity Retrieve(object primaryKey)
    {
        return DbSet.Find(primaryKey);
    }

DbSet.Findメソッド

指定された主キー値を持つエンティティを検索します。指定された主キー値を持つエンティティがコンテキストに存在する場合、ストアにリクエストを送信せずにすぐに返されます。それ以外の場合は、指定された主キー値を持つエンティティのストアにリクエストが行われ、このエンティティが見つかった場合は、コンテキストにアタッチされて返されます。コンテキストまたはストアにエンティティが見つからない場合は、nullが返されます。

基本的に、指定されたオブジェクトのアタッチされたオブジェクトがprimaryKey返されるため、返されたオブジェクトに変更を適用するだけで、適切なインスタンスを維持できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.