Entity Framework 6 kullanarak kayıt nasıl güncellenir?


245

EF6 kullanarak kaydı güncellemeye çalışıyorum. Öncelikle kaydı bulursanız güncelleyin. İşte benim kod: -

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

Her zaman yukarıdaki kodu kullanarak kayıt güncellemeye çalışın, bu hatayı alıyorum: -

{System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Güncelleme, ekleme veya silme ifadesini depolamak beklenmedik sayıda satırı etkiledi (0). Varlıklar yüklendiği için varlıklar değiştirilmiş veya silinmiş olabilir. ObjectStateManager girişini yenile


7
Yan not: catch (Exception ex){throw;}gereksizdir ve tamamen kaldırabilirsiniz.
Sriram Sakthivel

yakalamak blok deneyin sadece başarısızlık nedenini bulmaktır. Ama yine de bu kod neden başarısız olduğunu anlamadı?
user1327064

2
Bu konuda uzman değilim, bu soruya cevap veremiyorum. ancak try catch olmadan , istisna atıldığında break özelliğini de istisna olduğunda hata ayıklayıcıyı kırmak için kullanabilirsiniz.
Sriram Sakthivel

1
Hiçbir şeyi değiştirmedin. Varlık durumu ile oynamak, nesnenin gerçekten değiştirilmemiş olduğu gerçeğini değiştirmez.
Jonathan Allen

1
Seninle aynı şeyi yaptım ve hatayı anlamadım. İstisna DbUpdateConcurrencyException diyor. Eşzamanlılığı nasıl ele aldınız? Zaman damgası kullandınız mı, nesneleri yeniden kopyalayıp birleştirdiniz mi yoksa kendi kendini izleyen varlıklar mı kullandınız? (En çok kullanılan 3 yaklaşım). Eşzamanlılığı ele almadıysanız, sorun budur sanırım.
El Mac

Yanıtlar:


345

Kaydı güncellemeye çalışıyorsunuz (bu benim için "mevcut bir kayıttaki bir değeri değiştirmek ve geri kaydetmek" anlamına gelir). Bu yüzden nesneyi almanız, bir değişiklik yapmanız ve kaydetmeniz gerekir.

using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        result.SomeValue = "Some new value";
        db.SaveChanges();
    }
}

16
Değer atamak veritabanını güncellemez db.SaveChanges(), bağlamda değiştirilmiş nesnelerle çağrı yapmak veritabanını günceller.
Craig

6
Yine de beni büyülüyor ... bu yüzden var sonuç, aslında dbcontext'e bağlı hale geliyor ... yani bu, herhangi bir dbcontext üyesi tarafından başlatılan herhangi bir değişkenin aslında veritabanıyla ilişkilendirilmiş olacağı anlamına gelir, böylece bu değişkene herhangi bir değişiklik uygulanır , ayrıca uygulanır mı yoksa kalıcı mıdır?
14'de

6
İçerik nesneyi oluşturduğundan, nesnede yapılan değişiklikler de dahil olmak üzere içerik nesneyi izleyebilir. SaveChangesBağlamı çağırdığınızda , eklenecek, değiştirilecek veya silinecek olup olmadığını belirlemek için izlediği tüm nesneleri değerlendirir ve bağlı veritabanına uygun SQL'i verir.
Craig

3
Aynı sorunla karşı karşıyayım - EF6 kullanarak bir varlığı güncellemeye çalışıyor. Ekle + EntityState.Modified çalışmıyor. Çalışan tek şey - nesneyi almanız, istediğiniz değişiklikleri yapmanız ve db.SaveChanges () aracılığıyla kaydetmeniz gerekir;
Gurpreet Singh

7
Güncellemek için önce nesneyi almanız GEREKMEMELİDİR. Birincil anahtar değerlerinden birini (kompozit anahtar) değiştirmeye çalıştığımı anlayana kadar aynı sorunu yaşadım. Doğru bir birincil anahtar sağladığınız sürece, tabloda tanımlanan diğer bütünlük kısıtlamalarını ihlal etmemeniz koşuluyla EntityState'i Değiştirilmiş ve SaveChanges () işlevini ayarlayabilirsiniz.
adrianz

166

Entity Framework kaynak kodunu gözden geçiriyor ve Key özelliği biliyorsanız aslında bir varlık güncellemek için bir yol buldum:

