2013-03-11 8 views
789

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

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

Метод 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 запроса для обновления (получите оригинал + обновите его), но я бы не назвал это «Кон». Если это ваша единственная проблема с производительностью, вы счастливый человек;) –

+0

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

+3

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

ответ

634

Вы ищете:

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

спасибо большое, я попробовал это сразу после того, как я опубликовал вопрос на днях, и он продолжал давать мне ошибку, но сегодня он работает и именно то, что я хотел ;-) – Stokedout

+54

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

+21

@Foysal: Да, вы можете. –

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, оно не сработает, если я не установил значение, отличное от нуля. – RolandoCC

+0

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

+4

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

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-пули - есть ли причина, по которой у нее больше нет голосов? – nocarrier

+0

Это не так. Он имеет один из самых больших «минусов», более одного попадания в базу данных. Вам все равно придется загрузить оригинал с этим ответом. – smd

+1

@smd почему вы говорите, что он попадает в базу данных более одного раза? Я не вижу, что это происходит, если использование SetValues ​​() не имеет такого эффекта, но это не похоже, что это будет правда. – parliament

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 ... отдельный проект для просмотра моделей и отдельный проект для репозиториев, работающих с моделями просмотра. –

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) и попытается поместить ее в ваш прецедент.

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

Смежные вопросы