具有键"XXX"的ViewData项的类型为"System.Int32",但必须是"IEnumerable <SelectListItem>"类型

99 c# asp.net-mvc

我有以下视图模型

public class ProjectVM
{
    ....
    [Display(Name = "Category")]
    [Required(ErrorMessage = "Please select a category")]
    public int CategoryID { get; set; }
    public IEnumerable<SelectListItem> CategoryList { get; set; }
    ....
}
Run Code Online (Sandbox Code Playgroud)

和以下控制器方法创建一个新项目并分配一个 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
}
Run Code Online (Sandbox Code Playgroud)

并在视图中

@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" />
}
Run Code Online (Sandbox Code Playgroud)

视图显示正确但在提交表单时,我收到以下错误消息

InvalidOperationException:具有键"CategoryID"的ViewData项的类型为"System.Int32",但必须是"IEnumerable <SelectListItem>"类型.

使用该@Html.DropDownList()方法会发生相同的错误,如果我使用ViewBag或传递SelectList ViewData.

小智 94

该错误意味着值为CategoryList null(因此该DropDownListFor()方法期望第一个参数是类型IEnumerable<SelectListItem>).

您不产生每个的每个属性的输入SelectListItemCategoryList(也不应你),以便为任何值SelectList都贴到控制器的方法,因此,值model.CategoryList在POST方法是null.如果返回视图,则必须首先重新分配值CategoryList,就像在GET方法中一样.

public ActionResult Create(ProjectVM model)
{
    if (!ModelState.IsValid)
    {
        model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
        return View(model);
    }
    // Save and redirect
}
Run Code Online (Sandbox Code Playgroud)

解释内部工作原理(源代码可以在这里看到)

每次重载DropDownList()DropDownListFor()最终调用以下方法

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
  string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
  IDictionary<string, object> htmlAttributes)
Run Code Online (Sandbox Code Playgroud)

检查selectList(第二个参数@Html.DropDownListFor())是否null

// 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;
}
Run Code Online (Sandbox Code Playgroud)

反过来打电话

private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
Run Code Online (Sandbox Code Playgroud)

它评估了第一个参数@Html.DropDownListFor()(在这种情况下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>"));
}
Run Code Online (Sandbox Code Playgroud)

因为属性CategoryID是typeof int,所以无法强制转换IEnumerable<SelectListItem>为异常并抛出异常(在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>
Run Code Online (Sandbox Code Playgroud)

  • @Shyju,是的,我问过并回答它(作为社区wiki)纯粹是出于对SO的许多其他类似问题进行欺骗的目的,这些问题仍未得到答复或未被接受.但我看到报复选民已经开始了 - 第一个是在发布后不到2秒 - 没有足够的时间来阅读它,更不用说答案了. (8认同)
  • @DilipN,你是什么意思_不是正确的方法_?它实际上鼓励SO.你应该阅读[this](http://stackoverflow.com/help/self-answer)并花一些时间在meta上. (8认同)
  • @DilipN,因为我会用它来标记许多类似的问题,因为它们要么没有得到答复,要么被回答但不被接受,因此它们可以被关闭(因此其他人不会浪费时间).我还把它作为一个社区维基,所以任何人都可以随着时间的推移编辑和改进它. (4认同)

Omi*_*-RH 5

根据stephens(user3559349)的回答,这可能很有用:

@Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
Run Code Online (Sandbox Code Playgroud)

或在ProjectVM中:

public class ProjectVM
{
    public ProjectVM()
    {
        CategoryList = new List<SelectListItem>();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)