Елемент ViewData, який має ключ "XXX", має тип "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; }
    ....
}

та наступний метод контролера для створення нового проекту та призначення a 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.

Відповіді:


109

Помилка означає, що значення 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>"));
}

Оскільки властивість CategoryIDtypeof 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, так, я запитав і відповів на це (як вікі спільноти) виключно з метою глубокого забиття багатьох інших подібних питань щодо SO, які залишаються без відповіді або не прийняті. Але я бачу, що помста виборців вже почалася - перший був менше 2 секунд після публікації - не вистачало часу, щоб навіть прочитати його, не кажучи вже про відповідь.

1
Я бачу. Є 100 запитань з тією ж проблемою. Зазвичай люди, які задають ці запитання, не роблять належного пошуку (або копіювали та вставляли існуючу відповідь слово за словом, але не працювали!) Тож я не впевнений, що це справді може допомогти. :) Чудово написано BTW.
Шию

@Stephen це неправильний спосіб, коли ти питаєш, а ти відповідаєш
Діліп Оганія

8
@DilipN, Що ви маєте в виду неправильний шлях ? Це насправді заохочується на SO. Ви повинні прочитати це і витратити деякий час на мета.

4
@DilipN, Тому що я буду використовувати його для позначення численних подібних питань як дублікатів, які або залишили без відповіді, або відповіли, але не прийняли, щоб їх можна було закрити (і так інші не витрачають час). Я також зробив вікі спільноти, щоб кожен міг її редагувати та вдосконалювати з часом.

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

У моєму випадку перший ідентифікатор у моєму списку дорівнював нулю, як тільки я змінив ідентифікатор на 1, він спрацював.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.