1. ホーム
  2. .net

[解決済み] Entity Frameworkでオブジェクトが既にデータコンテキストにアタッチされているかどうかを確認することは可能ですか?

2023-03-13 14:02:28

質問

あるコンテキストにすでにアタッチされているオブジェクトを、次のようなエラーでアタッチしようとします。 context.AttachTo(...) :

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

というようなことを実現する方法はないでしょうか。

context.IsAttachedTo(...)

乾杯

編集します。

Jasonが概説した拡張方法は近いのですが、私の状況ではうまくいきません。

私は別の質問に対する回答で概説された方法を使用して、いくつかの仕事をしようとしています。

Linq to Entitiesを使用して、最初に行を取得することなく、私のテーブルから1つまたは複数の行を削除するにはどうすればよいですか?

私のコードは少しこのようになります。

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

これは問題なく動作しますが、そのユーザーに対して同じメソッドを使用してダミーの User オブジェクトをアタッチしようとしたときです。これは、私が以前にそのダミーユーザーオブジェクトを添付したため、失敗します。どのように私はこれをチェックすることができますか?

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

最終的には以下のようになり、とてもうまくいきました。

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がすでにそれをやってくれているからです!)。

編集、12年後(2021年12月)...ウフフ!

EF Coreで使っているのはこんな感じです。

public static class EfExtensions
{
    public static T AttachToOrGet<T>(this DbContext context, Func<T,bool> predicate, Func<T> factory)
        where T : class, new()
    {
        var match = context.Set<T>().Local.FirstOrDefault(predicate);
        if (match == null)
        {
            match = factory();
            context.Attach(match);
        }

        return match;
    }
}

使用方法

var item = db.AttachToOrGet(_ => _.Id == someId, () => new MyItem { Id = someId });

これをリファクタリングしてエンティティキーで動作させることもできますが、これだけでも十分です!