Entity Framework 5 Bir Kaydı Güncelleme


870

ASP.NET MVC3 ortamında Entity Framework 5 içindeki bir kaydı düzenlemek / güncellemek için farklı yöntemler araştırıyorum, ancak şimdiye kadar hiçbiri ihtiyacım olan tüm kutuları işaretliyor. Nedenini açıklayacağım.

Artılarını ve eksilerini anlatacağım üç yöntem buldum:

Yöntem 1 - Orijinal kaydı yükleyin, her özelliği güncelleyin

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Artıları

  • Hangi özelliklerin değişeceğini belirleyebilir
  • Görünümlerin her mülkü içermesi gerekmez

Eksileri

  • Orijinali yüklemek ve güncellemek için veritabanında 2 x sorgu

Yöntem 2 - Orijinal kaydı yükleyin, değiştirilen değerleri ayarlayın

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Artıları

  • Veritabanına yalnızca değiştirilmiş özellikler gönderilir

Eksileri

  • Görüntülemelerin her mülkü içermesi gerekir
  • Orijinali yüklemek ve güncellemek için veritabanında 2 x sorgu

Yöntem 3 - Güncellenmiş kaydı ekleyin ve EntityState'e durumu ayarlayın.

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Artıları

  • Güncellemek için veritabanında 1 x sorgu

Eksileri

  • Hangi özelliklerin değişeceğini belirleyemiyorum
  • Görünümler her mülkü içermelidir

Soru

Size olan sorum; bu hedeflere ulaşmamın temiz bir yolu var mı?

  • Hangi özelliklerin değişeceğini belirleyebilir
  • Görünümlerin her mülkü içermesi gerekmez (şifre gibi!)
  • Güncellemek için veritabanında 1 x sorgu

Bunun dikkat çeken küçük bir şey olduğunu anlıyorum, ancak bunun için basit bir çözümü kaçırıyor olabilirim. Aksi takdirde yöntem geçerli olacaktır ;-)


13
ViewModels ve iyi bir haritalama motoru mu kullanıyorsunuz? Yalnızca "güncellenecek özellikler" görünümünüzü doldurmak (ve daha sonra güncellemek) için alırsınız. Hala güncelleme (orijinal + güncelleme olsun) için 2 sorgu olacak, ama ben buna "Con" demezdim. Tek performans probleminiz buysa, sen mutlu bir adamsın;)
Raphaël Althaus

Teşekkürler @ RaphaëlAlthaus, çok geçerli bir nokta. Bunu yapabilirdim, ama her tablo için n-1 ViewModel oluşturmama beni kurtarmak için doğrudan model ile çalışabilir bir yöntem arıyorum böylece bir dizi tablo için CRUD işlemi oluşturmak zorunda.
Stokedout

3
Şu anki projemde (birçok varlık da) Modeller üzerinde çalışmaya başladık, ViewModels ile çalışmak için zaman kaybedeceğimizi düşünüyoruz. Şimdi ViewModels'e gidiyoruz ve başlangıçta (ihmal edilemez) altyapı çalışmaları ile artık çok, çok, çok daha net ve bakımı daha kolay. Ve daha güvenli (kötü niyetli "gizli alanlar" veya bunun gibi şeylerden korkmaya gerek yok)
Raphaël Althaus

1
Ve artık (korkunç) DropDownLists doldurmak için ViewBags (neredeyse tüm CRU (D) görünümlerimizde en az bir DropDownList var ...)
Raphaël Althaus

Sanırım haklısın, ViewModels'i gözden kaçırmaya çalıştığım için kötüyüm. Evet, ViewBag zaman zaman biraz kirli görünüyor. Genellikle Dino Esposito'nun bloguna göre bir adım daha ileri gider ve InputModels'i de yaratırım, biraz kemer ve parantez ama oldukça iyi çalışır. Sadece model başına 2 ekstra model anlamına gelir - doh ;-)
Stokedout 11:13

Yanıtlar:


681

Şunu arıyorsunuz:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();

59
merhaba @ Ladislav Mrnka, tüm özellikleri bir kerede güncellemek istiyorsam, aşağıdaki kodu kullanabilir miyim? db.Departments.Attach (bölge); db.Entry (departman) .State = EntityState.Modified; db.SaveChanges ();
Foyzul Karim

23
@Foysal: Evet yapabilirsin.
Ladislav Mrnka

