实体框架5更新记录

Sto*_*out 856 c# asp.net-mvc-3 entity-framework-5

我一直在探索在ASP.NET MVC3环境中编辑/更新实体框架5中的记录的不同方法,但到目前为止,它们都没有勾选我需要的所有框.我会解释原因.

我找到了三种方法,我将提到它的优点和缺点:

方法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();
}    
Run Code Online (Sandbox Code Playgroud)

优点

  • 可以指定更改哪些属性
  • 视图不需要包含每个属性

缺点

  • 在数据库上进行2次查询以加载原始数据然后更新它

方法2 - 加载原始记录,设置更改的值

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

优点

  • 仅将已修改的属性发送到数据库

缺点

  • 视图需要包含每个属性
  • 在数据库上进行2次查询以加载原始数据然后更新它

方法3 - 附加更新的记录并将状态设置为EntityState.Modified

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

优点

  • 1 x查询数据库以进行更新

缺点

  • 无法指定更改哪些属性
  • 视图必须包含每个属性

我向你们提问; 有没有一种干净的方式可以达到这套目标?

  • 可以指定更改哪些属性
  • 视图不需要包含每个属性(例如密码!)
  • 1 x查询数据库以进行更新

我明白这是一个很小的事情要指出,但我可能错过了一个简单的解决方案.如果不是方法一将占上风;-)

Lad*_*nka 678

您正在寻找:

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

  • 嗨@Ladislav Mrnka,如果我想立即更新所有属性,我可以使用下面的代码吗?db.Departments.Attach(部门); db.Entry(department).State = EntityState.Modified; db.SaveChanges(); (59认同)
  • @Foysal:是的,你可以. (23认同)
  • @Foysal做context.Entry(entity).State = EntityState.Modified单独就足够了,不需要做attach.它会自动附加为修改后的...... (23认同)
  • 这种方法的一个问题是你无法模拟db.Entry(),这是一个严肃的PITA.EF在其他地方有一个相当不错的嘲弄故事 - 它(我可以告诉他们)在这里没有一个非常烦人. (5认同)
  • @ Sandman4,这意味着每个其他属性都需要存在并设置为当前值.在某些应用设计中,这是不可行的. (4认同)
  • "EF有一个相当不错的嘲弄故事" - 为什么现在的一切都必须是一个故事? (3认同)
  • 对我来说,只是`db.Users.Attach(updatedUser); db.SaveChanges();`单独做的伎俩. (2认同)
  • 参考如何?这似乎不适用于导航属性。我们如何更新代码以使上下文了解导航属性已被修改。 (2认同)
  • @LadislavMrnka你能解释一下你的答案吗? (2认同)

小智 175

我真的很喜欢接受的答案.我相信还有另一种方法可以解决这个问题.假设您有一个非常短的属性列表,您不希望在View中包含这些属性,因此在更新实体时,这些属性将被省略.假设这两个字段是密码和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();   
Run Code Online (Sandbox Code Playgroud)

此示例允许您在向Users表和View添加新字段后,基本上保留业务逻辑.

  • 如果我理解正确的话,"updatedUser"已经填充了FirstOrDefault()或类似的对象的实例,所以我只更新我改变了属性和设置别人ISModified =假.这很好用.但是,我想要做的是更新一个对象而不首先填充它,而不用任何FirstOrDefault()bofore更新.这是当我没有为所有被请求的字段指定值时收到错误,即使我在这些属性上设置了ISModified = false.entry.Property(e => e.columnA).IsModified = false; 没有这一行,ColumnA将失败. (4认同)

小智 28

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();
Run Code Online (Sandbox Code Playgroud)


Ian*_*ton 22

我在我的存储库基类中添加了一个额外的更新方法,类似于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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后打电话,例如:

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();
}
Run Code Online (Sandbox Code Playgroud)

我喜欢一次数据库之旅.但是,使用视图模型可能更好,以避免重复属性集.我还没有这样做,因为我不知道如何避免将我的视图模型验证器上的验证消息带到我的域项目中.


Mat*_*kan 11

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();
    }
}
Run Code Online (Sandbox Code Playgroud)