public void Update<T>(T item) where T: Entity
{
    // assume Entity base class have an Id property for all items
    var entity = _collection.Find(item.Id);
    if (entity == null)
    {
        return;
    }

    _context.Entry(entity).CurrentValues.SetValues(item);
}

Aksi takdirde, fikirler için AddOrUpdate uygulamasını kontrol edin .

Umarım bu yardım!


12
Güzel! Tüm özellikleri numaralandırmaya gerek yoktur. Ben varsayalım SaveChanges()çağrı değerlerini ayarladıktan sonra gereklidir.
Jan Zahradník

3
Evet, SaveChanges () üzerinde değişiklikler devam edecek
Miguel

1
Harika bir yanıt, IntelliSense ile böyle bir şey yapmanın ÇALIŞMADIĞI çok açık değildi: _context.MyObj = newObj; sonra SaveChanges () veya .... _context.MyObj.Update (newObj) sonra SaveChanges (); Çözümünüz, tüm özellikler arasında geçiş yapmak zorunda kalmadan tüm nesneyi günceller.
Adam

7
Bu bana kimlik alanını düzenlemeye çalıştığımdan şikayet ediyor
Vasily Hall

3
@VasilyHall - Kimlik alanları (veya Birincil Anahtar olarak tanımladığınız her şey) modeller arasında farklıysa (modellerden birinde null / 0 dahil) oluşur. Kimliklerin iki model arasında eşleştiğinden emin olun ve gayet iyi güncellenir.
Gavin Coates

51

AddOrUpdateYöntemi kullanabilirsiniz :

db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();

1
IMO en iyi çözümü
Norgul

112
.AddOrUpdate()veritabanı geçişi sırasında kullanıldığında, bu yöntemi taşıma işlemlerinin dışında, bu yüzden neden Entity.Migrationsad alanında kullanmanız kesinlikle önerilmez .
Adam Vincent

1
@AdamVincent'in belirttiği gibi, AddOrUpdate()yöntem taşıma işlemleri için tasarlanmıştır ve yalnızca mevcut satırı güncellemeniz gerektiğinde durum için uygun değildir. Arama referansı (yani ID) ile kitabınız yoksa, yeni satır oluşturur ve bazı durumlarda bir sorun olabilir (örneğin, size 404-NotFound yanıtı döndürmeniz gereken bir API'nız varsa varolmayan satır için PUT yöntemini çağırmayı deneyin).
Marko

4
Don; t ne yaptığınızı bilmiyorsanız bunu kullanın !!!!!!!!!!!!!!!! okuma: michaelgmccarthy.com/2016/08/24/…
Yusha

4
Bugün tekrar bu konuya geldim, sadece istediğiniz kullanım durumu için iyi bir çözüm olmadığı konusunda sizi uyarabilir miyim
Yusha

23

Böylece güncellenmiş bir varlığınız var ve bunu veritabanında en az kodla güncellemek istiyorsunuz ...

Eşzamanlılık her zaman zordur, ancak güncellemelerinizin kazanmasını istediğinizi varsayıyorum. İşte aynı durumum için bunu yaptım ve sınıflarınızı taklit etmek için isimleri değiştirdim. Başka bir deyişle, sadece değiştirmek attachiçin add, ve benim için çalışıyor:

public static void SaveBook(Model.Book myBook)
{
    using (var ctx = new BookDBContext())
    {
        ctx.Books.Add(myBook);
        ctx.Entry(myBook).State = System.Data.Entity.EntityState.Modified;
        ctx.SaveChanges();
    }
}

10

Nesnenizdeki tüm alanları güncellemek istiyorsanız Entry () yöntemini kullanmalısınız. Ayrıca alan kimliğini (anahtar) değiştiremeyeceğinizi unutmayın, bu nedenle önce kimliği düzenlediğinizle aynı değere ayarlayın.

using(var context = new ...())
{
    var EditedObj = context
        .Obj
        .Where(x => x. ....)
        .First();

    NewObj.Id = EditedObj.Id; //This is important when we first create an object (NewObj), in which the default Id = 0. We can not change an existing key.

    context.Entry(EditedObj).CurrentValues.SetValues(NewObj);

    context.SaveChanges();
}

2
En azından soruyu cevaplamaya çalışmalısınız, sadece kodu yayınlamayın
StaceyGirl

Soruya daha iyi yardımcı olmak için lütfen bir kod pasajı bırakmak yerine soruya biraz açıklama yapın.
feanor07

9

