Entity Framework 5 Обновление записи


789

Я изучал различные методы редактирования/обновления записи в Entity Framework 5 в среде ASP.NET MVC3, но пока ни один из них не отметит все необходимые мне поля. Я объясню, почему.

я нашел три способа, которые я упомяну плюсы и минусы:

Метод 1 - Загрузка исходной записи, обновлять каждое свойство

var original = db.Users.Find(updatedUser.UserId); 

if (original != null) 
{ 
    original.BusinessEntityId = updatedUser.BusinessEntityId; 
    original.Email = updatedUser.Email; 
    original.EmployeeId = updatedUser.EmployeeId; 
    original.Forename = updatedUser.Forename; 
    original.Surname = updatedUser.Surname; 
    original.Telephone = updatedUser.Telephone; 
    original.Title = updatedUser.Title; 
    original.Fax = updatedUser.Fax; 
    original.ASPNetUserId = updatedUser.ASPNetUserId; 
    db.SaveChanges(); 
}  

Pros

  • Можно указать, какие свойства изменить
  • Представление не должно содержать каждый объект

Cons

  • -х запросы на базе данных, чтобы загрузить оригинал затем обновить его

Метод 2 - Загрузка исходной записи, установленные измененные значения

var original = db.Users.Find(updatedUser.UserId); 

if (original != null) 
{ 
    db.Entry(original).CurrentValues.SetValues(updatedUser); 
    db.SaveChanges(); 
} 

Pros

  • только измененные свойства передаются в базу данных

Cons

  • Просмотров должны содержать каждому объекту
  • 2 х запросов на базу данных, чтобы загрузить оригинал затем обновите его

Способ 3 - Присоединить обновленную запись и установить состояние EntityState.Модифицированный

db.Users.Attach(updatedUser); 
db.Entry(updatedUser).State = EntityState.Modified; 
db.SaveChanges(); 

Pros

  • 1 х запросов на базу данных для обновления

Против

  • Не можете указать, какие свойства изменяются
  • Просмотров должно содержать все свойства

Вопрос

Мой вопрос к вам, ребята; есть ли чистый способ, которым я могу достичь этого набора целей?

  • Можно указать, какие свойства изменяются
  • Представления не должны содержать все свойства (например, пароль!)
  • 1 х запрос на базу данных для обновления

Я понимаю, что это довольно второстепенное дело, но я могу пропустить простое решение этого. Если не метод один будет преобладать ;-)

+13

Используйте ViewModels и хорошее отображение двигатель? Вы получаете только «свойства для обновления», чтобы заполнить ваше представление (а затем обновить). Там еще будут 2 запроса для обновления (получите оригинал + обновите его), но я бы не назвал это «Кон». Если это ваша единственная проблема с производительностью, вы счастливый человек;) 11 мар. 132013-03-11 10:50:37

  0

Спасибо @ RaphaëlAlthaus, очень действительная точка. Я мог бы сделать это, но мне нужно создать CRUD-операцию для нескольких таблиц, поэтому я ищу метод, который может работать с моделью напрямую, чтобы сохранить меня, создавая n-1 ViewModel для каждой модели. 11 мар. 132013-03-11 10:56:40

+3

Ну, в моем текущем проекте (многие сущности тоже) мы начали с работы над Моделями, думая, что мы потеряем время, работая с ViewModels. Теперь мы переходим к ViewModels, и при запуске (с незначительной) инфраструктурой она намного далека, намного яснее и удобнее в обслуживании. И более безопасно (не нужно бояться злых «скрытых полей» или подобных вещей) 11 мар. 132013-03-11 10:59:51

+1

И не более (ужасный) ViewBags для заполнения DropDownLists (у нас есть хотя бы один DropDownList почти на всех наших CRU (D) представлениях ...) 11 мар. 132013-03-11 11:15:00

  0

