我有以下视图模型
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&lt; SelectListItem&gt;”类型。
使用@Html.DropDownList()
方法发生同样的错误,如果我使用ViewBag
或ViewData
传递SelectList。
答案 0 :(得分:87)
错误意味着CategoryList
的值为null(因此DropDownListFor()
方法期望第一个参数的类型为IEnumerable<SelectListItem>
)。
您没有为SelectListItem
中的每个CategoryList
生成一个输入(也不应该),因此SelectList
的值不会发布到控制器方法中,因此POST方法中model.CategoryList
的值为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
的类型,所以无法将其强制转换为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>
答案 1 :(得分:3)
根据斯蒂芬的回答,这可能很有用:
@Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
或在ProjectVM中:
public class ProjectVM
{
public ProjectVM()
{
CategoryList = new List<SelectListItem>();
}
...
}
答案 2 :(得分:0)
我遇到了同样的问题,我在尝试发布表单时得到了无效的ModelState。对我来说,这是由于将CategoryId设置为int引起的,当我将其更改为字符串时,ModelState有效且Create方法按预期工作。
答案 3 :(得分:0)
最可能导致重定向到页面的某种错误,并且您没有再次初始化模型的下拉列表。
在将所述模型发送到页面之前,请确保在模型的构造函数中或每次都初始化下拉列表。
否则,您将需要通过视图包或隐藏的值助手来维护下拉列表的状态。
答案 4 :(得分: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值中为您填充正确的数据。
我将其发布在这里,以便当我不可避免地再次遇到此问题时可以找到答案-希望它也可以帮助其他人。
答案 5 :(得分:0)
在我的情况下,列表中的第一个ID为零,一旦将ID更改为从1开始,它就起作用了。