Entity Framework ile Else Insert Mantık Varsa Satırı Güncelle


179

Entity Framework kullanarak "güncelleme satırı varsa eklemek" mantık uygulamak için en verimli yolu hakkında önerileri var mı?


2
Bu, saklanan yordamda veritabanı altyapısı düzeyinde yapılması gereken bir şeydir. Aksi takdirde, bir işleme algılama / güncelleme / ekleme işlemlerini sarmanız gerekir.
Stephen Chung

1
@Stephen: Aslında, bunu yaptım. Teşekkürler.
Jonathan Wood

Jonathan, sorunuz benim için çok faydalı. Neden saklı yordama geçtiniz?
anar khalilov

2
@Anar: Sadece daha kolaydı ve çok daha verimli olmasını bekliyorum.
Jonathan Wood

Her tablo için saklı bir yordam yazmak zorunda mısınız?
tofutim

Yanıtlar:


174

Ekli nesneyle (içeriğin aynı örneğinden yüklenen nesne) çalışıyorsanız, şunları kullanabilirsiniz:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
    context.MyEntities.AddObject(myEntity);
}

// Attached object tracks modifications automatically

context.SaveChanges();

Nesnenin anahtarı hakkında herhangi bir bilgi kullanabiliyorsanız şu şekilde kullanabilirsiniz:

if (myEntity.Id != 0)
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();

Nesnenin kimliğiyle varlığına karar veremiyorsanız, arama sorgusunu gerçekleştirmelisiniz:

var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();

Teşekkürler. İhtiyacım olana benziyor. Bir süredir beni rahatsız eden bir soru sorabilir miyim? Normalde bağlamımı kısa bir usingbloğa koyarım. Bağlamı bir süreliğine hafızada bırakmak uygun mudur? Örneğin, bir Windows formunun ömrü boyunca? Normalde veritabanında minimum yük sağlamak için veritabanı nesnelerini temizlemeye çalışırım. EF bağlamımı yok etmeyi bekleyen bir sorun yok mu?
Jonathan Wood

Şunu kontrol edin: stackoverflow.com/questions/3653009/… nesne bağlamı olabildiğince kısa olmalıdır, ancak winforms veya wpf olması durumunda bağlamın sunum yapan kişi kadar uzun yaşadığı anlamına gelebilir. Bağlantılı soru, winforms içinde nhibernate oturumu kullanma hakkında msdn makaleye bağlantı içerir. Aynı yaklaşım bağlam için de kullanılabilir.
Ladislav Mrnka

1
Ama ne bir nesne listesi ile bunu yapmak gerekiyorsa ... benim veritabanında aynı kimliğe sahip satırların bir listesi var ve ben eğer varsa ww değiştirmek veya eklemek istiyorum .. nasıl yaparım? Teşekkürler!
Phoenix_uy

1
Bu yanıt harika görünüyor, ancak güncelleştirme sırasında bu sorunla karşılaşıyorum: ObjectStateManager'da aynı anahtara sahip bir nesne zaten var. ObjectStateManager aynı anahtarla birden fazla nesneyi izleyemez.
John Zumbrum

1
Güncelleme yapmadan önce anahtarını almak için mevcut nesneyi getirmeyle ilgili bir sorun yaşıyorum; bu arama nesnesinin ayrılması ilk olarak onu düzeltmeye yardımcı oldu.
John Zumbrum

33

Entity Framework 4.3 itibariyle AddOrUpdate, ad alanında bir yöntem vardır System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set,
    params TEntity[] entities
)
where TEntity : class

hangi doktor tarafından :

SaveChanges çağrıldığında varlıkları anahtarla ekler veya günceller. Veritabanı terminolojisinden "yukarı" bir işleme eşdeğerdir. Bu yöntem, Taşıma İşlemleri kullanarak veri eklerken faydalı olabilir.


@ Smashing1978 tarafından yapılan yorumu cevaplamak için , @Colin tarafından sağlanan bağlantıdan ilgili parçaları yapıştıracağım

AddOrUpdate'in görevi, geliştirme sırasında veri eklediğinizde kopya oluşturmamanızı sağlamaktır.

İlk olarak, veritabanınızda anahtar olarak sağladığınız şeyin (ilk parametre) AddOrUpdate'de sağlanan eşlenmiş sütun değeri (veya değerleri) ile eşleştiği bir kayıt arayan bir sorgu yürütür. Bu, eşleştirme için biraz gevşek, ancak tasarım zamanı verilerini tohumlamak için mükemmel bir şekilde iyi.

