我的UserRepository中存在一个问题,我想在其中更新用户。除非指定,否则我不希望更新某些字段,例如密码。例如,当我将用户从视图传递到服务器到存储库时,它会向用户发送空密码或空密码字符串。这个null被写入数据库(我不想要)。
我该如何应对这种情况?
域
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
存储库
public User Save(User user)
{
if (user.UserId > 0)
{
User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
//What do I do here?
}
context.Users.AddObject(user);
context.SaveChanges();
return user;
}
让我们说在这种情况下,我的观点只允许我更改Email
,因此唯一可以发送回Save()
方法的是:user.UserId
和{{1} } user.Email
为空。在我的情况下,数据库抛出错误,因为密码应该可以为空。
答案 0 :(得分:10)
分离的POCO场景(您不会在更新前从DB加载用户):
您可以有选择地说明必须更新哪些属性:
public User Save(User user)
{
if (user.UserId == 0)
{
context.Users.AddObject(user);
}
else
{
context.Users.Attach(user);
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
entry.SetModifiedProperty("Email");
}
context.SaveChanges();
return user;
}
您还可以创建两个Save
方法的重载。首先将更新整个对象,第二个将仅更新显式选择的属性:
public User Save(User user)
{
if (user.UserId == 0)
{
context.Users.AddObject(user);
}
else
{
context.Users.Attach(user);
context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);
}
context.SaveChanges();
return user;
}
public User Save(User user, IEnumerable<Expression<Func<User, object>>> properties)
{
if (user.UserId == 0)
{
context.Users.AddObject(user);
}
else
{
context.Users.Attach(user);
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
foreach(var selector in properties)
{
string propertyName = PropertyToString(selector.Body);
entry.SetModifiedProperty(propertyName);
}
}
context.SaveChanges();
return user;
}
// Doesn't work for navigation properties!
private static string PropertyToString(Expression selector)
{
if (selector.NodeType == ExpressionType.MemberAccess)
{
return ((selector as MemberExpression).Member as PropertyInfo).Name;
}
throw new InvalidOperationException();
}
您将以这种方式调用第二个重载:
userRepository.Save(user, new List<Expression<Func<User, object>>>
{
u => u.Email
});
附加方案(您将在更新前从DB加载用户):
您可以修改Save方法以接受委托,以便您可以控制如何执行更新:
public User Save(User user, Action<User, User> updateStrategy)
{
if (user.UserId > 0)
{
User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
updateStrategy(dbUser, user);
}
else
{
// New object - all properties should be saved
context.Users.AddObject(user);
}
context.SaveChanges();
return user;
}
您将以这种方式调用方法:
var user = GetUpdatedUserFromSomewhere();
repository.Save(user, (dbUser, mergedUser) =>
{
dbUser.Email = mergedUser.Email;
});
无论如何,尽管有我的例子,你一定要考虑Darin的帖子和特殊的ModelViews进行更新。
答案 1 :(得分:1)
您应该使用视图模型。视图模型是专门根据视图需求定制的类,仅包含此给定视图所需的属性。所以你的控制器动作应如下所示:
[HttpPost]
public ActionResult Update(UserViewModel model) { ... }
而不是:
[HttpPost]
public ActionResult Update(User model) { ... }
在控制器操作中,您可以在视图模型和模型之间进行映射。 AutoMapper是一个很好的工具,可以简化这项任务。
你应该非常小心,不要像这样暴露你的模特。始终在视图中使用视图模型。试想一下,如果你的模型上有一个IsAdministrator
布尔属性。
答案 2 :(得分:0)
你能这样做吗?
public User Save(User user)
{
if (user.UserId > 0)
{
User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
//What do I do here?
dbUser.Email = user.Email
user = dbUser;
}
else
{
context.Users.AddObject(user);
}
context.SaveChanges();
return user;
}