在Entity Framework Code First中更新记录的单个属性

时间:2011-04-06 10:15:23

标签: entity-framework validation entity-framework-4.1

如何在不先检索记录的情况下更新记录的单个属性? 我在 EF Code First 4.1

的背景下提问

说我有一个类User,映射到数据库中的表Users:

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问题提出问题。

3 个答案:

答案 0 :(得分: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,因此您无法确定哪些对象应该验证,哪些不验证。

答案 1 :(得分:4)

我现在正在处理这个问题。我决定做的是覆盖DB上下文中的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);
} 

我确信有一些漏洞可以在其中加入,但它似乎比替代方案更好。

答案 2 :(得分: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));