Bu kod, önce kaydı döndürmek için bir sorgu yapmadan yalnızca bir dizi sütunu güncelleme testinin sonucudur. Önce Entity Framework 7 kodunu kullanır.

// This function receives an object type that can be a view model or an anonymous 
// object with the properties you want to change. 
// This is part of a repository for a Contacts object.

public int Update(object entity)
{
    var entityProperties =  entity.GetType().GetProperties();   
    Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

    if (con != null)
    {
        _context.Entry(con).State = EntityState.Modified;
        _context.Contacts.Attach(con);

        foreach (var ep in entityProperties)
        {
            // If the property is named Id, don't add it in the update. 
            // It can be refactored to look in the annotations for a key 
            // or any part named Id.

            if(ep.Name != "Id")
                _context.Entry(con).Property(ep.Name).IsModified = true;
        }
    }

    return _context.SaveChanges();
}

public static object ToType<T>(object obj, T type)
{
    // Create an instance of T type object
    object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

    // Loop through the properties of the object you want to convert
    foreach (PropertyInfo pi in obj.GetType().GetProperties())
    {
        try
        {
            // Get the value of the property and try to assign it to the property of T type object
            tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
        }
        catch (Exception ex)
        {
            // Logging.Log.Error(ex);
        }
    }
    // Return the T type object:         
    return tmp;
}

İşte tam kod:

public interface IContactRepository
{
    IEnumerable<Contacts> GetAllContats();
    IEnumerable<Contacts> GetAllContactsWithAddress();
    int Update(object c);
}

public class ContactRepository : IContactRepository
{
    private ContactContext _context;

    public ContactRepository(ContactContext context)
    {
        _context = context;
    }

    public IEnumerable<Contacts> GetAllContats()
    {
        return _context.Contacts.OrderBy(c => c.FirstName).ToList();
    }

    public IEnumerable<Contacts> GetAllContactsWithAddress()
    {
        return _context.Contacts
            .Include(c => c.Address)
            .OrderBy(c => c.FirstName).ToList();
    }   

    //TODO Change properties to lambda expression
    public int Update(object entity)
    {
        var entityProperties = entity.GetType().GetProperties();

        Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

        if (con != null)
        {
            _context.Entry(con).State = EntityState.Modified;
            _context.Contacts.Attach(con);

            foreach (var ep in entityProperties)
            {
                if(ep.Name != "Id")
                    _context.Entry(con).Property(ep.Name).IsModified = true;
            }
        }

        return _context.SaveChanges();
    }

    public static object ToType<T>(object obj, T type)
    {
        // Create an instance of T type object
        object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

        // Loop through the properties of the object you want to convert
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
        {
            try
            {
                // Get the value of the property and try to assign it to the property of T type object
                tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
            }
            catch (Exception ex)
            {
                // Logging.Log.Error(ex);
            }
        }
        // Return the T type object
        return tmp;
    }
}    

public class Contacts
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Company { get; set; }
    public string Title { get; set; }
    public Addresses Address { get; set; }    
}

public class Addresses
{
    [Key]
    public int Id { get; set; }
    public string AddressType { get; set; }
    public string StreetAddress { get; set; }
    public string City { get; set; }
    public State State { get; set; }
    public string PostalCode { get; set; }  
}

public class ContactContext : DbContext
{
    public DbSet<Addresses> Address { get; set; } 
    public DbSet<Contacts> Contacts { get; set; } 
    public DbSet<State> States { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connString = "Server=YourServer;Database=ContactsDb;Trusted_Connection=True;MultipleActiveResultSets=true;";
        optionsBuilder.UseSqlServer(connString);
        base.OnConfiguring(optionsBuilder);
    }
}

7

.Net çekirdeği için

context.Customer.Add(customer);
context.Entry(customer).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
context.SaveChanges();

5

İşte bu sorun için en iyi çözüm: Görünüm'de tüm kimliği (Anahtarlar) ekleyin. (Birinci, İkinci ve Üçüncü) adlı birden çok tablonuz olmasını düşünün

@Html.HiddenFor(model=>model.FirstID)
@Html.HiddenFor(model=>model.SecondID)
@Html.HiddenFor(model=>model.Second.SecondID)
@Html.HiddenFor(model=>model.Second.ThirdID)
@Html.HiddenFor(model=>model.Second.Third.ThirdID)

C # kodunda,

