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

Asp.Net MVC - Model Binding to Model or ViewModel

我有一个控制器,它 return 是一个在视图模型中传递的视图,它具有显示视图所需的属性(下拉 select 项目列表等)。

但是当我 post 它到服务器时,我有一个不同的模型 class,它具有那些下拉菜单的 selected 值。在我的 HttpPost 控制器操作中,我在进行任何处理之前检查 (ModelState.IsValid),但是当它为假时,我 ' return 查看(模型)' 返回。

但是由于视图绑定到 ViewModel,而我的 Post 操作正在接受实际模型,我得到一个错误'传递到字典中的模型项是类型'Model',但是当我提交表单时,这个字典需要一个类型为 '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" })

所以当我 return 视图(模型)返回时,在 HTTPPost 中如果存在验证错误 (JQueryVal),我会尝试显示验证错误,如果我通过模型返回视图。但是我有这种类型不匹配。

您必须在再次返回视图之前重建视图模型 - 这意味着,在返回视图之前,在包含您的模型的情况下重建所有下拉菜单等。

您也可以考虑找到一种方法来在您的 Post 操作中使用视图模型。

在 post 上将您的视图模型移回实体模型(如果它有效)。请注意,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会自动回传错误信息。

首先,您的数据模型名称没有意义。名为 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() 方法并将其替换为控制器中填充 select 列表的方法,这样您也可以在 ModelState 无效时调用该方法并且您需要 return 视图并将 POST 方法更改为视图模型的参数(您的视图基于 BuyVM 因此您必须 post 返回 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)