entity framework5更新logging

在ASP.NET MVC3环境中,我一直在探索在Entity Framework 5中编辑/更新logging的不同方法,但是到目前为止,没有一个方法可以勾选我需要的所有框。 我会解释为什么。

我已经find了三个方法,我会提到的利弊:

方法1 – 加载原始logging,更新每个属性

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

优点

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

缺点

  • 2 x查询数据库加载原始然后更新它

方法2 – 加载原始logging,设置更改的值

 var original = db.Users.Find(updatedUser.UserId); if (original != null) { db.Entry(original).CurrentValues.SetValues(updatedUser); db.SaveChanges(); } 

优点

  • 只有被修改的属性被发送到数据库

缺点

  • 视图需要包含每个属性
  • 2 x查询数据库加载原始然后更新它

方法3 – 附加更新logging并将状态设置为EntityState.Modified

 db.Users.Attach(updatedUser); db.Entry(updatedUser).State = EntityState.Modified; db.SaveChanges(); 

优点

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

缺点

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

我的问题给你们 有没有一种干净的方式可以实现这一套目标?

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

我知道这是一个很小的事情要指出,但我可能会错过一个简单的解决scheme。 如果不是方法一将占上风;-)

您正在寻找:

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

我真的很喜欢接受的答案。 我相信还有另外一种方法来解决这个问题。 假设你有一个非常短的属性列表,你不希望包含在一个视图中,所以在更新实体时,这些属性将被忽略。 假设这两个字段是密码和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(); 

这个例子允许你在用户表和你的View中添加一个新的字段后,基本上保持业务逻辑不变。

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

我已经在我的存储库基类中添加了一个额外的更新方法,类似于Scaffolding生成的更新方法。 它不是将整个对象设置为“修改”,而是设置一组单独的属性。 (T是一个类的generics参数。)

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

然后打电话,例如:

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

我喜欢一次去数据库。 尽pipe如此,使用视图模型可能会更好,以避免重复设置属性。 我还没有这样做,因为我不知道如何避免将视图模型validation器上的validation消息放到我的域项目中。

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

只是添加到选项列表。 您也可以从数据库中抓取对象,然后使用Auto Mapper之类的自动映射工具更新要更改的logging部分。

根据您的使用情况,所有上述解决scheme都适用。 这是我通常这样做的:

对于服务器端代码(例如批处理),我通常加载实体并使用dynamic代理。 通常在批处理过程中,您需要在服务运行时加载数据。 我尝试批量加载数据而不是使用find方法来节省一些时间。 根据不同的过程,我使用乐观或悲观的并发控制(我总是使用乐观,除了平行执行的情况下,我需要用普通的sql语句locking一些logging,这是罕见的)。 根据代码和scheme的影响可以减less到几乎为零。

对于客户端scheme,您有几个选项

  1. 使用视图模型。 模型应该有一个属性UpdateStatus(未修改插入更新删除)。 客户端的责任是根据用户操作(insert-update-delete)为此列设置正确的值。 服务器可以在数据库中查询原始值,或者客户端应该将原始值和已更改的行一起发送到服务器。 服务器应该附加原始值,并使用每行的UpdateStatus列来决定如何处理新的值。 在这种情况下,我总是使用乐观并发。 这只会执行插入 – 更新 – 删除语句,而不是任何select,但它可能需要一些聪明的代码来走图和更新实体(取决于你的场景 – 应用程序)。 映射程序可以帮助但不处理CRUD逻辑

  2. 使用像breeze.js这样的库,它隐藏了大部分的复杂性(如1所述),并尝试将其适用于您的用例。

希望它有帮助