Как обновить запись с помощью Entity Framework 6?


121

Я пытаюсь обновить запись, используя EF6. Сначала найдите запись, если она существует, обновите ее. Вот мой код: -

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

Everytime я пытаюсь обновить запись, используя выше код, я получаю эту ошибку: -

{System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entrie

+2

Сторона примечания: 'catch (Exception ex) {throw;}' является избыточным, и вы можете его полностью удалить. 17 сен. 142014-09-17 15:30:28

  0

try catch block - это просто выяснить причину сбоя. Но все-таки не понял, почему этот код терпит неудачу? 17 сен. 142014-09-17 15:33:25

+2

Я не эксперт в этой теме, я не могу ответить на этот вопрос. но без try catch вы также можете использовать [break when exception is throw feature] (http://msdn.microsoft.com/en-us/library/d14azbfh.aspx), чтобы разбить отладчик, когда есть исключение. 17 сен. 142014-09-17 15:35:59

  0

Если вы ищете веб-сайт или этот сайт для этой ошибки, вы получите много советов. Какие из них вы нашли и что вы пробовали? Как выглядит ваш стол? 17 сен. 142014-09-17 15:38:36

  0

@CodeCaster: у моей таблицы нет ограничений NOT NULL (кроме первичного ключа, для которого установлено значение auto incremental). Также проверяется в профайле sql. Если запустить такой же запрос вручную, он работает. 17 сен. 142014-09-17 15:51:24

  0

Мой тип модели - Database-First. 17 сен. 142014-09-17 15:53:13

+1

Вы ничего не изменили. Игра с состоянием Entity не изменит тот факт, что объект фактически не был изменен. 17 сен. 142014-09-17 19:14:48

+1

Ну, я сделал то же самое, что и вы, и не получил ошибку. В исключении указано исключение DbUpdateConcurrencyException. Как вы справлялись с параллелизмом? Вы использовали временную метку, вы клонировали, а затем снова объединили объекты или использовали объекты самоконтроля? (3 наиболее используемых подхода). Если вы не справились с параллелизмом, я думаю, это проблема. 13 дек. 162016-12-13 18:47:19

183

Вы пытаетесь обновить запись (которая мне означает «изменить значение на существующую запись и сохранить ее обратно»). Поэтому вам нужно восстановить объект, внести изменения и сохранить его.

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

W, как это точно работает? как оценивать значение «результат», фактически обновляет базу данных? 03 мар. 152015-03-03 22:29:33

+4

Присвоение значения не обновляет базу данных, вызывая 'db.SaveChanges()' с измененными объектами в контексте, обновляет базу данных. 03 мар. 152015-03-03 23:06:25

+3

По-прежнему он меня завораживает ... так что результат var, фактически подключается к dbcontext ... поэтому это означает, что любая переменная, созданная любыми членами dbcontext, фактически будет иметь этот ассоциат в базе данных, чтобы любые изменения были применены к этому переменная, она также применяется или сохраняется? 04 мар. 152015-03-04 14:30:14

+3

Поскольку контекст, сгенерированный объектом, контекст может отслеживать объект, включая изменения объекта. Когда вы вызываете 'SaveChanges', контекст оценивает все объекты, которые он отслеживает, чтобы определить, были ли они добавлены, изменены или удалены и выдает соответствующий SQL для подключенной базы данных. 04 мар. 152015-03-04 18:07:05

+2

iam сталкивается с той же проблемой - с использованием EF6, пытаясь обновить объект. Attach + EntityState.Modified не работает. Работает только то, что вам нужно восстановить объект, внести необходимые изменения и сохранить его через db.SaveChanges(); 10 июн. 152015-06-10 10:06:47

+2

Этот вопрос такой же? , 'var result = db.Books.Where (b => b.BookNumber == bookNumber) .SingleOrDefault();' и есть ли [производительность] (http://stackoverflow.com/a/1745709/2218697) между 'FirstOrDefault' и' SingleOrDefault'? 08 окт. 162016-10-08 09:38:53

+2

@stom: Да, запрос тот же в том смысле, что он вернет тот же результат. Вы должны проверить его, чтобы увидеть, есть ли разница в производительности в вашей конкретной ситуации. 17 окт. 162016-10-17 14:27:02

  0

Вам не нужно сначала извлекать объект, чтобы его обновить. У меня была такая же проблема, пока я не понял, что пытался изменить одно из значений первичного ключа (составной ключ). Пока вы предоставляете правильный первичный ключ, вы можете настроить EntityState на Modified и SaveChanges(), если вы не нарушите другое ограничение целостности, определенное в таблице. 22 окт. 172017-10-22 12:50:47


1

Вот мой пост-RIA метод сущность-обновление (за временные рамки 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(); 
} 

Обратите внимание, что FrameworkTypeUtility.SetProperties() крошечная функция полезности я писал задолго до того AutoMapper на 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); 
     } 
    } 
} 

6

