Das ViewData-Element mit dem Schlüssel 'XXX' ist vom Typ 'System.Int32', muss jedoch vom Typ 'IEnumerable <SelectListItem>' sein.

112

Ich habe das folgende Ansichtsmodell

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

und die folgende Controller-Methode, um ein neues Projekt zu erstellen und a zuzuweisen 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
}

und in der Ansicht

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

Die Ansicht wird korrekt angezeigt, aber beim Senden des Formulars wird die folgende Fehlermeldung angezeigt

InvalidOperationException: Das ViewData-Element mit dem Schlüssel 'CategoryID' ist vom Typ 'System.Int32', muss jedoch vom Typ 'IEnumerable <SelectListItem>' sein.

Der gleiche Fehler tritt bei der @Html.DropDownList()Methode auf, und wenn ich die SelectList mit einem ViewBagoder übergebe ViewData.


quelle

Antworten:

108

Der Fehler bedeutet, dass der Wert von CategoryList null ist (und als Ergebnis DropDownListFor()erwartet die Methode, dass der erste Parameter vom Typ ist IEnumerable<SelectListItem>).

Sie generieren keine Eingabe für jede Eigenschaft von jedem SelectListItemin CategoryList(und sollten es auch nicht), daher werden keine Werte für SelectListin die Controller-Methode gebucht, und daher ist der Wert von model.CategoryListin der POST-Methode null. Wenn Sie die Ansicht zurückgeben, müssen Sie zuerst den Wert von neu zuweisen CategoryList, genau wie bei der GET-Methode.

public ActionResult Create(ProjectVM model)
{
    if (!ModelState.IsValid)
    {
        model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
        return View(model);
    }
    // Save and redirect
}

Um das Innenleben zu erklären (der Quellcode ist hier zu sehen )

Jede Überladung von DropDownList()und DropDownListFor()ruft schließlich die folgende Methode auf

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

Dies prüft, ob der selectList(zweite Parameter von @Html.DropDownListFor()) istnull

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

was wiederum ruft

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

welches den ersten Parameter von @Html.DropDownListFor()(in diesem Fall CategoryID) auswertet

....
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>"));
}

Da die Eigenschaft CategoryIDtypeof ist int, kann sie nicht umgewandelt werden IEnumerable<SelectListItem>und die Ausnahme wird ausgelöst (die in der MvcResources.resxDatei als definiert ist).

<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>
user3559349
quelle
8
@Shyju, Ja, ich habe es gefragt und beantwortet (als Community-Wiki), nur um viele andere ähnliche Fragen zu SO zu täuschen, die unbeantwortet oder nicht akzeptiert bleiben. Aber ich sehe, dass die Rachewähler bereits begonnen haben - der erste war weniger als 2 Sekunden nach dem Posten - nicht genug Zeit, um ihn überhaupt zu lesen, geschweige denn die Antwort.
1
Aha. Es gibt Hunderte von Fragen mit dem gleichen Problem. Normalerweise suchen die Leute, die diese Fragen stellen, nicht richtig (oder sie haben eine vorhandene Antwort Wort für Wort kopiert und eingefügt, aber es hat nicht funktioniert!). Ich bin mir also nicht sicher, ob dies wirklich helfen könnte. :) Schön geschrieben übrigens.
Shyju
@ Stephen dies ist nicht der richtige Weg, den Sie fragen und Sie antworten
Dilip Oganiya
8
@ DilipN, was meinst du nicht richtig ? Es ist tatsächlich auf SO ermutigt. Sie sollten lesen diese und einige Zeit auf Meta verbringen.
4
@DilipN, weil ich damit zahlreiche ähnliche Fragen als Duplikate markieren werde, die entweder unbeantwortet gelassen oder beantwortet, aber nicht akzeptiert wurden, damit sie geschlossen werden können (und andere ihre Zeit nicht verschwenden). Ich habe es auch zu einem Community-Wiki gemacht, damit jeder es im Laufe der Zeit bearbeiten und verbessern kann.
6

Laut der Antwort von stephens (user3559349) kann dies nützlich sein:

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

oder in ProjectVM:

public class ProjectVM
{
    public ProjectVM()
    {
        CategoryList = new List<SelectListItem>();
    }
    ...
}
Omid-RH
quelle
1

Höchstwahrscheinlich hat es einen Fehler verursacht, der auf Ihre Seite umgeleitet wurde, und Sie haben die Dropdown-Listen Ihres Modells nicht erneut initialisiert.

Stellen Sie sicher, dass Sie Ihre Dropdowns entweder im Konstruktor des Modells oder jedes Mal initialisieren, bevor Sie das Modell an die Seite senden.

Andernfalls müssen Sie den Status der Dropdown-Listen entweder über die Ansichtstasche oder über die Hilfsprogramme für versteckte Werte beibehalten.

Gavin Rotty
quelle
0

Ich hatte das gleiche Problem, ich bekam einen ungültigen ModelState, als ich versuchte, das Formular zu posten. Für mich wurde dies dadurch verursacht, dass CategoryId auf int gesetzt wurde. Als ich es in string änderte, war der ModelState gültig und die Create-Methode funktionierte wie erwartet.

ardmark
quelle
0

OK, die eingemachte Antwort des Posters erklärte genau, warum der Fehler aufgetreten ist, aber nicht, wie man ihn zum Laufen bringt . Ich bin mir nicht sicher, ob das wirklich eine Antwort ist, aber es hat mich in die richtige Richtung gelenkt.

Ich bin auf dasselbe Problem gestoßen und habe einen cleveren Weg gefunden, es zu lösen. Ich werde versuchen, das hier festzuhalten. Haftungsausschluss - Ich arbeite ungefähr einmal im Jahr an Webseiten und weiß wirklich nicht, was ich die meiste Zeit mache. Diese Antwort sollte in keiner Weise als "Experten" -Antwort betrachtet werden, aber sie erledigt den Job mit wenig Arbeit ...

Da ich ein Datenobjekt habe (höchstwahrscheinlich ein Datenübertragungsobjekt), möchte ich eine Dropdown-Liste verwenden, um gültige Werte für ein Feld bereitzustellen, z. B.:

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

Dann sieht das ViewModel folgendermaßen aus:

public class MyDataObjectVM
{
  public int id;

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

Das eigentliche Problem hier ist, wie @Stephen oben so eloquent beschrieben hat, dass die Auswahlliste nicht mit der POST-Methode in der Steuerung gefüllt ist. Ihre Controller-Methoden würden also folgendermaßen aussehen:

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

Hier hast du es. Dies ist KEIN funktionierender Code. Ich habe ihn kopiert / eingefügt und bearbeitet, um es einfach zu machen, aber Sie haben die Idee. Wenn die Datenelemente sowohl im ursprünglichen Datenmodell als auch im abgeleiteten Ansichtsmodell denselben Namen haben, kann UpdateModel () aus den FormCollection-Werten genau die richtigen Daten für Sie eingeben.

Ich poste dies hier, damit ich die Antwort finden kann, wenn ich unweigerlich wieder auf dieses Problem stoße - hoffentlich hilft es auch jemand anderem.

DaveN59
quelle
0

In meinem Fall war die erste ID in meiner Liste Null. Nachdem ich die ID von 1 geändert hatte, funktionierte sie.

JayKayOf4
quelle