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

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

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

Ответ 1

Вы ищете:

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

Ответ 2

Мне очень нравится принятый ответ. Я считаю, что есть еще один способ приблизиться к этому. Скажем, у вас есть очень короткий список свойств, которые вы бы никогда не хотели включать в представление, поэтому при обновлении объекта они будут опущены. Скажем, что эти два поля - Пароль и 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();   

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

Ответ 3

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();

Ответ 4

Я добавил дополнительный метод обновления в базовый класс моего репозитория, который похож на метод обновления, созданный 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();
}

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

Ответ 5

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();
    }
}

Ответ 6

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

Ответ 7

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

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

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

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

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

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