Daha da önemlisi, bir eşleşme bulunursa, güncelleme tüm güncellemeleri yapar ve AddOrUpdate'inizde olmayanları geçersiz kılar.

Bununla birlikte, harici bir hizmetten veri çektiğim ve birincil anahtarla (ve tüketiciler için yerel verilerim salt okunur) mevcut değerleri eklediğim veya güncellediğim bir durumum var - AddOrUpdate6 aydan fazla bir süredir üretimde kullanıyorum ve bu yüzden çok sorun yok.


7
System.Data.Entity.Migrations ad alanı, kod tabanlı geçişler ve bunların yapılandırmalarıyla ilgili sınıflar içerir. Göç etmeyen varlık AddOrUpdates için depolarımızda bunu kullanmamamız için herhangi bir neden var mı?
Matt Lengenfelder

10
AddOrUpdate yöntemine dikkat edin: thedatafarm.com/data-access/…
Colin

1
Bu makalede AddOrUpdate'ın neden kullanılmaması gerektiği açıklanmaktadır michaelgmccarthy.com/2016/08/24/…
Nolmë Informatique

11

Sihir çağırırken olur SaveChanges()ve akıma bağlıdır EntityState. Varlığın bir EntityState.Addeddeğeri varsa, veritabanına eklenir, eğer varsa EntityState.Modified, veritabanında güncellenir. Böylece InsertOrUpdate()aşağıdaki gibi bir yöntem uygulayabilirsiniz :

public void InsertOrUpdate(Blog blog) 
{ 
    using (var context = new BloggingContext()) 
    { 
        context.Entry(blog).State = blog.BlogId == 0 ? 
                                   EntityState.Added : 
                                   EntityState.Modified; 

        context.SaveChanges(); 
    } 
}

EntityState hakkında daha fazla bilgi

Eğer kontrol edemiyorsanız Id = 0yeni bir varlık olduğunu olmadığını belirlemek için, kontrol Ladislav Mrnka cevabını .


8

Aynı içeriği kullandığınızı ve herhangi bir varlığı ayırmadığınızı biliyorsanız, bunun gibi genel bir sürüm oluşturabilirsiniz:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
    if (db.Entry(entity).State == EntityState.Detached)
        db.Set<T>().Add(entity);

    // If an immediate save is needed, can be slow though
    // if iterating through many entities:
    db.SaveChanges(); 
}

db elbette bir sınıf alanı olabilir veya yöntem statik ve bir uzantı yapılabilir, ancak bu temeldir.


4

Ladislav'un ​​cevabı yakındı ama bunu EF6'da (önce veritabanı) çalıştırmak için birkaç değişiklik yapmak zorunda kaldım. Veri bağlamımı AddOrUpdate yöntemimle genişlettim ve şimdiye kadar bu ayrılmış nesnelerle iyi çalışıyor gibi görünüyor:

using System.Data.Entity;

[....]

public partial class MyDBEntities {

  public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
      if (ID != 0) {
          set.Attach(obj);
          ctx.Entry(obj).State = EntityState.Modified;
      }
      else {
          set.Add(obj);
      }
  }
[....]

AddOrUpdate ayrıca System.Data.Entity.Migrations içinde bir uzantı yöntemi olarak bulunur, bu yüzden ben olsaydım aynı yöntem adını kendi yönteminiz için yeniden kullanmaktan kaçınırdım.
AFract

2

Kanımca, yeni yayınlanan EntityGraphOperations for Entity Framework Code ile öncelikle grafikteki tüm varlıkların durumlarını tanımlamak için bazı tekrarlayan kodlar yazmaktan kurtulabilirsiniz . Bu ürünün yazarıyım. Ve bunu github'da yayınladım , kod projesi ( adım adım bir gösteri içerir ve örnek bir proje indirilmeye hazır) ve nuget .

Varlıkların durumunu otomatik olarak Addedveya olarak ayarlarModified . Artık yoksa, hangi varlıkların silinmesi gerektiğini manuel olarak seçeceksiniz.

Örnek:

Diyelim ki bir Personnesnem var. Personolabilir birçok telefon, bir belge ve olabilir bir eş.

public class Person
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
     public string MiddleName { get; set; }
     public int Age { get; set; }
     public int DocumentId {get; set;}

     public virtual ICollection<Phone> Phones { get; set; }
     public virtual Document Document { get; set; }
     public virtual PersonSpouse PersonSpouse { get; set; }
}

Grafiğe dahil olan tüm varlıkların durumunu belirlemek istiyorum.

context.InsertOrUpdateGraph(person)
       .After(entity =>
       {
            // Delete missing phones.
            entity.HasCollection(p => p.Phones)
               .DeleteMissingEntities();

            // Delete if spouse is not exist anymore.
            entity.HasNavigationalProperty(m => m.PersonSpouse)
                  .DeleteIfNull();
       });

Ayrıca bildiğiniz gibi benzersiz anahtar özellikleri Telefon varlık durumunu tanımlarken rol oynayabilir. Böyle özel amaçlar için ExtendedEntityTypeConfiguration<>miras alan bir sınıfımız var EntityTypeConfiguration<>. Biz o zaman bizim haritalama sınıfları miras gerekir tür özel yapılandırmaları kullanmak isterseniz ExtendedEntityTypeConfiguration<>ziyade, EntityTypeConfiguration<>. Örneğin:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
    {
        public PhoneMap()
        {
             // Primary Key
             this.HasKey(m => m.Id);
              
             // Unique keys
             this.HasUniqueKey(m => new { m.Prefix, m.Digits });
        }
    }

Bu kadar.


2

Her ikisini de başka güncelleme ekle

public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();

var query = from data in context.Employee
            orderby data.name
            select data;

foreach (Employee details in query)
{
    if (details.id == 1)
    {
        //Assign the new values to name whose id is 1
        details.name = "Sanjay";
        details. Surname="Desai";
        details.address=" Desiwadi";
    }
    else if(query==null)
    {
        details.name="Sharad";
        details.surname=" Chougale ";
        details.address=" Gargoti";
    }
}

//Save the changes back to database.
context.SaveChanges();
}

Bu yaklaşımı kullandım ama (ilk veya varsayılandan sonra) (query == null) ise kontrol ettim
Patrick

2

Herhangi biriyle mevcut satırı kontrol edin.

    public static void insertOrUpdateCustomer(Customer customer)
    {
        using (var db = getDb())
        {

            db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
            db.SaveChanges();

        }

    }

1

@LadislavMrnka yanıtı için alternatif. Bu Varlık Çerçevesi 6.2.0 için ise.

DbSetGüncellenmesi veya oluşturulması gereken belirli bir öğeniz varsa:

var name = getNameFromService();

var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
    _dbContext.Names.Add(name);
}
else
{
    _dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();

Ancak bu, DbSettek bir birincil anahtar veya birleşik birincil anahtar içeren bir jenerik için de kullanılabilir .

var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");

public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
    foreach (var value in values)
    {
        try
        {
            var keyList = new List<object>();

            //Get key values from T entity based on keyValues property
            foreach (var keyValue in keyValues)
            {
                var propertyInfo = value.GetType().GetProperty(keyValue);
                var propertyValue = propertyInfo.GetValue(value);
                keyList.Add(propertyValue);
            }

            GenericAddOrUpdateDbSet(keyList, value);
            //Only use this when debugging to catch save exceptions
            //_dbContext.SaveChanges();
        }
        catch
        {
            throw;
        }
    }
    _dbContext.SaveChanges();
}

public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
    //Get a DbSet of T type
    var someDbSet = Set(typeof(T));

    //Check if any value exists with the key values
    var current = someDbSet.Find(keyList.ToArray());
    if (current == null)
    {
        someDbSet.Add(value);
    }
    else
    {
        Entry(current).CurrentValues.SetValues(value);
    }
}

-1

Düzeltilmiş

public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity) 
        where T : class
        where T2 : class
        {
            foreach(var e in updateEntity)
            {
                context.Set<T2>().InsertOrUpdate(e);
            }
        }


        public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity) 
        where T : class
        where T2 : class
        {
            if (context.Entry(updateEntity).State == EntityState.Detached)
            {
                if (context.Set<T2>().Any(t => t == updateEntity))
                {
                   context.Set<T2>().Update(updateEntity); 
                }
                else
                {
                    context.Set<T2>().Add(updateEntity);
                }

            }
            context.SaveChanges();
        }

2
Kullanın düzenlemek yerine başka bir cevap gönderme
Suraj Rao
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.