3

显然有很多方法可以做到这一点,但我想我会要求对这些方法的优缺点提供一些反馈。

首先,NerdDinner 教程的 Edit Action 的形式是(比如 Form A):

[HttpPost]
public ActionResult Edit(int id, FormCollection collection) {

在我看来,如果你很好地塑造你的 ViewModel 以匹配你的观点,那么 Form B 的方法:

[HttpPost]
public ActionResult Edit(MyViewModel mvm) {

似乎是一种更好,更清洁的方法。然后,我只需将 VM 属性映射到模型属性并保存。但是,如果此 ViewModel 中嵌入了其他通过构造函数初始化的实体(例如在 nerddinner 教程中),那么如果没有默认构造函数并且您必须使用第一种方法,则此编辑操作将失败。

那么,第一个问题是您是否同意通常 B 型通常更好?有缺点吗?

其次,如果使用 Form B,那么装饰器类型验证似乎需要在 ViewModel 中。在 ViewModel 中嵌入实体并仅在实体级别进行验证是否有优势?

4

2 回答 2

1

这是一个非常普遍的问题。

第一个问题是您是否同意通常形式 B 通常更好?

我唯一不使用表格 B的时候是我上传文件的时候。否则,我不相信任何人都应该需要使用Form A。我认为人们使用Form A的原因是对 ASP.Net 版本的 MVC 的能力缺乏了解。

其次,如果使用 Form B,那么装饰器类型验证似乎需要在 ViewModel 中。

排序/它取决于。我给你举个例子:

public IValidateUserName
{
  [Required]
  string UserName { get; set; }
}

public UserModel
{
  string UserName { get; set; }
}

[MetadataType(typeof(IValidateUserName))]
public UserValiationModel : UserModel
{
}

验证装饰器位于接口中。我在派生类上使用 MetadataType 来验证派生类型。我个人喜欢这种做法,因为它允许可重复使用的验证,并且 MetadataType/Validation 不是 ASP.NET 核心功能的一部分,因此它可以在 ASP.Net (MVC) 应用程序之外使用。

在 ViewModels 中嵌入实体是否有优势..

是的,我尽我最大的努力从不将基本模型传递给视图。这是我不做的一个例子:

public class person { public Color FavoriteColor { get; set; } }

ActionResult Details()
{
  Person model = new Person();
  return this.View(model);
}

当您想将更多数据传递给您的视图(对于部分或布局数据)时会发生什么?大多数情况下,该信息与 Person 无关,因此将其添加到 Person 模型是没有意义的。相反,我的模型通常看起来像:

public class DetailsPersonViewModel()
{
  public Person Person { get; set; }
}

public ActionResult Details()
{
  DetailsPersonViewModel model = new DetailsPersonViewModel();
  model.Person = new Person();
  return this.View(model);
}

现在,我可以添加DetailsPersonViewModel该视图所需的数据,超出了 Person 知道的范围。例如,假设这将显示一个包含所有颜色的 for 供 Person 选择收藏夹。所有可能的颜色都不是人的一部分,也不应该是人模型的一部分,所以我将它们添加到 DetailPersonViewModel。

public class DetailsPersonViewModel()
{
  public Person Person { get; set; }
  public IEnumerable<Color> Colors { get; set; }
}

..并仅在实体级别进行验证?

System.ComponentModel.DataAnnotations并非旨在验证属性的属性,因此请执行以下操作:

public class DetailsPersonViewModel()
{
  [Required(property="FavoriteColor")]
  public Person Person { get; set; }
}

不存在也没有意义。为什么 ViewModel 不应该包含对需要验证的实体的验证。

如果没有默认构造函数,则此编辑操作将失败,您必须使用第一种方法。

正确,但是为什么 ViewModel 中的 ViewModel 或 Entity 没有无参数构造函数?听起来像是一个糟糕的设计,即使对此有一些要求,也可以通过 ModelBinding 轻松解决。这是一个例子:

// Lets say that this person class requires 
// a Guid for a constructor for some reason
public class Person
{
  public Person(Guid id){ }
  public FirstName { get; set; }
}

public class PersonEditViewModel
{
  public Person Person { get; set; }
}

public ActionResult Edit()
{
  PersonEditViewModel model = new PersonEditViewModel();
  model.Person = new Person(guidFromSomeWhere);

  return this.View(PersonEditViewModel);
}

//View 
@Html.EditFor(m => m.Person.FirstName)

//Generated Html
<input type="Text" name="Person.FirstName" />

现在我们有了一个用户可以输入新名字的表单。我们如何取回这个构造函数中的值?很简单,ModelBinder 不关心它绑定到什么模型,它只是将 HTTP 值绑定到匹配的类属性。

[MetadataType(typeof(IPersonValidation))]
public class UpdatePerson
{
  public FirstName { get; set; }
}  

public class PersonUpdateViewModel
{
  public UpdatePerson Person { get; set; }
}

[HttpPost]
public ActionResult Edit(PersonUpdateViewModel model)
{
  // the model contains a .Person with a .FirstName of the input Text box
  // the ModelBinder is simply populating the parameter with the values
  // pass via Query, Forms, etc

  // Validate Model

  // AutoMap it or or whatever

  // return a view
}
于 2012-12-20T18:21:47.263 回答
0

我还没有看过 NerDinner 项目,但是,我通常会尽量避免在操作的 POST 中使用 ViewModel,而是只提交“表单”的元素。

例如,如果 ViewModel 有一个 Dictionary 用于某种下拉列表,则不会提交整个下拉列表,只会提交选定的值。

我的一般做法是:

[HttpGet]
public ActionResult Edit(int id) 
{
     var form = _service.GetForm(id);

     var pageViewModel = BuildViewModel(form);

     return View(pageViewModel);
}

[HttpPost]
public ActionResult Edit(int id, MyCustomForm form) 
{
     var isSuccess = _service.ProcessForm(id);    

      if(isSuccess){
         //redirect
      }

      //There was an error. Show the form again, but preserve the input values
      var pageViewModel = BuildViewModel(form);

      return View(pageViewModel);
}

private MyViewModel BuildViewModel(MyCustomForm form)
{
     var viewModel = new MyViewModel();

     viewModel.Form = form;
     viewModel.StateList = _service.GetStateList();

    return viewModel;
}
于 2012-12-20T17:03:34.213 回答