Сначала обновите одно свойство записи в коде Entity Framework

#entity-framework #validation #entity-framework-4.1

#entity-framework #проверка #entity-framework-4.1

Вопрос:

Как я могу обновить одно свойство записи, не извлекая его сначала? Я спрашиваю в контексте 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();
}
  

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

Ответ №1:

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

 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 , поэтому вы не можете сказать, какие объекты должны быть проверены, а какие нет.

Комментарии:

1. Я искал способ отключить проверку при сохранении, но не смог. Спасибо за решение.

2. Эта проблема, кажется , исправлена в EF 5.

3. Я хотел бы отметить, что нечто, называемое проверкой параллелизма, может вызвать исключение «обновлено 0 строк», поскольку при этом будет добавлена проверка в предложении where на соответствие столбцу rowversion. (которое в моем случае было null, потому что это была заглушка)

4. Можем ли мы сделать то же самое, но использовать другое поле, отличное от ‘Id’? например, User u = new User {Email = "abcd@123.com", LastActivity = DateTime.Now }; где Email == «abcd@123.com » может однозначно идентифицировать объект.

5. Как это можно использовать для обновления свойства навигации?

Ответ №2:

На самом деле я занимаюсь этим прямо сейчас. Что я решил сделать, так это переопределить метод ValidateEntity в контексте DB.

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

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

Комментарии:

1. Это не полный код, но мне нравится идея. Основная проблема с кодом заключается в том, что вы попадете в ситуацию, когда свойство, которое вы пытаетесь увидеть, которое изменено, не является примитивным или сложным типом, и, таким образом, произойдет сбой. У вас должно быть условие для ссылки или коллекции.

Ответ №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));
  

Комментарии:

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