Entity Framework'te bir nesnenin zaten bir veri bağlamına eklenmiş olup olmadığını kontrol etmek mümkün müdür?


87

Belirli bir içeriğe zaten eklenmiş bir nesneyi eklemeye çalışırken aşağıdaki hatayı alıyorum context.AttachTo(...):

ObjectStateManager'da aynı anahtara sahip bir nesne zaten var. ObjectStateManager, aynı anahtarla birden çok nesneyi izleyemez.

Aşağıdakiler doğrultusunda bir şeyi başarmanın bir yolu var mı:

context.IsAttachedTo(...)

Şerefe!

Düzenle:

Jason'ın özetlediği uzatma yöntemi yakındır, ancak benim durumum için çalışmıyor.

Başka bir sorunun cevabında belirtilen yöntemi kullanarak bazı işler yapmaya çalışıyorum:

Linq to Entities * kullanarak tablomdan bir veya daha fazla satırı, önce satırları almadan * nasıl silebilirim?

Kodum biraz şuna benziyor:

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

Bu, aynı yöntemi kullandığım ve sahte bir Usernesne eklemeye çalıştığım o kullanıcı için başka bir şey yaptığım sürece iyi çalışıyor . Bu başarısız oluyor çünkü daha önce bu kukla kullanıcı nesnesini ekledim. Bunu nasıl kontrol edebilirim?

Yanıtlar:


57

İşte bulduğum sonuç çok güzel çalışıyor:

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);
}

Bunu şu şekilde arayabilirsiniz:

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

Bu çok güzel çalışıyor çünkü context.AttachTo(...)her seferinde yukarıda bahsettiğim kimlik numarasını kullanabilmeniz dışında. Ya önceden eklenen nesne ya da kendi nesnenizin eklenmesi ile sonuçlanırsınız. CreateEntityKeyBağlamı çağırmak , güzel ve genel olmasını sağlar ve başka kodlama gerektirmeyen bileşik anahtarlarla bile çalışacaktır (çünkü EF bunu zaten bizim için yapabilir!).


Ben de benzer bir problem yaşıyordum ve bu benimkini çözdü - harika, şerefe! +1
RPM1984

4
Dize parametresi, varlığın ait olduğu koleksiyon için bir seçici işlevle değiştirildiğinde daha da iyi olur.
Jasper

1
Neden (T)entry.Entitybazen null döndürdüğüne dair bir fikriniz var mı?
Tr1stan

1
EntitySetName'imi neye ayarlamam gerektiğini anlayamıyorum. Sürekli bir istisna alıyorum. Gerçekten sinirliyim çünkü tek yapmak istediğim, uygulamamı havaya uçurmak için çok fazla gizli anlamsızlıkla uğraşmak istemediğim bir kullanıcıyı silmek.
Julie

1
Tzaten öyleyse , mülkünü yeniden inşa etmek yerine IEntityWithKeykullanamaz mısınız entity.EntityKeyyoksa EntitySetName?
drzaus

55

Daha basit bir yaklaşım:

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

1
Bu benim için çalıştı, sadece "bağımsız" yerine "EntityState.Detached" kullanmak zorunda kaldım ...
Marcelo Myara

22
Hmm Çözümünüzü denedim, ancak benim için Ayrılmış doğru, ancak içeriğe girişi
eklemeye

Mükemmel değer biçme. Generic Depomla bile çalıştı.
16'da

5
O @Prokurors Geçenlerde Mosh çeki hep hatayı önlemek için yeterli değildir nedeni öğrendim edilir .Include()'ed navigasyon özellikleri de Aradığınızda eklenecek teşebbüs edilir .Attachveya onun setleri EntityStateiçin EntityState.Unchanged- varlıkların herhangi atıfta eğer onlar çelişecek aynı varlık. Sadece temel varlığı nasıl ekleyeceğimi bulamadım, bu yüzden tasarlandığı gibi her "ticari işlem" için ayrı bağlamlar kullanmak üzere projeyi biraz yeniden tasarlamak zorunda kaldım. Bunu gelecekteki projeler için not edeceğim.
Aske

18

Bu uzatma yöntemini deneyin (bu test edilmemiştir ve bilindiği gibi):

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;
}

Düzenlemenizde tanımladığınız durum göz önüne alındığında, EntityKeybir nesne yerine bir kabul eden aşağıdaki aşırı yüklemeyi kullanmanız gerekebilir :

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;
}

EntityKeyKendi durumunuzda bir oluşturmak için , aşağıdakileri bir kılavuz olarak kullanın:

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

Özelliği kullanarak (arabirimden ) EntityKeyvarolan bir örneğinden alabilirsiniz .UserUser.EntityKeyIEntityWithKey


Bu çok iyi, ama benim durumumda işe yaramıyor ... Soruyu ayrıntılarla güncelleyeceğim. ps, bool değil, boole ve statik, ancak bu oldukça harika genişletme yöntemi dışında!
joshcomley

@joshcomley: Ben size aşırı yüklenmeye kullanarak ele alabilir düşünüyorum TryGetObjectStateEntrydair bir kabul EntityKeyyerine ait object. Buna göre düzenledim. Bunun yardımcı olup olmadığını bana bildirin ve çizim tahtasına geri dönelim.
jason

Ah bunu gördüm - az önce yayınladığım bir cevapta özetlenen bir çözüm üzerinde çalışıyordum. Yardımınız ve işaretçileriniz için +1 !!
joshcomley

İ bir hata alıyorum neden herhangi bir fikir, ayrıntıların burada stackoverflow.com/questions/6653050/...
Joper

6

Kontrol etmeye çalıştığınız nesnenin varlık anahtarını kullanarak:

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

Nezaket,

Dan


0

Bu doğrudan OP'lerin sorusuna cevap vermiyor ama benimkini böyle çözdüm.

Bu, DbContextyerine kullananlar içindir ObjectContext.

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

DbSet.Find Yöntemi :

Verilen birincil anahtar değerlerine sahip bir varlık bulur. Verilen birincil anahtar değerlerine sahip bir varlık bağlamda mevcutsa, depoya bir talepte bulunmadan hemen döndürülür. Aksi takdirde, verilen birincil anahtar değerlerine sahip bir varlık için depoya bir istek yapılır ve bu varlık bulunursa bağlama eklenir ve döndürülür. Bağlamda veya depoda hiçbir varlık bulunamazsa, null döndürülür.

Temel olarak, verilen nesnenin ekli nesnesini döndürür, primaryKeyböylece doğru örneği korumak için döndürülen nesneye değişiklikleri uygulamanız yeterlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.