5
Bu yaklaşımdaki sorunlardan biri, ciddi bir PITA olan db.Entry () ile alay edememenizdir. EF'in başka bir yerde oldukça iyi bir alay hikayesi var - burada anlatabileceğim kadarıyla can sıkıcı.
Ken Smith

23
@Foysal context.Entry (entity) yapıyor .State = EntityState.Modified tek başına eki yapmaya gerek yok. Otomatik olarak değiştirildi ...
HelloWorld

4
@ Sandman4, diğer tüm mülklerin orada bulunması ve mevcut değere ayarlanması gerektiği anlamına gelir. Bazı uygulama tasarımlarında bu mümkün değildir.
Dan Esparza

176

Kabul edilen cevabı gerçekten seviyorum. Buna yaklaşmanın başka bir yolu daha olduğuna inanıyorum. Bir Görünüm'e dahil etmek istemeyeceğiniz özelliklerin çok kısa bir listesine sahip olduğunuzu varsayalım, bu nedenle varlığı güncellerken bunlar atlanır. Diyelim ki bu iki alan Şifre ve SSN.

db.Users.Attach(updatedUser);

var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;

entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;   

db.SaveChanges();   

Bu örnek, Kullanıcılar tablonuza ve Görünümünüze yeni bir alan ekledikten sonra iş mantığınızı tek başına bırakmanıza olanak tanır.


SSM özelliği için bir değer belirtmezsem hala hata alıyorum, IsModified'i false olarak ayarlamış olsam da hala model kurallarına göre özelliği doğrular. Özellik NULL DEĞİL olarak işaretlenmişse, null değerinden farklı bir değer ayarlamazsanız başarısız olur.
RolandoCC

Bu alanlar formunuzda olmayacağı için bir hata almayacaksınız. Kesinlikle güncellemeyeceğiniz alanları dışarıda bırakır, veritabanına girdiyi ekleyerek geri gönderilen formu kullanarak alır ve girdiye bu alanların değiştirilmediğini bildirirsiniz. Model doğrulaması, bağlamda değil ModelState'de kontrol edilir. Bu örnek mevcut bir kullanıcıya, dolayısıyla "updatedUser" e atıfta bulunuyor. SSN'niz zorunlu bir alansa, ilk oluşturulduğunda orada olurdu.
smd

4
Doğru anlıyorsam, "updatedUser" zaten bir FirstOrDefault () veya benzeri ile doldurulmuş bir nesnenin bir örneğidir, bu yüzden sadece değiştirdiğim özellikleri güncelleştiriyorum ve diğerleri ISModified = false olarak ayarlıyorum. Bu iyi çalışıyor. Ancak, yapmaya çalıştığım şey, bir nesneyi güncelleştirmeden önce herhangi bir FirstOrDefault () yapmadan önce doldurmadan güncelleştirmek. Bu, gerekli tüm alanlar için bir değer belirtmezsem, bu özelliklerde ISModified = false ayarladığımı düşünürsem bir hata aldığım zamandır. giriş.Özellik (e => e.columnA) .IsModified = false; Bu satır olmadan ColumnA başarısız olur.
RolandoCC

Açıkladığınız şey yeni bir varlık yaratmaktır. Bu sadece güncelleme için geçerlidir.
smd

1
RolandoCC, put db.Configuration.ValidateOnSaveEnabled = false; db.SaveChanges () öncesi;
Wilky

28
foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
    if (propertyInfo.GetValue(updatedUser, null) == null)
        propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();

Bu gerçekten güzel bir çözüm gibi görünüyor - gürültü ya da karışıklık yok; özellikleri manuel olarak belirtmeniz gerekmez ve tüm OP mermilerini dikkate alır - bunun daha fazla oy almamasının bir nedeni var mı?
nocarrier

Olmaz. Veritabanına birden fazla isabet, en büyük "eksilerini" birine sahiptir. Yine de orijinali bu cevapla yüklemelisiniz.
smd

1
@ smd neden veritabanına bir kereden fazla vurduğunu söylüyorsunuz? SetValues ​​() kullanmanın bu etkiye sahip olmadığı sürece bunun gerçekleştiğini görmüyorum ama bu doğru gibi görünmüyor.
parlamento

@parlamento Sanırım bunu yazarken uyumuş olmalıyım. Özür. Asıl sorun, amaçlanan bir boş değeri geçersiz kılmaktır. Güncellenmiş kullanıcının artık bir şeye referansı yoksa, temizlemek istiyorsanız orijinal değeriyle değiştirmek doğru olmaz.
smd

