El elemento ViewData que tiene la clave 'XXX' es del tipo 'System.Int32' pero debe ser del tipo 'IEnumerable'

c# asp.net-mvc


Tengo el siguiente modelo de vista

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

y el siguiente método de controlador para crear un nuevo proyecto y asignar una 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
}

y en la vista

@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" />
}

La vista se muestra correctamente pero al enviar el formulario,recibo el siguiente mensaje de error

InvalidOperationException: el elemento ViewData que tiene la clave 'CategoryID' es del tipo 'System.Int32' pero debe ser del tipo 'IEnumerable <SelectListItem>'.

El mismo error se produce usando el @Html.DropDownList() método, y si paso la SelectList usando un ViewBag o ViewData .





Answer 1 user3559349


El error significa que el valor de CategoryList es nulo (y como resultado, el método DropDownListFor() espera que el primer parámetro sea del tipo IEnumerable<SelectListItem> ).

No está generando una entrada para cada propiedad de cada SelectListItem en CategoryList (y tampoco debería hacerlo), por lo que no se publican valores para SelectList en el método del controlador y, por lo tanto, el valor de model.CategoryList en el método POST es null . Si devuelve la vista, primero debe reasignar el valor de CategoryList , tal como lo hizo en el método 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
}

Para explicar el funcionamiento interno (el código fuente se puede ver aquí )

Cada sobrecarga de DropDownList() y DropDownListFor() finalmente llama al siguiente método

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
  string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
  IDictionary<string, object> htmlAttributes)

que comprueba si selectList (el segundo parámetro de @Html.DropDownListFor() ) es 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;
}

que a su vez llama

private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)

que evalúa el primer parámetro de @Html.DropDownListFor() (en este caso 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>"));
}

Debido a la propiedad CategoryID es typeof int , no se puede convertir a IEnumerable<SelectListItem> y se produce la excepción (que se define en el MvcResources.resx archivo como)

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



Answer 2 Omid-RH


según la respuesta de stephens (user3559349) , esto puede ser útil:

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

o en ProjectVM:

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



Answer 3 Gavin Rotty


Lo más probable es que haya causado algún tipo de error al redirigir a su página y que no vuelva a inicializar las listas desplegables de su modelo.

Asegúrate de inicializar los desplegables en el constructor del modelo o cada vez antes de enviar dicho modelo a la página.

De lo contrario,tendrá que mantener el estado de las listas desplegables ya sea a través de la bolsa de vista o a través de los ayudantes de valor oculto.




Answer 4 ardmark


Tuve el mismo problema,estaba recibiendo un ModelState inválido cuando intenté enviar el formulario.Para mí,esto fue causado por el ajuste de CategoryId a int,cuando lo cambié a string el ModelState era válido y el método Create funcionó como se esperaba.




Answer 5 DaveN59


OK, la respuesta enlatada del póster explicaba claramente por qué ocurrió el error, pero no cómo hacer que funcione. No estoy seguro de que sea realmente una respuesta, pero me señaló en la dirección correcta.

Me encontré con el mismo problema y encontré una forma ingeniosa de resolverlo.Trataré de capturar eso aquí.Descargo de responsabilidad-Trabajo en páginas web una vez al año o así y realmente no sé lo que estoy haciendo la mayor parte del tiempo.Esta respuesta no debe ser considerada de ninguna manera como una respuesta "experta",pero hace el trabajo con poco trabajo...

Dado que tengo algún objeto de datos (muy probablemente un Objeto de Transferencia de Datos)que quiero usar una lista desplegable para suministrar valores válidos para un campo,así:

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

Entonces el ViewModel se ve así:

public class MyDataObjectVM
{
  public int id;

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

El verdadero problema aquí,como @Stephen tan elocuentemente descrito arriba,es que la lista de selección no está poblada en el método POST en el controlador.Así que los métodos del controlador se verían así:

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

Ahí lo tienes.Este NO es un código de trabajo,lo copié y lo edité para hacerlo simple,pero ya te haces una idea.Si los miembros de datos tanto en el modelo de datos original como en el modelo de vista derivado tienen el mismo nombre,UpdateModel()hace un trabajo impresionante de rellenar los datos correctos para ti a partir de los valores de FormCollection.

Estoy publicando esto aquí para poder encontrar la respuesta cuando inevitablemente me vuelva a encontrar con este tema...espero que ayude a alguien más también.