Элемент ViewData с ключом "XXX" имеет тип "System.Int32", но должен иметь тип "IEnumerable <SelectListItem>",
У меня есть следующая модель представления
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: элемент ViewData, который имеет ключ "CategoryID", имеет тип "System.Int32", но должен иметь тип "IEnumerable <SelectListItem> ".
То же самое происходит с использованием метода @Html.DropDownList()
, и если я передаю SelectList с помощью ViewBag
или ViewData
.
Ответы
Ответ 1
Ошибка означает, что значение CategoryList
равно null (и в результате метод DropDownListFor()
ожидает, что первый параметр имеет тип IEnumerable<SelectListItem>
).
Вы не создаете вход для каждого свойства каждого SelectListItem
в CategoryList
(и не должны), поэтому никакие значения для 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
}
Чтобы объяснить внутреннюю работу (исходный код может быть здесь)
Каждая перегрузка 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
является typeof int
, его нельзя отнести к IEnumerable<SelectListItem>
и исключение (которое определено в файле MvcResources.resx
as)
<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>
Ответ 2
согласно Stephens (user3559349) ответ, это может быть полезно:
@Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
или в ProjectVM:
public class ProjectVM
{
public ProjectVM()
{
CategoryList = new List<SelectListItem>();
}
...
}
Ответ 3
У меня была та же проблема, я получал неверный ModelState, когда пытался опубликовать форму. Для меня это было вызвано установкой CategoryId на int, когда я изменил его на строку, ModelState был действителен, а метод Create работал как положено.
Ответ 4
Скорее всего Вызвали какую-то ошибку при перенаправлении на вашу страницу, и вы не инициализировали выпадающие списки своей модели снова.
Убедитесь, что вы инициализируете свои раскрывающиеся списки либо в конструкторе модели, либо каждый раз перед отправкой указанной модели на страницу.
В противном случае вам нужно будет поддерживать состояние выпадающих списков либо через пакет просмотра, либо через помощников скрытых значений.
Ответ 5
Ладно, афиша ответа баннера аккуратно объяснила, почему произошла ошибка, а не как заставить ее работать. Я не уверен, что это действительно ответ, но это действительно указало мне правильное направление.
Я столкнулся с той же проблемой и нашел хитрый способ ее решения. Я постараюсь уловить это здесь. Отказ от ответственности - я работаю над веб-страницами один раз в год и действительно не знаю, чем занимаюсь большую часть времени. Этот ответ никоим образом не следует считать "экспертным" ответом, но он выполняет работу без особой работы...
Учитывая, что у меня есть некоторый объект данных (скорее всего, объект передачи данных), я хочу использовать раскрывающийся список для предоставления допустимых значений для поля, например, так:
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()
{
return new MyDataObjectVM
{
int id = 0,
string 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.
Я публикую это здесь, чтобы найти ответ, когда неизбежно столкнусь с этой проблемой снова - надеюсь, это поможет кому-то еще.