ActionResult参数没有默认构造函数

时间:2012-12-20 16:43:32

标签: asp.net asp.net-mvc viewmodel

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

首先,NerdDinner教程的编辑操作采用的形式(比如表格A):

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

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

[HttpPost]
public ActionResult Edit(MyViewModel mvm) {

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

所以,第一个问题是你是否同意通常表格B通常更好?有缺点吗?

其次,似乎如果使用表单B,装饰器类型验证将需要在ViewModel中。是否有在ViewModel中嵌入实体并仅在实体级别保持验证的优势?

2 个答案:

答案 0 :(得分:1)

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

  

第一个问题是你是否同意通常表格B通常更好?

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

  

其次,似乎如果使用表单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或实体没有无参数构造函数?听起来像是一个糟糕的设计,即使有一些要求,它也可以通过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
}

答案 1 :(得分:0)

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

例如,如果ViewModel具有在某种下拉列表中使用的词典,则不会提交整个下拉列表,只会提交所选的值。

我的一般方法是:

[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;
}