我有以下视图模型
public class ProjectVM { .... [Display(Name = "Category")] [Required(ErrorMessage = "Please select a category")] public int CategoryID { get; set; } public IEnumerable<SelectListItem> CategoryList { get; set; } .... }
和以下控制器方法来创建新项目并分配一个 Category
Category
public ActionResult Create() { ProjectVM model = new ProjectVM { CategoryList = new SelectList(db.Categories, "ID", "Name") } return View(model); } public ActionResult Create(ProjectVM model) { if (!ModelState.IsValid) { return View(model); } // Save and redirect }
并认为
@model ProjectVM .... @using (Html.BeginForm()) { .... @Html.LabelFor(m => m.CategoryID) @Html.DropDownListFor(m => m.CategoryID, Model.CategoryList, "-Please select-") @Html.ValidationMessageFor(m => m.CategoryID) .... <input type="submit" value="Create" /> }
视图显示正确,但是提交表单时,出现以下错误消息
InvalidOperationException:具有键“ CategoryID”的ViewData项的类型为“ System.Int32”,但必须为类型“ IEnumerable ”。
使用@Html.DropDownList()方法会发生相同的错误,如果我使用ViewBag或传递了SelectList,则会发生同样的错误ViewData。
@Html.DropDownList()
ViewBag
ViewData
该错误表示的值为CategoryList null(因此该DropDownListFor()方法期望第一个参数的类型为IEnumerable<SelectListItem>)。
CategoryList
DropDownListFor()
IEnumerable<SelectListItem>
您不会为SelectListItemin中的每个属性生成一个输入CategoryList(也不应该),因此不会将的值SelectList发布到控制器方法中,因此model.CategoryListPOST方法中的值为null。如果返回视图,则必须首先重新分配的值CategoryList,就像在GET方法中一样。
SelectListItem
SelectList
model.CategoryList
null
public ActionResult Create(ProjectVM model) { if (!ModelState.IsValid) { model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this return View(model); } // Save and redirect }
解释内部工作原理(可以在此处查看源代码)
每个重载DropDownList()并DropDownListFor()最终调用以下方法
DropDownList()
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
它检查selectList(的第二个参数@Html.DropDownListFor())是否为null
selectList
@Html.DropDownListFor()
// If we got a null selectList, try to use ViewData to get the list of items. if (selectList == null) { selectList = htmlHelper.GetSelectData(name); usedViewData = true; }
依次调用
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
它计算@Html.DropDownListFor()(在这种情况下CategoryID)的第一个参数
CategoryID
.... o = htmlHelper.ViewData.Eval(name); .... IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>; if (selectList == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.HtmlHelper_WrongSelectDataType, name, o.GetType().FullName, "IEnumerable<SelectListItem>")); }
由于property CategoryID是typeof int,因此无法将其强制转换为IEnumerable<SelectListItem>并且抛出异常(在MvcResources.resx文件中定义为)
int
MvcResources.resx
<data name="HtmlHelper_WrongSelectDataType" xml:space="preserve"> <value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value> </data>