Bagaimana cara memperbarui catatan menggunakan Entity Framework 6?

245

Saya mencoba memperbarui catatan menggunakan EF6. Pertama-tama menemukan catatan, jika ada, perbarui. Ini kode saya: -

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

Setiap kali saya mencoba memperbarui catatan menggunakan kode di atas, saya mendapatkan kesalahan ini: -

{System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Menyimpan pembaruan, menyisipkan, atau menghapus pernyataan memengaruhi jumlah baris yang tidak terduga (0). Entitas mungkin telah dimodifikasi atau dihapus karena entitas dimuat. Segarkan entri ObjectStateManager

pengguna1327064
sumber
7
Catatan samping: catch (Exception ex){throw;}berlebihan dan Anda dapat menghapusnya sepenuhnya.
Sriram Sakthivel
coba tangkap blok hanya untuk mengetahui alasan kegagalan. Tapi Tetap tidak mengerti mengapa kode ini gagal?
user1327064
2
Saya tidak ahli dalam topik ini, saya tidak bisa menjawab pertanyaan ini. tetapi tanpa try catch juga Anda dapat menggunakan break ketika fitur exception dilemparkan untuk memecah debugger ketika ada pengecualian.
Sriram Sakthivel
1
Anda belum mengubah apa pun. Bermain dengan status Entity tidak akan mengubah fakta bahwa objek belum benar-benar dimodifikasi.
Jonathan Allen
1
Yah, saya melakukan hal yang sama seperti Anda dan tidak mendapatkan kesalahan. Pengecualian mengatakan DbUpdateConcurrencyException. Bagaimana Anda menangani konkurensi? Apakah Anda menggunakan stempel waktu, apakah Anda mengkloning dan kemudian menggabungkan objek lagi atau apakah Anda menggunakan entitas pelacak diri? (3 pendekatan yang paling banyak digunakan). Jika Anda tidak menangani konkurensi, saya kira itulah masalahnya.
El Mac

Jawaban:

344

Anda mencoba memperbarui catatan (yang bagi saya berarti "mengubah nilai pada catatan yang ada dan menyimpannya kembali"). Jadi, Anda perlu mengambil objek, membuat perubahan, dan menyimpannya.

using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        result.SomeValue = "Some new value";
        db.SaveChanges();
    }
}
Craig W.
sumber
16
Menetapkan nilai tidak memperbarui database, memanggil db.SaveChanges()dengan objek yang diubah dalam konteks memperbarui database.
Craig W.
6
Tetap saja itu menarik saya ... jadi hasil var, sebenarnya menjadi terhubung ke dbcontext ... jadi ini berarti bahwa setiap variabel yang dipakai oleh setiap anggota dbcontext benar-benar akan memiliki yang terkait dengan database sehingga perubahan apa pun diterapkan ke variabel itu , apakah ini juga diterapkan atau bertahan?
WantIt
6
Karena konteks yang dihasilkan objek konteks dapat melacak objek, termasuk perubahan pada objek. Ketika Anda memanggil SaveChangeskonteks, evaluasi semua objek yang dilacak untuk menentukan apakah mereka ditambahkan, diubah, atau dihapus dan mengeluarkan SQL yang sesuai untuk database yang terhubung.
Craig W.
3
saya menghadapi masalah yang sama - menggunakan EF6, mencoba memperbarui suatu entitas. Lampirkan + EntityState.Modified tidak berfungsi. Satu-satunya yang berfungsi adalah - Anda perlu mengambil objek, membuat perubahan yang diinginkan, dan menyimpannya melalui db.SaveChanges ();
Gurpreet Singh
7
Anda TIDAK harus mengambil objek terlebih dahulu untuk memperbaruinya. Saya memiliki masalah yang sama sampai saya menyadari bahwa saya sedang mencoba untuk mengubah salah satu nilai kunci utama (kunci komposit). Selama Anda memberikan kunci utama yang benar, Anda bisa mengatur EntityState ke Modified dan SaveChanges () akan berfungsi, asalkan Anda tidak melanggar beberapa batasan integritas lain yang ditentukan pada tabel.
adrianz
165

Saya telah meninjau kode sumber dari Entity Framework dan menemukan cara untuk benar-benar memperbarui entitas jika Anda mengetahui properti Key:

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

Jika tidak, periksa penerapan AddOrUpdate untuk ide.

Semoga bantuan ini!

Miguel
sumber
12
Bagus! Tidak perlu menghitung semua properti. Saya menganggap SaveChanges()panggilan diperlukan setelah menetapkan nilai.
Jan Zahradník
3
Ya, perubahan akan tetap ada di SaveChanges ()
Miguel
1
Jawaban yang bagus, tidak terlalu jelas dengan IntelliSense bahwa melakukan sesuatu seperti ini TIDAK akan berhasil: _context.MyObj = newObj; kemudian SaveChanges () atau .... _context.MyObj.Update (newObj) lalu SaveChanges (); Solusi Anda memperbarui seluruh objek tanpa harus mengulang semua properti.
Adam
7
Ini mengeluh kepada saya bahwa saya mencoba mengedit bidang ID
Vasily Hall
3
@VasilyHall - ini terjadi jika bidang ID (atau apa pun yang telah Anda tetapkan Kunci Primer) berbeda antara model (termasuk null / 0 di salah satu model). Pastikan ID cocok antara kedua model dan itu akan diperbarui dengan baik.
Gavin Coates
51

