Обновить одно свойство записи в Entity Framework Code First

17

Как я могу обновить одно свойство записи, не извлекая его первым? Я спрашиваю в контексте EF Code First 4.1

Говорит, что у меня есть класс User, сопоставление с таблицей Пользователи в базе данных:

class User
{
    public int Id {get;set;}
    [Required]
    public string Name {get;set;}
    public DateTime LastActivity {get;set;}
    ...
}

Теперь я хочу обновить LastActivity пользователя. У меня есть идентификатор пользователя. Я легко могу сделать это, запросив запись пользователя, установить новое значение в LastActivity, а затем вызвать SaveChanges (). Но это приведет к избыточному запросу.

Я работаю с помощью метода Attach. Но поскольку EF генерирует исключение проверки для Name, если оно равно null, я устанавливаю Name в случайную строку (не будет обновляться до DB). Но это не выглядит элегантным решением:

using (var entities = new MyEntities())
{
    User u = new User {Id = id, Name="this wont be updated" };
    entities.Users.Attach(u);
    u.LastActivity = DateTime.Now;
    entities.SaveChanges();
}

Я был бы очень appriciate, если кто-то может предоставить мне лучшее решение. И простите меня за любую ошибку, поскольку это первый раз, когда я задал вопрос о SO.

    
задан Quang Vo 06.04.2011 в 12:15
источник

3 ответа

36

Это проблема реализации валидации. Валидация способна проверять только целое сущность. Он не проверяет только измененные свойства, как ожидалось. Из-за этого проверка должна быть отключена в сценариях, где вы хотите использовать неполные фиктивные объекты:

using (var entities = new MyEntities())
{
    entities.Configuration.ValidateOnSaveEnabled = false;

    User u = new User {Id = id, LastActivity = DateTime.Now };
    entities.Users.Attach(u);
    entities.Entry(user).Property(u => u.LastActivity).IsModified = true;
    entities.SaveChanges();
}

Это, очевидно, проблема, если вы хотите использовать тот же контекст для обновления фиктивных объектов и для обновления целых объектов, где должна использоваться валидация. Проверка выполняется в SaveChanges , поэтому вы не можете сказать, какие объекты должны быть проверены, а какие нет.

    
ответ дан Ladislav Mrnka 06.04.2011 в 23:22
источник
  • Я искал способ отключить проверку при сохранении, но не смог. Спасибо за решение. –  Quang Vo 07.04.2011 в 06:07
  • Эта проблема, по-видимому, исправлена ​​в EF 5. –  Peter Smith 29.09.2013 в 09:31
  • Я хотел бы указать, что что-то, называемое проверкой параллелизма, может вызвать исключение «0 строк обновлено», поскольку оно будет добавлять проверку в аргументе where, чтобы он соответствовал столбцу rowversion. (что в моем случае было нулевым, потому что это был заглушка) –  Slight 20.04.2015 в 19:43
  • Можем ли мы сделать то же самое, но использовать другое поле, отличное от «Id»? например Пользователь u = новый пользователь {Email="[email protected]", LastActivity = DateTime.Now}; где Email == "[email protected]" может однозначно идентифицировать объект. –  dan 10.11.2015 в 10:42
  • Как это будет использоваться для обновления свойства навигации? –  MikeW 22.06.2016 в 10:52
4

Я сейчас занимаюсь этим прямо сейчас. То, что я решил сделать, это переопределить метод ValidateEntity в контексте БД.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    var result = base.ValidateEntity(entityEntry, items);

    var errors = new List<DbValidationError>();

    foreach (var error in result.ValidationErrors)
    {
        if (entityEntry.Property(error.PropertyName).IsModified)
        {
            errors.Add(error);
        }
    }

    return new DbEntityValidationResult(entityEntry, errors);
} 

Я уверен, что в нем есть дыры, но это кажется лучше, чем альтернативы.

    
ответ дан zeonic 18.07.2013 в 19:44
источник
  • Это не полный код, но мне нравится эта идея. Основная проблема с кодом заключается в том, что вы попадете в ситуацию, когда свойство, которое вы пытаетесь увидеть, модифицирует его не примитивным или сложным типом, и таким образом произойдет сбой. У вас должно быть условие для ссылки или коллекции. –  Patrick Desjardins 13.09.2015 в 20:52
3

Вы можете попробовать своего рода взломать:
context.Database.ExecuteSqlCommand("update [dbo].[Users] set [LastActivity] = @p1 where [Id] = @p2",
new System.Data.SqlClient.SqlParameter("p1", DateTime.Now),
new System.Data.SqlClient.SqlParameter("p2", id));

    
ответ дан Devart 06.04.2011 в 13:16
источник
  • Это не взломать. Однако, к сожалению, прямые обновления одного столбца могут быть выполнены только со всей сущностью в руке и с помощью необработанной строки SQL, подобной этой. –  Nicholas Petersen 30.09.2016 в 19:10