Когда я нахожусь в отдельном сценарии и получаю dto от клиента, который я сопоставляю с объектом, чтобы сохранить его, я делаю следующее:
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
Для чего тогда DbSet.Attach(entity)
или зачем мне использовать метод .Attach, когда EntityState.Modified уже прикрепляет объект?
c#
entity-framework
entity-framework-6
Элизабет
источник
источник
Ответы:
Когда вы это делаете
context.Entry(entity).State = EntityState.Modified;
, вы не только прикрепляете объект к объектуDbContext
, но и отмечаете его как грязный. Это означает, что когда вы это сделаетеcontext.SaveChanges()
, EF сгенерирует оператор обновления, который обновит все поля сущности.Это не всегда желательно.
С другой стороны,
DbSet.Attach(entity)
присоединяет объект к контексту, не отмечая его как грязный. Это эквивалентно выполнениюcontext.Entry(entity).State = EntityState.Unchanged;
При подключении таким образом, если вы не приступите к обновлению свойства объекта, при следующем вызове
context.SaveChanges()
EF не будет создавать обновление базы данных для этого объекта.Даже если вы планируете обновить объект, если объект имеет много свойств (столбцы db), но вы хотите обновить только несколько, вам может показаться выгодным сделать
DbSet.Attach(entity)
, а затем обновить только несколько свойств которые нуждаются в обновлении. Таким образом будет сгенерирован более эффективный оператор обновления из EF. EF обновит только те свойства, которые вы изменили (в отличие отcontext.Entry(entity).State = EntityState.Modified;
этого, все свойства / столбцы будут обновлены)Соответствующая документация: Add / Attach и Entity States .
Пример кода
Допустим, у вас есть следующая сущность:
Если ваш код выглядит так:
Сгенерированный SQL будет выглядеть примерно так:
Обратите внимание, как приведенный выше оператор обновления обновит все столбцы, независимо от того, действительно ли вы изменили значения или нет.
Напротив, если в вашем коде используется «нормальное» присоединение, например:
Тогда сгенерированный оператор обновления будет другим:
Как видите, оператор обновления обновляет только те значения, которые были фактически изменены после того, как вы прикрепили сущность к контексту. В зависимости от структуры вашей таблицы это может положительно сказаться на производительности.
Теперь, какой вариант лучше для вас, полностью зависит от того, что вы пытаетесь сделать.
источник
WHERE
предложением, содержащим только первичный ключ, и без какой-либо проверки параллелизма. Для проверки параллелизма мне нужно явно настроить столбец как токен параллелизма или rowVersion. В этом случаеWHERE
предложение будет иметь только первичный ключ и столбец токена параллелизма, а не все поля. Если ваши тесты покажут обратное, я хотел бы услышать об этом.DbContext.Entry(person).CurrentValues
иDbContext.Entry(person).OriginalValues
.Когда вы используете этот
DbSet.Update
метод, Entity Framework помечает все свойства вашей сущности какEntityState.Modified
и отслеживает их. Если вы хотите изменить только некоторые из ваших свойств, а не все, используйтеDbSet.Attach
. Этот метод создает все ваши свойстваEntityState.Unchanged
, поэтому вы должны сделать свои свойства, которые хотите обновитьEntityState.Modified
. Таким образом, когда приложение попадает вDbContext.SaveChanges
, оно будет работать только с измененными свойствами.источник
Просто в дополнение (к отмеченному ответу) есть важное различие между
context.Entry(entity).State = EntityState.Unchanged
иcontext.Attach(entity)
(в EF Core):Я провел несколько тестов, чтобы лучше понять это сам (поэтому это также включает некоторые общие эталонные тесты), так что это мой тестовый сценарий:
QueryTrackingBehavior.NoTracking
Это модели:
Это (исходные) тестовые данные в базе данных:
Чтобы получить заказ:
Теперь тесты:
Простое обновление с EntityState :
Простое обновление с прикреплением :
Обновление с изменением дочерних идентификаторов с помощью EntityState :
Обновление с изменением дочерних идентификаторов с прикреплением :
Примечание. Это вызывает исключение, независимо от того, был ли Id изменен или установлен на исходное значение, похоже, что состояние Id установлено на «изменено», и это недопустимо (потому что это первичный ключ)
Обновление с изменением дочерних идентификаторов как новых (без разницы между EntityState и Attach):
Примечание. Посмотрите разницу в обновлении с EntityState без new (см. Выше). На этот раз имя будет обновлено из-за нового экземпляра пользователя.
Обновление с изменением идентификаторов ссылок с помощью EntityState :
Обновление с изменением идентификаторов ссылок с прикреплением :
Примечание: ссылка будет изменена на пользователя 3, но также будет обновлен пользователь 1, я думаю, это потому, что
order.OrderedByUser.Id
не изменился (он все еще 1).Заключение С EntityState у вас больше контроля, но вы должны обновлять подсвойства (второй уровень) самостоятельно. С помощью Attach вы можете обновлять все (я думаю, со всеми уровнями свойств), но вы должны следить за ссылками. Например: если User (OrderedByUser) будет dropDown, изменение значения с помощью dropDown может перезаписать весь объект User. В этом случае исходное значение dropDown-Value будет перезаписано вместо ссылки.
Для меня лучший случай - установить такие объекты, как OrderedByUser, в значение null и установить только order.OrderedByUserId на новое значение, если я хочу изменить только ссылку (независимо от того, EntityState или Attach).
Надеюсь, это поможет, я знаю, что текста много: D
источник