Anda dapat menggunakan AddOrUpdatemetode ini:

db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();
nicedev80
sumber
1
Solusi terbaik IMO
Norgul
112
.AddOrUpdate()digunakan selama migrasi basis data, sangat disarankan untuk menggunakan metode ini di luar migrasi, karenanya mengapa ada di Entity.Migrationsnamespace.
Adam Vincent
1
Seperti yang dikatakan @AdamVincent, AddOrUpdate()metode ditujukan untuk migrasi dan itu tidak cocok untuk situasi ketika Anda hanya perlu memperbarui baris yang ada. Dalam kasus ketika Anda tidak memiliki buku dengan referensi pencarian (yaitu ID) itu akan membuat baris baru dan itu bisa menjadi masalah dalam kasus yang akan datang (misalnya, Anda memiliki API yang perlu mengembalikan respons 404-NotFound jika Anda coba panggil metode PUT untuk baris yang tidak ada).
Marko
4
Jangan gunakan ini kecuali Anda tahu apa yang Anda lakukan !!!!!!!!!!!!!!!! baca: michaelgmccarthy.com/2016/08/24/…
Yusha
4
Saya kembali lagi ke sini hari ini, bisakah saya memperingatkan Anda semua bahwa ini bukan solusi yang baik untuk kasus penggunaan yang diinginkan
Yusha
23

Jadi Anda memiliki entitas yang diperbarui, dan Anda ingin memperbaruinya dalam database dengan jumlah kode paling sedikit ...

Konkurensi selalu rumit, tetapi saya berasumsi bahwa Anda hanya ingin pembaruan Anda menang. Inilah cara saya melakukannya untuk kasus yang sama dan memodifikasi nama untuk meniru kelas Anda. Dengan kata lain, hanya mengubah attachke add, dan bekerja untuk saya:

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();
    }
}
Duray Akar
sumber
10

Anda harus menggunakan metode Entri () jika Anda ingin memperbarui semua bidang di objek Anda. Ingat juga bahwa Anda tidak dapat mengubah id bidang (kunci) karena itu atur Id terlebih dahulu sama dengan yang Anda edit.

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();
}
Jarek
sumber
2
Anda setidaknya harus mencoba menjawab pertanyaan, bukan hanya memposting kode
StaceyGirl
Tolong buat penjelasan untuk pertanyaan daripada hanya meninggalkan cuplikan kode untuk membantu penanya pertanyaan lebih baik.
feanor07
9

Kode ini adalah hasil dari tes untuk memperbarui hanya satu set kolom tanpa membuat kueri untuk mengembalikan catatan terlebih dahulu. Ia menggunakan kode Entity Framework 7 terlebih dahulu.

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

Ini kode lengkapnya:

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

Untuk .net core

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

Berikut adalah solusi terbaik untuk masalah ini: Dalam Tampilan tambahkan semua ID (Tombol). Pertimbangkan memiliki beberapa tabel bernama (Pertama, Kedua, dan Ketiga)

@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)

Dalam kode C #,

[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);
}
Kumar R
sumber
5

AttachJika suatu entitas akan mengatur status pelacakannya Unchanged. Untuk memperbarui entitas yang ada, yang perlu Anda lakukan adalah mengatur status pelacakan Modified. Menurut dokumen EF6 :

Jika Anda memiliki entitas yang Anda tahu sudah ada dalam database tetapi perubahan yang mungkin telah dibuat maka Anda dapat memberi tahu konteks untuk melampirkan entitas dan mengatur statusnya ke Dimodifikasi. Sebagai contoh:

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();
}
Bondolin
sumber
4
using(var myDb = new MyDbEntities())
{

    user user = new user();
    user.username = "me";
    user.email = "[email protected]";

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

Saya menemukan cara yang berfungsi dengan baik.

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

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

Anda harus menghapus db.Books.Attach(book);

Renat Seyfetdinov
sumber
1

Inilah metode pembaruan entitas post-RIA saya (untuk kerangka waktu Ef6):

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

Perhatikan bahwa FrameworkTypeUtility.SetProperties()ini adalah fungsi utilitas kecil yang saya tulis jauh sebelum AutoMapper di NuGet:

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);
        }
    }
}
rasx
sumber
Catatan: Hanya berfungsi jika properti Anda persis sama dengan model Anda sebagai objek ViewModel Anda yang disimpan di dalamnya.
vapcguy
1

Seperti yang Renat katakan, hapus: db.Books.Attach(book);

Juga, ubah kueri hasil Anda untuk menggunakan "AsNoTracking", karena kueri ini membuang status model kerangka kerja entitas. Ia berpikir "hasil" adalah buku untuk dilacak sekarang dan Anda tidak menginginkannya.

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

Cobalah....

UpdateModel (buku);

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;
        }
    }
}
Karan
sumber
1

Saya tahu itu sudah dijawab beberapa kali, tetapi saya suka cara ini. Saya harap ini akan membantu seseorang.

//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();
Pawel Czapski
sumber
1

Ini jika untuk Entity Framework 6.2.0.

Jika Anda memiliki DbSetitem spesifik dan yang perlu diperbarui atau dibuat:

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

Namun ini juga dapat digunakan untuk generik DbSetdengan kunci primer tunggal atau kunci primer komposit.

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