Я думаю, что вы правы, мне плохо, потому что я пытаюсь игнорировать ViewModels. Да, ViewBag временами кажется немного грязным. Я обычно делаю еще один шаг в соответствии с [Dino Esposito's] (https://www.simple-talk.com/dotnet/asp.net/the-three-models-of-asp.net-mvc-apps/) блога и также создайте InputModels, немного пояса и брекеты, но он работает очень хорошо. Просто означает 2 дополнительных модели для каждой модели - doh ;-) 11 мар. 132013-03-11 11:28:00

  0

** Метод 1 ** имеет проблемы с ленивыми грузами и потенциально вызывает много неожиданных транзакций и снижает производительность. 21 мар. 142014-03-21 08:24:55

  0

Какой механизм отображения/шаблон вы бы рекомендовали достичь такого рода поведения? Что-то вроде automapper, похоже, было бы излишним. Я хочу, чтобы рабочий процесс сохранения/обновления EF был более тесно связан с движком моделирования mvc. Возможно, в EF 7. 10 авг. 142014-08-10 18:41:54

  0

Согласен. Если у меня есть сущность с 10 полями, я должен иметь возможность передавать идентификатор и другое поле, и только это поле обновляется, и это происходит только с одним запросом в db. 14 ноя. 142014-11-14 00:57:52

  0

Корень проблемы заключается в том, что свойства обновляемого объекта не становятся помечены как грязные, когда вы их устанавливаете, потому что они POCO. В отличие от LLBLGen, например, генерирует объекты для вас, которые автоматически устанавливают поле как грязное, когда вы устанавливаете его значение. 25 апр. 152015-04-25 16:35:00

  0

Вопрос только с вопросом, откуда вы взяли 'updatedUser'? Я пытаюсь реализовать принятый ответ на свою программу, но я не знаю, что «updateUser» is 18 авг. 162016-08-18 07:15:50

  0

'updatedUser' - это объект' User', который имеет разные данные из оригинала. В принятом ниже ответе мы говорим, что вы хотите сохранить пользователя, но только свойство электронной почты 18 авг. 162016-08-18 11:11:14

  0

Также вы можете использовать недавно выпущенную библиотеку, которая *** автоматически установит состояние всех сущностей *** в графе сущности. Вы можете прочитать [мой ответ на аналогичный вопрос] (http://stackoverflow.com/questions/5557829/update-row-if-it-exists-else-insert-logic-with-entity-framework/39609020#39609020) , 21 сен. 162016-09-21 06:45:52

634

Вы ищете:

db.Users.Attach(updatedUser); 
var entry = db.Entry(updatedUser); 
entry.Property(e => e.Email).IsModified = true; 
// other changed properties 
db.SaveChanges(); 
  0

спасибо большое, я попробовал это сразу после того, как я опубликовал вопрос на днях, и он продолжал давать мне ошибку, но сегодня он работает и именно то, что я хотел ;-) 13 мар. 132013-03-13 09:30:28

+54

привет @Ladislav Mrnka, если я хочу обновить все свойства сразу, могу ли я использовать приведенный ниже код? db.Departments.Attach (отдел); db.Entry (department) .State = EntityState.Modified; db.SaveChanges(); 21 июн. 132013-06-21 00:07:09

+21

@Foysal: Да, вы можете. 21 июн. 132013-06-21 08:37:08

+5

Одна из проблем с этим подходом заключается в том, что вы не можете издеваться над db.Entry(), что является серьезным PITA. EF имеет достаточно хорошую издевательскую историю в другом месте - это довольно раздражает то, что (насколько я могу судить) у них их нет. 29 авг. 132013-08-29 21:22:06

+2

Для меня просто 'db.Users.Attach (updatedUser); db.SaveChanges();' один делает трюк. 16 сен. 132013-09-16 09:16:20

+22

@Foysal Doing context.Entry (entity) .State = EntityState.Модифицированный сам по себе достаточно не нужно прилагать. Он будет автоматически прикреплен как измененный ... 16 дек. 132013-12-16 21:00:16

+4

@ Sandman4, это означает, что каждое другое свойство должно быть там и быть установлено на текущее значение. В некоторых проектах приложений это невозможно. 26 мар. 142014-03-26 19:41:59

  0

@LadislavMrnka Ladiislav, отличный ответ. У меня возникают проблемы с этим кодом: http://stackoverflow.com/q/23566781/1500199 09 май. 142014-05-09 14:20:52

  0

Привет @LadislavMrnka Я не могу найти метод Entry на db. Я использую DataContext. Не могли бы вы посмотреть мой пост? http://stackoverflow.com/questions/25717114/cannot-update-entity-because-cannot-find-entry-method-in-entity-framework-5 thx 08 сен. 142014-09-08 04:34:41

  0

@LadislavMrnka Что происходит в "updatedUser" 19 ноя. 142014-11-19 20:30:37

+1

как насчет ссылок? Это, похоже, не работает с навигационными свойствами. Как мы можем обновить код, чтобы контекст понял, что свойство навигации было изменено. 28 янв. 152015-01-28 15:22:37

+2

«EF имеет достаточно хорошую насмешливую историю» - Почему все в наше время должно быть историей? 24 апр. 152015-04-24 21:19:20

  0

@Franva context.Entry является новым для EF5 и выше. https://msdn.microsoft.com/en-us/library/gg696238(v=vs.103).aspx 22 май. 152015-05-22 20:41:45

  0

Как это будет работать, если между таблицей «Пользователи» было отношение «один-много», например, один пользователь мог иметь несколько ролей, удерживаемых в таблице ролей. Итак, если вы хотите обновить данные пользователя и список User.Roles, как вы можете подключить EF к дочерней таблице? 04 фев. 162016-02-04 10:48:22

  0

если у пользователя есть список объектов ролей, например Список <Role> Роли, с таким подходом работают, если: 1. Список изменен? 2. Изменен ли User.Roles [2] .PermissionList? 13 мар. 162016-03-13 16:10:47

  0

Что делать, если я обновляю 'viewModel'. Я добавил три модели (с отношениями) к 'view'. Теперь этот код будет работать «public ActionResult Edit (Model1 model1, Model2 model2, Model3 model3, CollectionCollection collection) {Database_Entity db = new Database_Entity; model1 mod1 = новая модель1(); model1.entity1 = коллекция ["htmleditorfor1"]; db.model1.Attach (model1); db.Entry (model1) .state = EntityState.Modified; db.SaveChanges();} 'И то же самое для других моделей. Если у кого-то есть лучшее решение, пожалуйста, покажите мне пример. 19 апр. 162016-04-19 05:41:25

+2

@LadislavMrnka Не могли бы вы объяснить ваш ответ? 01 июн. 162016-06-01 09:01:50

  0

Как я могу обнаружить измененное свойство? как я могу найти свойства, которые были изменены? 18 июл. 162016-07-18 10:58:47

  0

Когда я использую лямбда-выражение в 'entry.Property (e => e.Email) .IsModified = true;' Я получаю сообщение об ошибке 'Невозможно преобразовать лямбда-выражение для ввода 'string', потому что это не тип делегата'. Возможно, потому, что я использовал IUnitOfWork с 'DbEntityEntry Entry (объект объекта);' и мой класс dbcontext говорит 'public new DbEntityEntry Entry (объект объекта) {return base.Entry (entity); } '? Не совсем уверен, как правильно это сделать ... 26 авг. 162016-08-26 16:26:46

  0

@AlanSchapira Это должен быть отдельный вопрос. 08 ноя. 162016-11-08 19:14:31

  0

@surfasb Вы правы. Хотя я уже давно это решил. 09 ноя. 162016-11-09 09:50:16

  0

Я новичок в mvc. поэтому прощайте вопрос новичков, но откуда «обновленный пользователь» приходит из, пожалуйста. что я имею в виду, как вы это понимаете? 24 июн. 172017-06-24 07:02:34

  0

@ djack109 надеюсь, что вы, возможно, решили, о чем вы просили. В случае, если кто-то новый, то «updateuser» - это тот, который вы передаете из своего браузера i.e; пользовательский измененный объект. 26 дек. 172017-12-26 04:30:47


165

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

db.Users.Attach(updatedUser); 

var entry = db.Entry(updatedUser); 
entry.State = EntityState.Modified; 

entry.Property(e => e.Password).IsModified = false; 
entry.Property(e => e.SSN).IsModified = false; 

db.SaveChanges(); 

Этот пример позволяет существенно оставить свой бизнес-логику в одиночку после добавления нового поля в таблицу пользователей и ваш просмотр.

  0

По-прежнему я получаю сообщение об ошибке, если не укажу значение свойства SSN, даже если я установил IsModified в false, он все еще проверяет свойство на соответствие правилам модели. Итак, если свойство помечено как NOT NULL, оно не сработает, если я не установил значение, отличное от нуля. 29 сен. 142014-09-29 03:33:11

  0

Вы не получите сообщение об ошибке, поскольку эти поля не будут отображаться в вашей форме. Вы оставляете поля, которые вы, определенно, не будете обновлять, возьмите запись из базы данных, используя форму, переданную обратно, добавив ее и сообщите записи, что эти поля не изменяются. Проверка модели контролируется в ModelState, а не в контексте. Этот пример ссылается на существующего пользователя, следовательно, «updatedUser». Если ваш SSN является обязательным полем, он был бы там, когда он был впервые создан. 30 сен. 142014-09-30 21:12:19

+4

Если я правильно понимаю, «updatedUser» - это экземпляр объекта, уже заполненного FirstOrDefault() или аналогичного, поэтому я обновляю только те свойства, которые я изменил, и устанавливаю другие для ISModified = false. Это прекрасно работает. Но, что я пытаюсь сделать, это обновить объект, не заселяя его в первую очередь, не сделав ни одного FirstOrDefault() до обновления. Это когда я получаю сообщение об ошибке, если не укажу значение для всех требуемых полей, даже если я установил ISModified = false для этих свойств. entry.Property (e => e.columnA) .IsModified = false; Без этой строки ColumnA не удастся. 01 окт. 142014-10-01 20:53:57

  0

Что вы описываете, это создание нового объекта. Это относится только к обновлению. 02 окт. 142014-10-02 00:07:15

+1

RolandoCC, put db.Configuration.ValidateOnSaveEnabled = false; перед db.SaveChanges(); 06 ноя. 142014-11-06 12:54:32


26
foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) { 
    if (propertyInfo.GetValue(updatedUser, null) == null) 
     propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null); 
} 
db.Entry(original).CurrentValues.SetValues(updatedUser); 
db.SaveChanges(); 
  0