Этот код является результатом теста для обновления только набора столбцов без запроса запроса для возврата записи в первую очередь. Сначала он использует код EF 7.

//The function receives an object type to receive a custom object can be a view model or an anonymous object wit the properties you will like 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) 
       { 
//Here is checking if the property is named Id dont add it in the update. It can be refactored to look in the annotations to find a key or to fin in the name 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 instance of T type object: 
      object tmp = Activator.CreateInstance(Type.GetType(type.ToString())); 

      //loop through the properties of the object you want to covert:   
      foreach (PropertyInfo pi in obj.GetType().GetProperties()) 
      { 
       try 
       { 
        //get the value of 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 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 Cambiar properties a 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 instance of T type object: 
      object tmp = Activator.CreateInstance(Type.GetType(type.ToString())); 

      //loop through the properties of the object you want to covert:   
      foreach (PropertyInfo pi in obj.GetType().GetProperties()) 
      { 
       try 
       { 
        //get the value of 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); 
     } 
    } 

55

я просматривал исходный код Entity Framework и нашел способ действительно обновить объект, если вы знаете ключевое свойство в другом случае вам нужно для проверки реализации AddOrUpdate:

public void Update(T item) 
{ 
    var entity = _collection.Find(item.Id); 
    if (entity == null) 
    { 
     return; 
    } 

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

Надеюсь, что эта помощь!

+3

Ницца! Нет необходимости перечислять все свойства. Я предполагаю, что вызов 'SaveChanges()' 'требуется после установки значений. 16 окт. 162016-10-16 19:57:57

+1

Да, изменения будут сохраняться в SaveChanges() 17 окт. 162016-10-17 20:23:38

+1

Отличный ответ, было не слишком ясно, что IntelliSense что-то подобное не будет работать: _context.MyObj = newObj; затем SaveChanges() или .... _context.MyObj.Обновить (newObj), затем SaveChanges(); Ваше решение обновляет весь объект без необходимости перебирать все свойства. 13 окт. 172017-10-13 04:17:38

+2

Это жалуется мне, что я пытаюсь изменить поле ID 09 ноя. 172017-11-09 20:57:42


2

Вы должны удалить db.Books.Attach(book);


29

Вы можете использовать метод AddOrUpdate:

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

ИМО лучшее решение 30 ноя. 162016-11-30 14:16:00

  0

спас меня часы !!! 11 янв. 172017-01-11 13:25:26

+40

'.AddOrUpdate()' используется во время миграции базы данных, крайне не рекомендуется использовать этот метод вне миграций, поэтому он находится в пространстве имен Entity.Migrations. 05 апр. 172017-04-05 01:22:59

  0

Я сделал обратную разработку этого кода AddOrUpdate, и результаты - это мой другой ответ. https://stackoverflow.com/questions/25894587/how-to-update-record-using-entity-framework-6/36684660#36684660 22 авг. 172017-08-22 20:43:50

  0

Как @AdamVincent сказал, что метод AddOrUpdate() предназначен для миграции, и это не подходит для ситуации, когда вам нужно только обновить существующую строку. В случае, если у вас нет книги с поисковой ссылкой (т.е. идентификатор), она создаст новую строку, и это может быть проблемой в случаях (например, у вас есть API, который должен вернуть вам ответ 404-NotFound, если вы попробуйте вызвать метод PUT для несуществующей строки). 18 окт. 172017-10-18 13:12:57

  0

Лучшее решение спасибо! 06 дек. 172017-12-06 14:29:17


2

Вот лучшее решение этой проблемы: В видовом добавить все ID (Ключи). Рассмотрите наличие нескольких таблиц с именем (первый, второй и третий)

@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# код,

[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

Таким образом, вы имеете объект, который обновляется, и вы хотите обновить его в базу данных с наименьшее количество кода ... Параллельность всегда сложна, но я предполагаю, что вы просто хотите, чтобы ваши обновления выиграли. Вот как я сделал это для моего же случая и изменил имена, чтобы имитировать ваши классы. IOW, просто изменить присоединять добавить, и это работает для меня:

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

0

Как Ренат сказал, удалить: db.Books.Attach(book);

Кроме того, изменить свой результат запроса использовать «AsNoTracking», так как этот запрос сбросив модель состояния модели. Он думает, что «результат» - это книга для отслеживания сейчас, и вы этого не хотите.

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

0

Вы должны использовать. Entry(), если вы хотите обновить все поля вашего объекта. Также имейте в виду, что вы не можете изменить идентификатор поля (key), поэтому сначала задайте значение Id, когда вы редактируете.

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

Вы должны хотя бы попытаться ответить на вопрос, а не просто опубликовать код 17 ноя. 172017-11-17 00:21:51

  0

. Пожалуйста, внесите некоторое объяснение в вопрос, а не просто оставите фрагмент кода, чтобы лучше ответить на вопрос. 17 ноя. 172017-11-17 04:55:09


-2

попробовать ....

UpdateModel (книга);

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