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

795

Я изучал различные методы редактирования/обновления записи в 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();
}    

Доводы

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

против

  • 2 x запроса в базе данных для загрузки оригинала, затем обновление

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

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

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

Доводы

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

против

  • В представлениях должно быть указано каждое свойство
  • 2 x запроса в базе данных для загрузки оригинала, затем обновление

Метод 3 - Прикрепите обновленную запись и установите состояние в EntityState.Modified

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

Доводы

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

против

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

Вопрос

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

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

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

  • 13
    Использовать ViewModels и хороший движок картирования? Вы получаете только «свойства для обновления», чтобы заполнить ваше представление (а затем обновить). Там все еще будет 2 запроса на обновление (получить оригинал + обновить его), но я бы не назвал это «Con». Если это ваша единственная проблема с производительностью, вы счастливый человек;)
  • 0
    Спасибо @ RaphaëlAlthaus, очень верный момент. Я мог бы сделать это, но мне нужно создать операцию CRUD для нескольких таблиц, поэтому я ищу метод, который может работать с моделью напрямую, чтобы сохранить мое создание n-1 ViewModel для каждой модели.
Показать ещё 10 комментариев
Теги:
asp.net-mvc-3
entity-framework-5

7 ответов

647
Лучший ответ

Вы ищете:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
  • 0
    Большое спасибо, я попробовал это сразу после того, как опубликовал вопрос на днях, и он продолжал давать мне ошибку, но сегодня это работает и именно то, что я хотел ;-)
  • 55
    привет @Ladislav Mrnka, если я хочу обновить все свойства сразу, могу ли я использовать приведенный ниже код? db.Departments.Attach (департамент); db.Entry (отдел) .State = EntityState.Modified; db.SaveChanges ();
Показать ещё 22 комментария
161

Мне очень нравится принятый ответ. Я считаю, что есть еще один способ приблизиться к этому. Скажем, у вас есть очень короткий список свойств, которые вы бы никогда не хотели включать в представление, поэтому при обновлении объекта они будут опущены. Скажем, что эти два поля - Пароль и 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, оно завершится ошибкой, если я не установлю любое значение, отличное от нуля.
  • 0
    Вы не получите сообщение об ошибке, потому что эти поля не будут в вашей форме. Вы пропускаете поля, которые вы точно не будете обновлять, извлекаете запись из базы данных, используя форму, переданную обратно, прикрепляя ее, и сообщаете записи, что эти поля не изменяются. Проверка модели контролируется в ModelState, а не в контексте. Этот пример ссылается на существующего пользователя, следовательно, "updatedUser". Если ваш SSN является обязательным полем, он был бы там при первом создании.
Показать ещё 3 комментария
25
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
    Это кажется действительно хорошим решением - без суеты и суеты; Вам не нужно указывать свойства вручную, и он учитывает все маркеры ОП - есть ли какая-то причина, по которой у него нет больше голосов?
  • 0
    Это не так, хотя. У него один из самых больших «минусов», более одного попадания в базу данных. Вам все равно придется загрузить оригинал с этим ответом.
Показать ещё 2 комментария
19

Я добавил дополнительный метод обновления в базовый класс моего репозитория, который похож на метод обновления, созданный Scaffolding. Вместо того, чтобы установить весь объект на "измененный", он устанавливает набор индивидуальных свойств. (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
    Ага ... отдельный проект для моделей представления и отдельный проект для репозиториев, которые работают с моделями представления.
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
    Почему бы просто DbContext.Attach (obj); DbContext.Entry (obj) .State = EntityState.Modified;
1

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

0

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню