我一直在探索在ASP实体框架5中编辑/更新记录的不同方法。NET MVC3环境,但到目前为止,他们没有一个tick所有的盒子我需要。我会解释为什么。
我发现了三种方法,我将在这里介绍它们的优缺点:
方法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();
}
Pros
可以指定哪些属性更改
视图不需要包含每个属性
Cons
2 x查询数据库加载原始,然后更新它
方法2 -加载原始记录,设置更改值
var original = db.Users.Find(updatedUser.UserId);
if (original != null)
{
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();
}
Pros
只有修改后的属性才会被发送到数据库
Cons
视图需要包含每个属性
2 x查询数据库加载原始,然后更新它
方法3 -附加更新的记录并将状态设置为EntityState。修改
db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();
Pros
1 x查询数据库更新
Cons
不能指定更改哪些属性
视图必须包含所有属性
问题
我的问题是;有没有一种干净利落的方法可以让我实现这些目标?
可以指定哪些属性更改
视图不需要包含每个属性(比如密码!)
1 x查询数据库更新
我知道这是一件微不足道的事情,但我可能错过了一个简单的解决方案。如果没有,方法一将占上风;-)
根据您的用例,以上所有解决方案都适用。我通常是这样做的:
For server side code (e.g. a batch process) I usually load the entities and work with dynamic proxies. Usually in batch processes you need to load the data anyways at the time the service runs. I try to batch load the data instead of using the find method to save some time. Depending on the process I use optimistic or pessimistic concurrency control (I always use optimistic except for parallel execution scenarios where I need to lock some records with plain sql statements, this is rare though). Depending on the code and scenario the impact can be reduced to almost zero.
对于客户端场景,您有几个选项
Use view models. The models should have a property UpdateStatus(unmodified-inserted-updated-deleted). It is the responsibility of the client to set the correct value to this column depending on the user actions (insert-update-delete). The server can either query the db for the original values or the client should send the original values to the server along with the changed rows. The server should attach the original values and use the UpdateStatus column for each row to decide how to handle the new values. In this scenario I always use optimistic concurrency. This will only do the insert - update - delete statements and not any selects, but it might need some clever code to walk the graph and update the entities (depends on your scenario - application). A mapper can help but does not handle the CRUD logic
Use a library like breeze.js that hides most of this complexity (as described in 1) and try to fit it to your use case.
希望能有所帮助
根据您的用例,以上所有解决方案都适用。我通常是这样做的:
For server side code (e.g. a batch process) I usually load the entities and work with dynamic proxies. Usually in batch processes you need to load the data anyways at the time the service runs. I try to batch load the data instead of using the find method to save some time. Depending on the process I use optimistic or pessimistic concurrency control (I always use optimistic except for parallel execution scenarios where I need to lock some records with plain sql statements, this is rare though). Depending on the code and scenario the impact can be reduced to almost zero.
对于客户端场景,您有几个选项
Use view models. The models should have a property UpdateStatus(unmodified-inserted-updated-deleted). It is the responsibility of the client to set the correct value to this column depending on the user actions (insert-update-delete). The server can either query the db for the original values or the client should send the original values to the server along with the changed rows. The server should attach the original values and use the UpdateStatus column for each row to decide how to handle the new values. In this scenario I always use optimistic concurrency. This will only do the insert - update - delete statements and not any selects, but it might need some clever code to walk the graph and update the entities (depends on your scenario - application). A mapper can help but does not handle the CRUD logic
Use a library like breeze.js that hides most of this complexity (as described in 1) and try to fit it to your use case.
希望能有所帮助
已经有一些非常好的答案,但我想发表我的意见。下面是一个将视图对象转换为实体的非常简单的方法。简单的想法是,只有存在于视图模型中的属性才会被写入实体。这类似于@Anik Islam Abhi的回答,但具有零传播。
public static T MapVMUpdate<T>(object updatedVM, T original)
{
PropertyInfo[] originalProps = original.GetType().GetProperties();
PropertyInfo[] vmProps = updatedVM.GetType().GetProperties();
foreach (PropertyInfo prop in vmProps)
{
PropertyInfo projectProp = originalProps.FirstOrDefault(x => x.Name == prop.Name);
if (projectProp != null)
{
projectProp.SetValue(original, prop.GetValue(updatedVM));
}
}
return original;
}
Pros
视图不需要拥有实体的所有属性。
当您向视图添加删除属性时,您不必更新代码。
完全通用的
Cons
对数据库进行2次点击,一次加载原始实体,一次保存它。
对我来说,这种方法的简单性和低维护需求超过了增加的数据库调用。
我已经在我的存储库基类中添加了一个额外的更新方法,它类似于脚手架生成的更新方法。它不是将整个对象设置为“modified”,而是设置一组单独的属性。(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;
}
}
然后调用,例如:
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();
}
我喜欢去数据库一趟。不过,为了避免重复属性集,最好在视图模型中这样做。我还没有这样做,因为我不知道如何避免将视图模型验证器上的验证消息带入域项目。