Объект не обновляется с использованием подхода Code-First

У меня есть этот класс, который я использую для операций с БД:

public class EntityService<TEntity> : IRepository<TEntity> where TEntity : BaseModel
{

     ApplicationDbContext _context;
     private DbSet<TEntity> _entities;

     public EntityService()
     {
         _context = new ApplicationDbContext();
     }

     public virtual void Update(TEntity entity)
     {
          if (entity == null)
               throw new ArgumentNullException(nameof(entity));

          try
          {
                var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

                dbEnt = entity;
                dbEnt.UpdatedBy = GetCurrentUser();
                dbEnt.DateUpdated = DateTime.Now;
                _context.SaveChanges();
           }
           catch (DbUpdateException exception)
           {
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
           }

           //-----other methods for insert and get working fine----
}

В этом классе есть и другие методы для insert и get, которые работают нормально. Только этот метод обновления не обновляет сущность и не генерирует исключение.

ОБНОВИТЬ

Я столкнулся с аналогичной проблемой, но с противоположным функционированием здесь: Метод Add(), добавляющий повторяющиеся строки для связанных моделей в Code-First Entity Framework

Я думаю, что у этих двоих одна и та же причина отслеживания изменений. Но один добавляет другой не обновляет.

если вы хотите обновить entity, почему вы снова получаете его и ставите entity в dbEnt?

hassan.ef 25.05.2019 09:20

@ hassan.ef Это не сработало, когда я это сделал. Поэтому я подумал, что если я снова получу объект базы данных из базы данных, то _context будет иметь ссылку на базу данных. Потому что entity — это просто параметр метода.

Aishwarya Shiva 25.05.2019 09:22

Я думаю, что сначала вы должны обновить параметр dbEnt параметром entity следующим образом: например: edbEnt.Name = entity.Name и не использовать dbEnt = entity.

hassan.ef 25.05.2019 09:26

@hassan.ef Это распространенный метод обновления всех объектов. Я не могу явно назначить здесь отдельное свойство.

Aishwarya Shiva 25.05.2019 09:29

Перед сохранением вы можете попробовать вызвать context.Entry(entity).State=EntityState.Modified;.

bolkay 25.05.2019 09:43

@bolkay Я пробовал, но мне показывает эту ошибку Attaching an entity of type '<type>' failed because another entity of the same type already has the same primary key value.This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. `

Aishwarya Shiva 25.05.2019 13:51

@bolkay продолжение комментария выше This may be because some entities are new and have not yet received database-generated key values.In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.'

Aishwarya Shiva 25.05.2019 13:52

@bolkay Как использовать решение, предложенное ошибкой?

Aishwarya Shiva 25.05.2019 13:53

Это проблема отслеживания. Попробуйте что-то вроде: _context.Set<TEntity>().Add(dbEnt); Тогда : _context.Entry(dbEnt).State=EntityState.Modified; Тогда SaveChanges(); Вы можете прочитать о различных состояниях сущности.

bolkay 25.05.2019 14:21

@bolkay После этого появляется эта ошибка Saving or accepting changes failed because more than one entity of type '<type>' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.

Aishwarya Shiva 25.05.2019 14:42

Ваш опубликованный код в порядке, за исключением dbEnt = entity;. Вместо этого скопируйте измененные поля в dbEnt.Prop1 = entity.Prop1; и т. д. Мы используем Automapper, чтобы сделать все поля за 1 шаг: Mapper.Map(entity, dbEnt);

Steve Greene 25.05.2019 16:38

Вы начали вознаграждение, потому что вопрос не получил достаточного внимания. Но что не ясно/недостаточно в моем ответе? Кажется, я точно описал причину неудачного обновления. И предложил рабочие альтернативы. Вам было бы намного дешевле ответить на мой ответ, если бы он был недостаточно ясен.

Gert Arnold 03.06.2019 17:01

@GertArnold Я уже пробовал ваш ответ, как упоминал в предыдущих комментариях. Болкай предложил мне это. Я пробовал всеми способами прикрепить сущность к трекеру изменений, но это не работает. Даже я попытался снова инициализировать объект ApplicationContext в этом методе. Но я все еще пробую другие способы и ищу еще предложения, чтобы получить подсказку. Вот почему я начал щедрость. Я вернусь к вашему ответу, как только перепробую все, что смогу найти в Интернете.

Aishwarya Shiva 03.06.2019 18:00

Непонятно, какой точно вы пробовали, потому что это всего лишь фрагмент в комментарии. Но что бы это ни было, нет смысла использовать Add() для сущности, которую вы только что извлекли из базы данных и хотите только обновить. Он уже привязан к контексту! Вам нужно только обновить его значения и сохранить изменения. В этом суть моего второго предложения.

Gert Arnold 03.06.2019 21:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
14
392
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Линия...

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

... присоединяет объект сущности к контексту и возвращает ссылку на эту сущность.

Потом очередь...

dbEnt = entity;

... заменяет эту ссылку ссылкой на переменную entity, которая входит в метод. Это не отслеживаемый объект сущности. Вы фактически потеряли ссылку на отслеживаемый объект, и изменить ее больше невозможно.

Вы должны либо прикрепить entity к контексту и пометить его как измененный, либо получить dbEnt, как вы уже делаете, и изменить и сохранить объект это. Оба метода имеют свои плюсы и минусы, см. здесь.

После получения сущности из _context обновите все поля из новой детали и установите состояние сущности как измененное.

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();
dbEnt.Name = entity.Name;
...
...
...
dbEnt.UpdatedBy = GetCurrentUser();
dbEnt.DateUpdated = DateTime.Now;
_context.Entry(dbEnt).State = EntityState.Modified;
_context.SaveChanges();

Ставить State = EntityState.Modified не обязательно, я бы даже сказал не рекомендуется.

Gert Arnold 05.06.2019 21:24

Если вы нашли свою сущность по идентификатору

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

тогда зачем эта строка?

dbEnt = entity;

Удалите указанную выше строку, так как она удалит ссылку на отслеживаемый объект.

Спасибо всем. Я получил много подсказок из ваших ответов. Как @GertArnold и Ответ @Colonel Software намекнул мне, что я изменил свой код следующим образом, и это сработало:

//Assigning BaseModel properties
entity.CreatedBy = dbEnt.CreatedBy;
entity.UpdatedBy = GetCurrentUser();
entity.DateUpdated = DateTime.Now;
entity.DateCreated = dbEnt.DateCreated;

//Changing entity states
_context.Entry(dbEnt).State = EntityState.Detached;
_context.Entry(entity).State = EntityState.Modified;

_context.SaveChanges();

Почему бы просто не изменить и не сохранить dbEnt, как это происходит в ответе, на который вы ссылаетесь?

Gert Arnold 05.06.2019 21:30

@GertArnold Пробовал это, но он показывает исключение повторяющихся значений первичного ключа. Я попытаюсь решить это позже, но пока это работает, и мне нужно скоро доставить проект, поэтому я должен пойти на это решение. Спасибо за вашу помощь, я многому научился из вашего ответа и +1 к нему.

Aishwarya Shiva 05.06.2019 21:57

Понижение за что? Это решает мою проблему. Может быть, это не лучшее решение, но это решение.

Aishwarya Shiva 05.06.2019 21:59

Тогда вы сделали больше, чем показываете. Как сказано в другом ответе (и моем), все, что вам нужно, это удалить dbEnt = entity;.

Gert Arnold 05.06.2019 21:59

Понижение связано с тем, что ваш код едва соответствует бывает, работает. Использование AddOrUpdate здесь довольно странно.

Gert Arnold 05.06.2019 22:02

@GertArnold Решение представляло собой смесь всех ответов. Было две проблемы. Одно упомянуло вами, что это произошло из-за того, что отслеживание было потеряно, а другое упоминание Colonel Software намекнуло, что мне нужно назначить все свойства базовой модели.

Aishwarya Shiva 05.06.2019 22:05

@GertArnold в любом случае я отмечаю твой ответ вместо своего. Пожалуйста, отзовите отрицательный голос, потому что это одно из решений.

Aishwarya Shiva 05.06.2019 22:06

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

Gert Arnold 05.06.2019 22:10

@GertArnold Хорошо, не могли бы вы объяснить мне или дать ссылку, где я могу найти его недостатки? И не могли бы вы изменить мой код и обновить свой ответ?

Aishwarya Shiva 05.06.2019 22:22

@GertArnold Каким-то образом я смог сделать это без AddOrUpdate. Пожалуйста, проверьте обновление моего ответа и отзовите отрицательный голос, если все в порядке.

Aishwarya Shiva 05.06.2019 22:48

Удалите dbEnt = entity; и измените свойства dbEnt!

Gert Arnold 05.06.2019 22:59

Другие вопросы по теме