L'elemento ViewData che ha la chiave "XXX" è di tipo "System.Int32" ma deve essere di tipo "IEnumerable <SelectListItem>"


113

Ho il seguente modello di visualizzazione

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

e il seguente metodo del controller per creare un nuovo progetto e assegnare un file 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
}

e nella 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 visualizzazione viene visualizzata correttamente ma quando si invia il modulo, viene visualizzato il seguente messaggio di errore

InvalidOperationException: l'elemento ViewData con la chiave "CategoryID" è di tipo "System.Int32" ma deve essere di tipo "IEnumerable <SelectListItem>".

Lo stesso errore si verifica utilizzando il @Html.DropDownList()metodo e se passo il SelectList utilizzando un ViewBago ViewData.

Risposte:


109

L'errore significa che il valore di CategoryList è nullo (e di conseguenza il DropDownListFor()metodo si aspetta che il primo parametro sia di tipo IEnumerable<SelectListItem>).

Non stai generando un input per ogni proprietà di ciascuno SelectListItemin CategoryList(e né dovresti) quindi nessun valore per il SelectListviene inviato al metodo del controller, e quindi il valore di model.CategoryListnel metodo POST è null. Se restituisci la visualizzazione, devi prima riassegnare il valore di CategoryList, proprio come hai fatto nel metodo 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
}

Per spiegare il funzionamento interno (il codice sorgente può essere visto qui )

Ogni overload di DropDownList()ed DropDownListFor()eventualmente chiama il metodo seguente

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

che controlla se il selectList(il secondo parametro di @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;
}

che a sua volta chiama

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

che valuta il primo parametro di @Html.DropDownListFor()(in questo 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>"));
}

Poiché la proprietà CategoryIDè typeof int, non può essere eseguito il cast IEnumerable<SelectListItem>e viene generata l'eccezione (definita nel MvcResources.resxfile come)

<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, Sì, ho chiesto e risposto (come wiki della comunità) puramente allo scopo di ingannare molte altre domande simili su SO che rimangono senza risposta o non accettate. Ma vedo che la vendetta degli elettori è già iniziata - la prima è stata meno di 2 secondi dopo la pubblicazione - non abbastanza tempo per leggerla, figuriamoci la risposta.

1
Vedo. Ci sono centinaia di domande con lo stesso problema. Di solito le persone che pongono queste domande non fanno una ricerca adeguata (o hanno copiato e incollato parola per parola una risposta esistente, ma non ha funzionato!) Quindi non sono sicuro che questo potrebbe davvero aiutare. :) Ben scritto BTW.
Shyju

@Stephen questo non è il modo giusto in cui stai chiedendo e stai rispondendo
Dilip Oganiya

8
@DilipN, cosa intendi non nel modo giusto ? In realtà è incoraggiato su SO. Si consiglia di leggere questo e trascorrere del tempo su meta.

4
@DilipN, perché lo userò per contrassegnare numerose domande simili come duplicati che sono stati lasciati senza risposta o hanno ricevuto risposta ma non sono stati accettati in modo che possano essere chiusi (e così gli altri non perdono tempo). L'ho anche reso un wiki della comunità in modo che chiunque possa modificarlo e migliorarlo nel tempo.

6

secondo la risposta di stephens (user3559349) , questo può essere utile:

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

o in ProjectVM:

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

1

Molto probabilmente ha causato una sorta di errore nel reindirizzamento alla tua pagina e non hai inizializzato nuovamente gli elenchi a discesa del tuo modello.

Assicurati di inizializzare i menu a discesa nel costruttore del modello o ogni volta prima di inviare detto modello alla pagina.

In caso contrario, sarà necessario mantenere lo stato degli elenchi a discesa tramite il sacchetto di visualizzazione o tramite gli helper dei valori nascosti.


0

Ho avuto lo stesso problema, stavo ottenendo un ModelState non valido quando ho provato a pubblicare il modulo. Per me, questo è stato causato dall'impostazione di CategoryId su int, quando l'ho modificato in stringa ModelState era valido e il metodo Create ha funzionato come previsto.


0

OK, la risposta predefinita del poster ha spiegato chiaramente perché si è verificato l'errore, ma non come farlo funzionare. Non sono sicuro che sia davvero una risposta, ma mi ha indirizzato nella giusta direzione.

Mi sono imbattuto nello stesso problema e ho trovato un modo semplice per risolverlo. Proverò a catturarlo qui. Disclaimer - Lavoro sulle pagine web una volta all'anno e non so davvero cosa sto facendo la maggior parte del tempo. Questa risposta non dovrebbe in alcun modo essere considerata una risposta "esperta", ma fa il lavoro con poco lavoro ...

Dato che ho un oggetto dati (molto probabilmente un oggetto di trasferimento dati) che voglio utilizzare un elenco a discesa per fornire valori validi per un campo, in questo modo:

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

Quindi il ViewModel ha questo aspetto:

public class MyDataObjectVM
{
  public int id;

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

Il vero problema qui, come @Stephen così eloquentemente descritto sopra, è che l'elenco di selezione non è popolato sul metodo POST nel controller. Quindi i metodi del tuo controller sarebbero simili a questo:

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

Ecco qua. Questo NON è codice funzionante, ho copiato / incollato e modificato per renderlo semplice, ma hai capito. Se i membri dei dati sia nel modello di dati originale che nel modello di vista derivato hanno lo stesso nome, UpdateModel () fa un ottimo lavoro nel compilare solo i dati giusti per te dai valori di FormCollection.

Lo sto postando qui in modo da poter trovare la risposta quando inevitabilmente mi imbatterò di nuovo in questo problema - si spera che possa aiutare anche qualcun altro.


0

Nel mio caso il primo ID nella mia lista era zero, una volta cambiato l'ID per iniziare da 1, ha funzionato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.