[HttpPost]
public ActionResult Edit(First first)
{
  if (ModelState.Isvalid)
  {
    if (first.FirstID > 0)
    {
      datacontext.Entry(first).State = EntityState.Modified;
      datacontext.Entry(first.Second).State = EntityState.Modified;
      datacontext.Entry(first.Second.Third).State = EntityState.Modified;
    }
    else
    {
      datacontext.First.Add(first);
    }
    datacontext.SaveChanges();
    Return RedirectToAction("Index");
  }

 return View(first);
}

5

Attachbir varlığın izlenmesi durumunda izleme durumu ayarlanır Unchanged. Mevcut bir varlığı güncellemek için tek yapmanız gereken izleme durumunu olarak ayarlamaktır Modified. Göre Ef6'yı docs :

Veritabanında zaten var olduğunu bildiğiniz ancak değişikliklerin yapılmış olabileceğini bildiğiniz bir varlığınız varsa, varlığa varlığı iliştirmesini ve durumunu Değiştirildi olarak ayarlamasını sağlayabilirsiniz. Örneğin:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

4
using(var myDb = new MyDbEntities())
{

    user user = new user();
    user.username = "me";
    user.email = "me@me.com";

    myDb.Users.Add(user);
    myDb.users.Attach(user);
    myDb.Entry(user).State = EntityState.Modified;//this is for modiying/update existing entry
    myDb.SaveChanges();
}

4

Worksţe yarayan bir yol buldum.

 var Update = context.UpdateTables.Find(id);
        Update.Title = title;

        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();


1

İşte benim RIA sonrası varlık güncelleme yöntemim (Ef6 zaman dilimi için):

public static void UpdateSegment(ISegment data)
{
    if (data == null) throw new ArgumentNullException("The expected Segment data is not here.");

    var context = GetContext();

    var originalData = context.Segments.SingleOrDefault(i => i.SegmentId == data.SegmentId);
    if (originalData == null) throw new NullReferenceException("The expected original Segment data is not here.");

    FrameworkTypeUtility.SetProperties(data, originalData);

    context.SaveChanges();
}

FrameworkTypeUtility.SetProperties()NuGet'te AutoMapper'dan çok önce yazdığım küçük bir yardımcı program işlevi olduğuna dikkat edin :

public static void SetProperties<TIn, TOut>(TIn input, TOut output, ICollection<string> includedProperties)
    where TIn : class
    where TOut : class
{
    if ((input == null) || (output == null)) return;
    Type inType = input.GetType();
    Type outType = output.GetType();
    foreach (PropertyInfo info in inType.GetProperties())
    {
        PropertyInfo outfo = ((info != null) && info.CanRead)
            ? outType.GetProperty(info.Name, info.PropertyType)
            : null;
        if (outfo != null && outfo.CanWrite
            && (outfo.PropertyType.Equals(info.PropertyType)))
        {
            if ((includedProperties != null) && includedProperties.Contains(info.Name))
                outfo.SetValue(output, info.GetValue(input, null), null);
            else if (includedProperties == null)
                outfo.SetValue(output, info.GetValue(input, null), null);
        }
    }
}

Not: Yalnızca özellikleriniz modelinizde içine kaydedilen ViewModel nesnenizle tam olarak aynıysa çalışır.
vapcguy

1

Renat'ın dediği gibi kaldırın: db.Books.Attach(book);

Ayrıca, bu sorgu varlık çerçevesinin model durumunu attığından, sonuç sorgunuzu "AsNoTracking" kullanacak şekilde değiştirin. "Sonuç" un şimdi izlenecek kitap olduğunu düşünüyor ve bunu istemiyorsunuz.

var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);

1

Dene....

UpdateModel (kitap);

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            UpdateModel(book);
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

1

Zaten birkaç kez iyi yanıtlandığını biliyorum, ama bunu yapmanın yolunu seviyorum. Umarım birine yardım eder.

//attach object (search for row)
TableName tn = _context.TableNames.Attach(new TableName { PK_COLUMN = YOUR_VALUE});
// set new value
tn.COLUMN_NAME_TO_UPDATE = NEW_COLUMN_VALUE;
// set column as modified
_context.Entry<TableName>(tn).Property(tnp => tnp.COLUMN_NAME_TO_UPDATE).IsModified = true;
// save change
_context.SaveChanges();

1

Varlık Çerçevesi 6.2.0 için bu.

DbSetGüncellenmesi veya oluşturulması gereken belirli bir öğeniz ve 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);
    }
}
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.