下面是场景:我有一个像这样的User对象:
public class User : BaseEntity<User>, IAggregateRoot
{
public virtual string Name { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string SecretQuestion { get; set; }
public virtual string SecretAnswer { get; set; }
public virtual DateTime LastLogin { get; set; }
}
在编辑这个对象的过程中,我将它加载到视图中,但我只想更新一些属性(即我不想更新LastLogin属性)。在这种情况下,我该怎么办?
是创建用户视图模型的最佳策略,当我尝试使用null LastLogin字段更新用户对象时,是否会禁止这样做?
提前致谢。
修改
这样的事情:
public class UserViewModel
{
public string Name {get;set;}
public string UserName {get;set;}
public string Password {get;set;}
public string SecretQuestion {get;set;}
public string SecretAnswer {get;set;}
}
然后编辑:
public ActionResult Edit(int id)
{
return View(_userRepository.FindById(id));
}
[HttpPost]
public ActionResult Edit(int id, UserViewModel userViewModel)
{
try
{
//Not sure how to update the model
//with the view Model and save.
_userRepository.Update(????);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
答案 0 :(得分:8)
一个好的方法是创建一个仅包含要显示/更新的属性的UserViewModel
。不要让nHibernate知道视图模型。然后,当编辑回发到控制器时,您从nHibernate检索实际的User对象,从视图模型更新它的属性,然后将其保存回数据库。
<强>更新强>
这样的事情:
[HttpPost]
public ActionResult Edit(int id, UserViewModel userViewModel)
{
try
{
User model = _userRepository.FindById(id);
model.Name = userViewModel.Name;
model.Username = userViewModel.Username;
model.Password = userViewModel.Password;
model.SecretQuestion = userViewModel.SecretQuestion;
model.SecretAnswer = userViewModel.SecretAnswer;
_userRepository.Update(model);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
在我最近一直在研究的项目中,我创建了一个ViewModelBase类,其中包含将属性从域模型映射到视图模型的方法,然后根据属性名称和类型进行匹配。我的所有视图模型都是从ViewModelBase派生的。
还有其他工具,例如AutoMapper,可以做很多这样的事情。
答案 1 :(得分:3)
我正在添加另一个答案,因为还有另一种完全不同的方法。
您可以在视图中使用User
对象,然后使用TryUpdateModel
指定要在模型中更新的属性。在这种情况下,您不一定需要视图模型。如果你愿意,你可以使用一个,但你不需要。
Post动作看起来像是:
[HttpPost]
public ActionResult Edit(int id)
{
try
{
User model = _userRepository.FindById(id);
var propertiesToUpdate = new string[] { "Name",
"Username",
"Password",
"SecretQuestion",
"SecretAnswer" };
if (TryUpdateModel(model, propertiesToUpdate)) {
_userRepository.Update(model);
return RedirectToAction("Index");
}
}
catch
{
// Handle exceptions however you want.
}
return View();
}
字符串数组是要在模型中更新的属性的白名单,控制器会尝试更新表单发布数据(和其他来源)中的值。由于LastLogin
不在字符串数组中,因此不会在模型更新中触及它。
答案 2 :(得分:0)
我正在开发一种技术,我可以创建DefaultModelBinder的自定义衍生产品。它有点像@AndrewCooper发布的白名单方法一样有效,但是,有点扭曲。
在我的方法中,我坚持使用一个可以让无数部分视图尝试更新它的大型鸣喇叭ViewModel。在我的例子中,白名单是通知ViewModel如何从存储库自动加载自身所需的关键对象标识字段 - 因此,期望部分视图仅包含字段他们关心,并且,如果从路线或其他方面没有立即显现出来的话,足够隐藏的领域可以充实对象身份。
因此,在POST'模型绑定'阶段,首先绑定标识字段(因此,触发'加载'以立即从DB中充实模型)并且随着绑定过程的其余部分完成,您有效“合并”从数据库中提取的对象与用户提交的数据..除此之外(假设您已经坚持使用MVC约定),它现在已通过验证。
这使我能够坚持使用教科书简单的控制器方法......就像..
Public Function Edit(id As Integer, workOrder as MergedWorkOrderModel) As ActionResult
If ModelState.IsValid Then
/* save model, send success messaging into ViewBag, etc */
workOrder.Save("ServiceUrl")
workOrder.Load("ServiceUrl")
ViewBag.SuccessMessage = String.Format("Order {0} saved successfully!", workOrder.Id)
Return View(workOrder)
Else
/* return user model to fix errors */
ViewBag.ErrorMessage = String.Format("Order {0} did not save.", workOrder.Id)
Return View(workOrder)
End If
End Function
理论上,我可以将所有不同的视图都指向同一个方法 - 因为它不关心用户数据来自哪里......所有这些都与DB中最新的一起合并,验证并保存完全相同。< / p>