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)
我有一个控制器,它 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)