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


113

我有以下视图模型

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

和以下控制器方法来创建新项目并分配一个 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 <SelectListItem>”。

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

Answers:


109

该错误表示的值为CategoryList null(因此该DropDownListFor()方法期望第一个参数的类型为IEnumerable<SelectListItem>)。

您不会为SelectListItemin中的每个属性生成输入CategoryList(也不应该),因此不会将的值SelectList发布到控制器方法,因此model.CategoryListPOST方法中的值为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
}

解释内部工作原理(可以在此处查看源代码)

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

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

// 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)的第一个参数

....
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文件中定义为)

<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>

8
@Shyju,是的,我问并回答了这个问题(作为社区Wiki),目的纯粹是为了欺骗其他许多类似的问题,这些问题仍然没有得到回答或不被接受。但是我看到报仇者已经开始报仇-第一个是在发布后不到2秒的时间-甚至没有足够的时间阅读,更不用说答案了。

1
我懂了。共有100道问题,同样的问题。通常,提出这些问题的人没有进行正确的搜索(或者他们逐字复制并粘贴了现有的答案,但是没有用!)所以我不确定这是否真的有帮助。:)写的顺便说一句。
Shyju 2015年

@斯蒂芬,这不是你问和回答的正确方法
Dilip Oganiya

8
@DilipN,你是什么意思不正确的做法?它实际上对SO鼓励。你应该阅读和花一些时间元。

4
@DilipN,因为我将使用它来将许多类似的问题标记为重复项,这些重复项要么未被回答,要么被回答但未被接受,因此可以将其拒之门外(这样其他人就不会浪费时间)。我还使其成为社区Wiki,因此任何人都可以随着时间的推移对其进行编辑和改进。

6

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

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

或在ProjectVM中:

public class ProjectVM
{
    public ProjectVM()
    {
        CategoryList = new List<SelectListItem>();
    }
    ...
}

1

最有可能导致某种错误重定向到您的页面,并且您没有再次初始化模型的下拉列表。

在将所述模型发送到页面之前,请确保在模型的构造函数中或每次都初始化下拉列表。

否则,您将需要通过视图包或隐藏的值助手来维护下拉列表的状态。


0

我遇到了同样的问题,尝试发布表单时收到无效的ModelState。对我来说,这是由于将CategoryId设置为int引起的,当我将其更改为字符串时,ModelState有效且Create方法按预期工作。


0

好了,海报的罐头答案很好地解释了为什么发生错误,但不知道如何得到它的工作。我不确定这是否是真正的答案,但这确实为我指明了正确的方向。

我遇到了同样的问题,并找到了一种巧妙的方法来解决它。我会在这里尝试捕获。免责声明-我每年大约在网页上工作一次,但实际上大部分时间我都不知道自己在做什么。绝不应该将此答案视为“专家”答案,但它几乎不需要做任何工作。

假设我有一些数据对象(最有可能是数据传输对象),我想使用下拉列表为字段提供有效值,如下所示:

public class MyDataObject
{
  public int id;
  public string StrValue;
}

然后,ViewModel如下所示:

public class MyDataObjectVM
{
  public int id;

  public string StrValue;
  public List<SectListItem> strValues;
}

正如上面@Stephen雄辩地描述的那样,这里的真正问题是选择列表未填充在控制器的POST方法上。因此,您的控制器方法将如下所示:

// GET
public ActionResult Create()
{
  var dataObjectVM = GetNewMyDataObjectVM();
  return View(dataObjectVM); // I use T4MVC, don't you?
}

private MyDataObjectVM GetNewMyDataObjectVM(MyDataObjectVM model = null)
{
  return new MyDataObjectVM
  {
    int id = model?.Id ?? 0,
    string StrValue = model?.StrValue ?? "", 
    var strValues = new List<SelectListItem> 
      { 
        new SelectListItem {Text = "Select", Value = ""},
        new SelectListITem {Text = "Item1", Value = "Item1"},
        new SelectListItem {Text = "Item2", Value = "Item2"}
      };
  };
}

// POST
public ActionResult Create(FormCollection formValues)
{
  var dataObject = new MyDataObject();

  try
  {
    UpdateModel(dataObject, formValues);
    AddObjectToObjectStore(dataObject);

    return RedirectToAction(Actions.Index);
  }
  catch (Exception ex)
  {
    // fill in the drop-down list for the view model
    var dataObjectVM = GetNewMyDataObjectVM();
    ModelState.AddModelError("", ex.Message);

    return View(dataObjectVM);
  )
}

你有它。这不是工作代码,为了方便起见,我进行了复制/粘贴和编辑,但您明白了。如果原始数据模型和派生的视图模型中的数据成员都具有相同的名称,则UpdateModel()会完成一项出色的工作,即从FormCollection值中为您填充正确的数据。

我将其发布在此处,以便当我不可避免地再次遇到此问题时可以找到答案-希望它也可以帮助其他人。


0

在我的情况下,列表中的第一个ID为零,一旦我将ID更改为从1开始,它就会起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.