我一直在探索在ASP.NET MVC3环境中编辑/更新Entity Framework 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();
}
赞成
缺点
方法2 - 加载原始记录,设置更改值
var original = db.Users.Find(updatedUser.UserId);
if (original != null)
{
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();
}
赞成
缺点
方法3 - 附加更新记录并将状态设置为EntityState.Modified
db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();
赞成
缺点
问题
我向你们提问;有没有一种干净的方式可以达到这套目标?
我理解这是一个非常小的事情要指出,但我可能错过了一个简单的解决方案。如果不是方法一将占上风; - )
答案 0 :(得分:667)
您正在寻找:
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
答案 1 :(得分:174)
我真的很喜欢接受的答案。我相信还有另一种方法可以解决这个问题。假设您有一个非常短的属性列表,您不希望在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();
此示例允许您在向Users表和View添加新字段后,基本上保留业务逻辑。
答案 2 :(得分: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();
答案 3 :(得分:22)
我在我的存储库基类中添加了一个额外的更新方法,类似于Scaffolding生成的更新方法。它不是将整个对象设置为“已修改”,而是设置一组单独的属性。 (T是类通用参数。)
pyusb
然后打电话,例如:
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;
}
}
我喜欢一次数据库之旅。但是,使用视图模型可能更好,以避免重复属性集。我还没有这样做,因为我不知道如何避免将我的视图模型验证器上的验证消息带到我的域项目中。
答案 4 :(得分: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();
}
}
答案 5 :(得分:3)
只需添加到选项列表中即可。您还可以从数据库中获取对象,并使用自动映射工具(如Auto Mapper)更新要更改的记录部分。
答案 6 :(得分:3)
根据您的使用情况,以上所有解决方案均适用。这就是我通常这样做的方式:
对于服务器端代码(例如批处理),我通常加载实体并使用动态代理。通常在批处理过程中,您需要在服务运行时加载数据。我尝试批量加载数据而不是使用find方法来节省一些时间。根据流程,我使用乐观或悲观的并发控制(我总是使用乐观,除了我需要使用普通的sql语句锁定一些记录的并行执行场景,这种情况很少见)。根据代码和场景,影响可以降低到几乎为零。
对于客户端方案,您有几个选项
使用视图模型。模型应具有UpdateStatus属性(未修改插入更新删除)。客户端有责任根据用户操作(insert-update-delete)为此列设置正确的值。服务器可以向db查询原始值,或者客户端应将原始值与更改的行一起发送到服务器。服务器应附加原始值并使用每行的UpdateStatus列来决定如何处理新值。在这种情况下,我总是使用乐观并发。这只会执行insert - update - delete语句而不是任何选择,但它可能需要一些聪明的代码来遍历图形并更新实体(取决于您的场景 - 应用程序)。映射器可以帮助但不处理CRUD逻辑
使用类似breeze.js的库来隐藏大部分复杂性(如1中所述),并尝试将其与您的用例相匹配。
希望有所帮助
答案 7 :(得分:0)
已经给出了一些非常好的答案,但我想投入两分钱。这是将视图对象转换为实体的非常简单的方法。简单的想法是,只有视图模型中存在的属性才被写入实体。这类似于@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;
}
专业人士
缺点
对我来说,这种方法的简单性和低维护要求超过了添加的数据库调用。