Asp.Net MVC - 模型绑定到模型或ViewModel

时间:2015-09-04 19:48:24

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

我有一个控制器,它返回一个视图模型中传递的视图,该视图具有显示视图所需的属性(下拉选择项列表等)。

但是当我将它发布到服务器时,我有一个不同的模型类,它具有这些下拉列表的选定值。在我的HttpPost控制器操作中,我在进行任何处理之前检查(ModelState.IsValid),但是当它为false时,我返回View(模型) '背部。

但由于视图绑定到ViewModel,而我的Post动作正在接受实际模型,因此我收到错误' 传入字典的模型项的类型为' Model& #39;,但是当我提交表单时,此字典需要类型为' ViewModel' 的模型项,并且验证错误会显示在视图上。

我该如何解决这个问题?使用强类型视图,传入视图模型,但提交到其他模型时,最佳做法是什么?

代码:

 public ActionResult Buy()
    {
      BuyVM buyVM = GetBuyVM();
      return View(buyVM);
    }

   [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Buy(BuyModel model)
    {
      if (ModelState.IsValid)
        {
        // Do Procesing
        return View("Success");
        }
      return View(model);
    }

 public class BuyVM
    {
        public SelectList PurchaseDateList { get; set; }

        public SelectList BedroomsList { get; set; }

        public SelectList StoriesList { get; set; }

        [Required]
        public string SquareFootage { get; set; }

        [Required]
        public string PreferredCityLocations { get; set; }

        public string AdditionalInfo { get; set; }
    }

 public class BuyModel 
    {
        public string PurchaseDateList { get; set; }
        public string BedroomsList { get; set; }
        public string StoriesList { get; set; }
        public string SquareFootage { get; set; }
        public string PreferredCityLocations { get; set; }
        public string AdditionalInfo { get; set; }
    }

 private static BuyVM GetBuyVM()
        {
            BuyVM buyVM = new BuyVM();

            buyVM.PurchaseDateList = new SelectList(new[] { "Immediately", "1 to 3 months", "4 to 6 months", "More than 6 months" });
            buyVM.BedroomsList = new SelectList(new[] { "1", "2", "3", "4", "5+" });
            buyVM.StoriesList = new SelectList(new[] { "1", "2", "Does not matter" });

            return buyVM;
        }

Buy.cshtml

    @model Models.BuyVM
    // html
 @Html.DropDownListFor(m => m.PurchaseDateList, Model.PurchaseDateList, new { @class = "form-control" })

 @Html.DropDownListFor(m => m.BedroomsList, Model.BedroomsList, new { @class = "form-control" })

所以当我返回View(模型)时,如果有验证错误(JQueryVal),在HTTPPost中,我试图显示验证错误,如果我将模型传递回视图。但我有这种类型不匹配。

3 个答案:

答案 0 :(得分:3)

首先,您的数据模型的名称没有意义。名为BedroomsList的属性建议Bedrooms的集合,但属性为string。首先命名您的属性来描述它们是什么,以便其他人可以理解您的代码。

public class BuyModel 
{
  public string PurchaseDate { get; set; }
  public string Bedrooms { get; set; }
  public string Stories { get; set; }
  public string SquareFootage { get; set; }
  public string PreferredCityLocations { get; set; }
  public string AdditionalInfo { get; set; }
}

相应的视图模型需要包含这些属性以及SelectList属性。

public class BuyVM
{
  public string PurchaseDate { get; set; }
  public string Bedrooms { get; set; }
  public string Stories { get; set; }
  [Required]
  public string SquareFootage { get; set; }
  [Required]
  public string PreferredCityLocations { get; set; }
  public string AdditionalInfo { get; set; }
  public SelectList PurchaseDateList { get; set; }
  public SelectList BedroomsList { get; set; }
  public SelectList StoriesList { get; set; }
}

接下来删除GetBuyVM()方法并将其替换为填充选择列表的控制器中的方法,以便在ModelState无效且您需要返回视图时也可以调用该方法将POST方法更改为参数到您的视图模型(您的视图基于BuyVM,因此您必须回发到BuyVM,而不是BuyModel

public ActionResult Buy()
{
  BuyVM model = new BuyVM(); // initalise an instance of the view model
  ConfigureViewModel(model);
  return View(model);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Buy(BuyVM model)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(model);
    return View(model);
   }
   Initialize your data model and map the view model properties to it
   BuyModel dataModel = new BuyModel()
   {
     PurchaseDate = model.PurchaseDate,
     Bedrooms = model.Bedrooms,
     ....
   };
   // save the data model
   return View("Success");
}

private ConfigureViewModel(BuyVM model)
{
  model.PurchaseDateList = new SelectList(new[] { "Immediately", "1 to 3 months", "4 to 6 months", "More than 6 months" });
  model.BedroomsList = new SelectList(new[] { "1", "2", "3", "4", "5+" });
  model.StoriesList = new SelectList(new[] { "1", "2", "Does not matter" });
}

最后在视图中,绑定到您的媒体资源(PurchaseDate,而不是PurchaseDateList

@Html.DropDownListFor(m => m.PurchaseDate, Model.PurchaseDateList)
@Html.DropDownListFor(m => m.Bedrooms, Model.BedroomsList)

答案 1 :(得分:0)

在回发后,如果视图模型有效,则将视图模型移动到实体模型。请注意,Post采用了​​一个视图模型,可以防止您暴露通常被认为不安全的实体模型。像AutoMapper这样的工具非常适用于此,但您可以手动完成:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Buy(BuyVM buyVM)
{

  if (ModelState.IsValid)
    {
    var buyModel = new BuyModel {
      PurchaseDateList = buyVM.PurchaseDateList,
      BedroomsList = buyVM.BedroomsList,
      ...
     };
     // Do Procesing, Save Entity Model
  }
  // Otherwise, reset unbound fields on viewmodel
  buyVM.List = GetList();
  ...
  return View(buyVM);
}

MVC会自动传回错误信息。

答案 2 :(得分:-1)

在返回视图之前,您必须重新构建视图模型 - 这意味着,在返回视图之前,包含您的模型,重建所有下拉列表等。

您还可以考虑在Post动作中找到一种方法来使用viewmodel。