MVC - 在视图中部分更新模型

时间:2011-05-16 23:27:06

标签: asp.net-mvc nhibernate viewmodel

下面是场景:我有一个像这样的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();
  }
}

3 个答案:

答案 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>

More info at my StackOverflow post