22

Havuz taban sınıfıma, İskele tarafından oluşturulan güncelleme yöntemine benzer ek bir güncelleme yöntemi ekledim. Tüm nesneyi "değiştirildi" olarak ayarlamak yerine, bir dizi ayrı özellik ayarlar. (T, sınıf genel bir parametredir.)

public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate)
{
    Context.Set<T>().Attach(obj);

    foreach (var p in propertiesToUpdate)
    {
        Context.Entry(obj).Property(p).IsModified = true;
    }
}

Ve sonra aramak için, örneğin:

public void UpdatePasswordAndEmail(long userId, string password, string email)
{
    var user = new User {UserId = userId, Password = password, Email = email};

    Update(user, u => u.Password, u => u.Email);

    Save();
}

Veritabanına bir geziden hoşlanıyorum. Yine de, özellik kümelerini tekrarlamaktan kaçınmak için görünüm modelleri ile bunu yapmak daha iyidir. Bunu henüz yapmadım çünkü görünüm modeli doğrulayıcılarımdaki doğrulama mesajlarını alan adı projeme getirmekten nasıl kaçınacağımı bilmiyorum.


Aha ... görünüm modelleri için ayrı proje ve görünüm modelleri ile çalışan depolar için ayrı proje.
Ian Warburton

11
public interface IRepository
{
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}

public class Repository : DbContext, IRepository
{
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
    {
        Set<T>().Attach(obj);
        propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
        SaveChanges();
    }
}

Neden sadece DbContext.Attach (obj); DbContext.Entry (obj) .State = EntityState.Modified;
nelsontruran

Bu setgüncelleme deyiminin bir bölümünü kontrol eder .
Tanveer Badar

4

Sadece seçenekler listesine eklemek için. Ayrıca nesneyi veritabanından alabilir ve kaydın değiştirmek istediğiniz bölümlerini güncellemek için Otomatik Eşleyici gibi bir otomatik eşleme aracı kullanabilirsiniz .


3

Kullanım durumunuza bağlı olarak yukarıdaki tüm çözümler geçerlidir. Ancak ben genellikle bunu nasıl yapılır:

Sunucu tarafı kodu (örneğin bir toplu işlem) için genellikle varlıkları yükler ve dinamik proxy'lerle çalışırım. Genellikle toplu işlemlerde, hizmetin çalıştığı sırada verileri yine de yüklemeniz gerekir. Biraz zaman kazanmak için find yöntemini kullanmak yerine toplu veri yüklemeye çalışıyorum. Sürece bağlı olarak iyimser veya kötümser eşzamanlılık kontrolü kullanıyorum (bazı kayıtları düz sql ifadeleri ile kilitlemem gereken paralel yürütme senaryoları dışında her zaman iyimser kullanıyorum, bu nadirdir). Kod ve senaryoya bağlı olarak etki neredeyse sıfıra indirilebilir.

İstemci tarafı senaryoları için birkaç seçeneğiniz vardır

  1. Görünüm modellerini kullanın. Modeller bir UpdateStatus özelliğine sahip olmalıdır (değiştirilmemiş-eklenen-güncellenmiş-silinmiş). Kullanıcı işlemlerine (insert-update-delete) bağlı olarak bu sütuna doğru değeri ayarlamak müşterinin sorumluluğundadır. Sunucu db'yi özgün değerler için sorgulayabilir veya istemci orijinal değerleri değiştirilen satırlarla birlikte sunucuya göndermelidir. Sunucu, orijinal değerleri eklemeli ve yeni değerlerin nasıl işleneceğine karar vermek için her satır için UpdateStatus sütununu kullanmalıdır. Bu senaryoda daima iyimser eşzamanlılık kullanıyorum. Bu yalnızca insert - update - delete deyimlerini yapar ve herhangi bir seçim yapmaz, ancak grafiği yürütmek ve varlıkları güncellemek için bazı akıllı kodlara ihtiyaç duyabilir (senaryonuza - uygulamanıza bağlıdır). Bir haritacı CRUD mantığına yardımcı olabilir ancak işlemez

  2. Bu karmaşıklığın çoğunu (1'de açıklandığı gibi) gizleyen breeze.js gibi bir kitaplık kullanın ve onu kullanım durumunuza uydurmaya çalışın.

Umarım yardımcı olur

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.