【问题标题】:Update a single property of a record in Entity Framework Code First在 Entity Framework Code First 中更新记录的单个属性
【发布时间】:2011-04-06 10:15:22
【问题描述】:

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

的上下文中询问

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

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

现在我想更新用户的 LastActivity。我有用户 ID。我可以通过查询用户记录轻松地做到这一点,为 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 的问题。

【问题讨论】:

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


    【解决方案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 中进行,因此您不能说哪些对象应该验证,哪些不应该验证。

    【讨论】:

    • 我正在寻找一种禁用保存验证的方法,但没能做到。感谢您的解决方案。
    • 这个问题似乎在 EF 5 中得到修复。
    • 我想指出,并发检查可能会导致“0 行更新”异常,因为它将在 where 子句中添加检查以匹配 rowversion 列。 (在我的情况下它是空的,因为它是一个存根)
    • 我们可以做同样的事情,但使用“Id”以外的其他字段吗?例如User u = new User {Email = "abcd@123.com", LastActivity = DateTime.Now }; 其中 Email == "abcd@123.com" 可以唯一标识一个实体。
    • 如何使用它来更新导航属性?
    【解决方案2】:

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

    我敢肯定它有一些可以戳的洞,但它似乎比替代品更好。

    【讨论】:

    • 这不是一个完整的代码,但我喜欢这个想法。代码的主要问题是您将陷入您尝试查看的属性被修改而不是原始类型或复杂类型的情况,因此会崩溃。您需要有引用或收藏的条件。
    【解决方案3】:

    您可以尝试一种 hack:
    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));

    【讨论】:

    • 这不是黑客行为。不幸的是,单个列的直接更新只能在整个实体在手的情况下通过这样的原始 SQL 字符串来完成。
    猜你喜欢
    • 1970-01-01
    • 2012-05-10
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    • 2019-05-19
    • 2017-05-25
    • 2011-10-29
    • 1970-01-01
    相关资源
    最近更新 更多