【问题标题】:The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'具有键“XXX”的 ViewData 项属于“System.Int32”类型,但必须属于“IEnumerable<SelectListItem>”类型
【发布时间】:2016-03-25 19:02:43
【问题描述】:

我有以下视图模型

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”类型。

使用@Html.DropDownList() 方法会发生同样的错误,如果我使用ViewBagViewData 传递SelectList。

【问题讨论】:

    标签: c# asp.net-mvc


    【解决方案1】:

    错误意味着CategoryList 的值为空(因此DropDownListFor() 方法要求第一个参数是IEnumerable&lt;SelectListItem&gt; 类型)。

    您没有为CategoryList 中的每个SelectListItem 的每个属性生成输入(您也不应该),因此没有将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
    }
    

    解释内部工作原理(源代码可以是seen here

    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>"));
    }
    

    因为CategoryID属性是int的typeof,所以不能强制转换为IEnumerable&lt;SelectListItem&gt;并抛出异常(在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>
    

    【讨论】:

    • @Shyju,是的,我提出并回答了它(作为社区维基),纯粹是为了欺骗许多其他关于 SO 的类似问题,这些问题仍未得到回答或未被接受。但我看到报复选民已经开始了——第一个在发布后不到 2 秒——甚至没有足够的时间阅读它,更不用说答案了。
    • 我明白了。有 100 个问题与相同的问题。通常提出这些问题的人没有进行适当的搜索(或者他们逐字复制并粘贴现有答案,但没有用!)所以我不确定这是否真的有帮助。 :) 写得很好顺便说一句。
    • @Stephen 这不是你问和你回答的正确方式
    • @DilipN,你是什么意思不正确的方式?它实际上鼓励SO。您应该阅读 this 并花一些时间在 meta 上。
    • @DilipN,因为我将使用它将许多类似的问题标记为重复项,这些问题要么未得到答复,要么已回答但未接受,因此可以将其关闭(因此其他人不会浪费他们的时间)。我还把它做成了一个社区维基,这样任何人都可以随着时间的推移对其进行编辑和改进。
    【解决方案2】:

    根据 stephens (user3559349) answer,这可能很有用:

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

    或在 ProjectVM 中:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        好的,发帖人的预设答案巧妙地解释了为什么会发生错误,但没有说明如何让它工作。我不确定这是否真的是一个答案,但它确实为我指明了正确的方向。

        我遇到了同样的问题,并找到了一种巧妙的方法来解决它。我会尝试在这里捕捉到这一点。免责声明 - 我一年左右在网页上工作一次,并且真的不知道我大部分时间在做什么。这个答案绝不应该被视为“专家”的答案,但它只需很少的工作就可以完成工作......

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

        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 值中为您填充正确的数据。

        我在这里发布这个,以便在我不可避免地再次遇到这个问题时找到答案——希望它也能帮助其他人。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              猜你喜欢
              • 2016-03-21
              • 2014-11-13
              • 2023-03-12
              • 2018-07-24
              • 1970-01-01
              • 2017-01-22
              相关资源
              最近更新 更多