Apakah mungkin untuk memeriksa apakah suatu objek sudah dilampirkan ke konteks data di Entity Framework?

87

Saya mendapatkan kesalahan berikut saat mencoba melampirkan objek yang sudah dilampirkan ke konteks tertentu melalui context.AttachTo(...):

Objek dengan kunci yang sama sudah ada di ObjectStateManager. ObjectStateManager tidak dapat melacak banyak objek dengan kunci yang sama.

Adakah cara untuk mencapai sesuatu di sepanjang garis:

context.IsAttachedTo(...)

Bersulang!

Edit:

Metode ekstensi yang dijelaskan Jason sudah dekat, tetapi tidak berhasil untuk situasi saya.

Saya mencoba melakukan beberapa pekerjaan menggunakan metode yang diuraikan dalam jawaban untuk pertanyaan lain:

Bagaimana cara menghapus satu atau beberapa baris dari tabel saya menggunakan Linq ke Entitas * tanpa * mengambil baris terlebih dahulu?

Kode saya terlihat seperti ini:

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

Ini berfungsi dengan baik, kecuali ketika saya melakukan sesuatu yang lain untuk pengguna itu di mana saya menggunakan metode yang sama dan mencoba melampirkan Userobjek dummy . Ini gagal karena saya sebelumnya telah melampirkan objek pengguna dummy itu. Bagaimana saya bisa memeriksa ini?

joshcomley
sumber

Jawaban:

57

Inilah yang saya dapatkan, yang bekerja dengan sangat baik:

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

Anda bisa menyebutnya sebagai berikut:

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

Ini bekerja dengan sangat baik karena sama seperti context.AttachTo(...)kecuali Anda dapat menggunakan trik ID yang saya kutip di atas setiap kali. Anda berakhir dengan salah satu objek yang sebelumnya terpasang atau objek Anda sendiri yang dilampirkan. Memanggil CreateEntityKeykonteks memastikan itu bagus dan umum dan akan bekerja bahkan dengan kunci komposit tanpa pengkodean lebih lanjut (karena EF sudah bisa melakukannya untuk kita!).

joshcomley
sumber
Saya mengalami masalah yang sama, dan ini baru saja menyelesaikan masalah saya - brilian, selamat! +1
RPM 1984
4
Akan lebih baik lagi jika parameter string diganti dengan fungsi pemilih untuk koleksi yang dimiliki entitas.
Jasper
1
Tahu mengapa (T)entry.Entityterkadang mengembalikan nol?
Tr1stan
1
Saya tidak tahu apa yang harus saya setel ke entitySetName saya. Saya terus mendapatkan pengecualian. Saya benar-benar frustrasi karena yang ingin saya lakukan hanyalah menghapus pengguna. Saya tidak ingin berurusan dengan begitu banyak hal tersembunyi yang tidak masuk akal meledakkan aplikasi saya.
Julie
1
jika Tsudah IEntityWithKey, tidak bisakah Anda menggunakan entity.EntityKeypropertinya daripada merekonstruksinya atau perlu menebak / memberikan EntitySetName?
drzaus
55

Pendekatan yang lebih sederhana adalah:

 bool isDetached = context.Entry(user).State == EntityState.Detached;
 if (isDetached)
     context.Users.Attach(user);
Mosh
sumber
1
Ini berhasil untuk saya, hanya saja saya harus menggunakan "EntityState.Detached" alih-alih hanya "terlepas" ...
Marcelo Myara
22
Hmm mencoba solusi Anda, tetapi bagi saya isDetached benar, tetapi masih kesalahan yang sama ketika saya mencoba Lampirkan entri ke konteks
Prokurors
Penilaian yang sangat bagus. Bahkan itu bekerja dengan Repositori Generik saya.
ATHER
5
@Prokurors Saya baru-baru ini mengetahui alasan pemeriksaan Mosh tidak selalu cukup untuk mencegah kesalahan, adalah bahwa .Include()properti navigasi juga dicoba untuk dilampirkan ketika Anda memanggil .Attachatau menyetelnya EntityStateke EntityState.Unchanged- dan mereka akan konflik jika ada entitas yang merujuk ke entitas yang sama. Saya belum menemukan cara untuk hanya melampirkan entitas dasar, jadi saya harus mendesain ulang proyek sedikit, untuk menggunakan konteks terpisah untuk setiap "transaksi bisnis", seperti yang dirancang untuk itu. Saya akan mencatat ini untuk proyek mendatang.
Aske B.
18

Coba metode ekstensi ini (ini belum teruji dan tidak langsung):

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

Mengingat situasi yang Anda gambarkan dalam pengeditan, Anda mungkin perlu menggunakan kelebihan beban berikut yang menerima EntityKeyalih - alih objek:

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

Untuk membangun EntityKeysituasi Anda, gunakan yang berikut sebagai pedoman:

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

Anda bisa mendapatkan EntityKeydari instance yang ada Userdengan menggunakan properti User.EntityKey(dari antarmuka IEntityWithKey).

jason
sumber
Ini sangat bagus, tetapi tidak berhasil untuk saya dalam situasi saya ... Saya akan memperbarui pertanyaan dengan detail. ps Anda ingin bool bukan boolean, dan statis, tetapi selain metode ekstensi yang cukup mengagumkan!
joshcomley
@ Joshcomley: Saya pikir Anda dapat mengatasi menggunakan overload TryGetObjectStateEntryyang menerima dan EntityKeybukannya object. Saya telah mengeditnya. Beri tahu saya jika ini tidak membantu dan kita akan kembali ke papan gambar.
jason
Ah baru saja melihat ini - saya sedang mengerjakan solusi yang diuraikan dalam jawaban yang baru saja saya posting. 1 untuk bantuan dan petunjuk Anda !!
joshcomley
Ada ide mengapa saya mendapatkan kesalahan, detailnya ada di sini stackoverflow.com/questions/6653050/…
Joper
6

Menggunakan kunci entitas dari objek yang Anda coba periksa:

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

Kebaikan,

Dan

Daniel Elliott
sumber
0

Ini tidak secara langsung menjawab pertanyaan OP tetapi ini adalah bagaimana saya menyelesaikan pertanyaan saya.

Ini untuk mereka yang menggunakan, DbContextbukan ObjectContext.

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

Metode DbSet.Find :

Menemukan entitas dengan nilai kunci utama yang diberikan. Jika entitas dengan nilai kunci utama yang diberikan ada dalam konteksnya, maka itu akan segera dikembalikan tanpa membuat permintaan ke toko. Jika tidak, permintaan dibuat ke penyimpanan untuk entitas dengan nilai kunci utama yang diberikan dan entitas ini, jika ditemukan, dilampirkan ke konteks dan dikembalikan. Jika tidak ada entitas yang ditemukan dalam konteks atau penyimpanan, maka null dikembalikan.

Pada dasarnya, ini mengembalikan objek terlampir yang diberikan primaryKeysehingga Anda hanya perlu menerapkan perubahan pada objek yang dikembalikan untuk mempertahankan contoh yang benar.

Lawrence
sumber