Это похоже на действительно приятное решение - без проблем или суеты; вам не нужно вручную указывать свойства, и он учитывает все OPs-пули - есть ли причина, по которой у нее больше нет голосов? 10 авг. 142014-08-10 19:53:08

  0

Это не так. Он имеет один из самых больших «минусов», более одного попадания в базу данных. Вам все равно придется загрузить оригинал с этим ответом. 29 авг. 142014-08-29 20:57:26

+1

@smd почему вы говорите, что он попадает в базу данных более одного раза? Я не вижу, что это происходит, если использование SetValues ​​() не имеет такого эффекта, но это не похоже, что это будет правда. 09 мар. 152015-03-09 17:52:32

  0

@parliament Я думаю, что, должно быть, я спал, когда писал это. Извиняюсь. Фактическая проблема заключается в переопределении предполагаемого нулевого значения. Если обновленный пользователь больше не ссылается на что-то, было бы неправильно заменить его исходным значением, если вы хотели его очистить. 10 мар. 152015-03-10 16:20:45


19

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

public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) 
{ 
    Context.Set<T>().Attach(obj); 

    foreach (var p in propertiesToUpdate) 
    { 
     Context.Entry(obj).Property(p).IsModified = true; 
    } 
} 

А потом позвонить, например:

public void UpdatePasswordAndEmail(long userId, string password, string email) 
{ 
    var user = new User {UserId = userId, Password = password, Email = email}; 

    Update(user, u => u.Password, u => u.Email); 

    Save(); 
} 

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

  0

Aha ... отдельный проект для просмотра моделей и отдельный проект для репозиториев, работающих с моделями просмотра. 17 май. 162016-05-17 15:57:12


1

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


6
public interface IRepository 
{ 
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class; 
} 

public class Repository : DbContext, IRepository 
{ 
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class 
    { 
     Set<T>().Attach(obj); 
     propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true); 
     SaveChanges(); 
    } 
} 

0

В зависимости от вашего варианта использования применяются все вышеперечисленные решения. Так я обычно это делаю:

Для кода на стороне сервера (например, пакетный процесс) я обычно загружаю сущности и работаю с динамическими прокси. Обычно в пакетных процессах необходимо загружать данные в любое время в момент запуска службы. Я пытаюсь загрузить пакет данных вместо использования метода find, чтобы сэкономить некоторое время. В зависимости от процесса я использую оптимистический или пессимистический контроль параллелизма (я всегда пользуюсь оптимизмом, за исключением сценариев параллельного выполнения, когда мне нужно блокировать некоторые записи с помощью простых операторов sql, это редко бывает). В зависимости от кода и сценария воздействие может быть уменьшено почти до нуля.

Для стороны клиента сценариев, у вас есть несколько вариантов

  1. Используйте вид модели. Модели должны иметь свойство UpdateStatus (немодифицированный-вставленный-обновленный-удаленный). Клиент обязан установить правильное значение этого столбца в зависимости от действий пользователя (insert-update-delete). Сервер может либо запросить db для исходных значений, либо клиент должен отправить исходные значения на сервер вместе с измененными строками. Сервер должен прикрепить исходные значения и использовать столбец UpdateStatus для каждой строки, чтобы решить, как обрабатывать новые значения. В этом сценарии я всегда использую оптимистический параллелизм. Это будет делать только инструкции insert-update-delete, а не какие-либо выборки, но может понадобиться какой-нибудь умный код для просмотра графика и обновления сущностей (зависит от вашего сценария-приложения). Картограф может помочь, но не обрабатывает логику CRUD.

  2. Используйте библиотеку, такую ​​как breeze.js, которая скрывает большую часть этой сложности (как описано в 1) и попытается поместить ее в ваш прецедент.

Надеется, что это помогает