Item ViewData yang memiliki kunci 'XXX' berjenis 'System.Int32' tetapi harus berjenis 'IEnumerable <SelectListItem>'

113

Saya memiliki model tampilan berikut

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

dan metode pengontrol berikut untuk membuat Proyek baru dan menetapkan 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
}

dan dalam tampilan

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

Tampilan ditampilkan dengan benar tetapi ketika mengirimkan formulir, saya mendapatkan pesan kesalahan berikut

InvalidOperationException: Item ViewData yang memiliki kunci 'CategoryID' berjenis 'System.Int32' tetapi harus berjenis 'IEnumerable <SelectListItem>'.

Kesalahan yang sama terjadi dengan @Html.DropDownList()metode ini, dan jika saya meneruskan SelectList menggunakan ViewBagatau ViewData.


sumber

Jawaban:

109

Kesalahan berarti nilai dari CategoryList adalah null (dan sebagai hasilnya DropDownListFor()metode mengharapkan bahwa parameter pertama adalah tipe IEnumerable<SelectListItem>).

Anda tidak menghasilkan input untuk setiap properti dari masing-masing SelectListItemin CategoryList(dan Anda juga tidak seharusnya) sehingga tidak ada nilai untuk SelectListyang diposting ke metode kontroler, dan oleh karena itu nilai model.CategoryListdalam metode POST adalah null. Jika Anda mengembalikan tampilan, Anda harus menetapkan kembali nilai dari CategoryList, seperti yang Anda lakukan di metode 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
}

Untuk menjelaskan cara kerja bagian dalam (source code bisa dilihat disini )

Setiap kelebihan beban DropDownList()dan DropDownListFor()akhirnya memanggil metode berikut

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

yang memeriksa apakah selectList(parameter kedua dari @Html.DropDownListFor()) adalahnull

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

yang pada gilirannya memanggil

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

yang mengevaluasi parameter pertama @Html.DropDownListFor()(dalam hal ini 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>"));
}

Karena properti CategoryIDadalah typeof int, itu tidak bisa dilemparkan ke IEnumerable<SelectListItem>dan pengecualian dilempar (yang didefinisikan dalam MvcResources.resxfile sebagai)

<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>
pengguna3559349
sumber
8
@Shyju, Ya, saya bertanya dan menjawabnya (sebagai wiki komunitas) murni untuk tujuan menipu banyak pertanyaan serupa lainnya tentang SO yang tetap tidak terjawab atau tidak diterima. Tapi saya lihat para pemilih balas dendam sudah dimulai - yang pertama kurang dari 2 detik setelah posting - tidak cukup waktu bahkan untuk membacanya apalagi jawabannya.
1
Saya melihat. Ada 100 pertanyaan dengan masalah yang sama. Biasanya orang yang mengajukan pertanyaan tersebut tidak melakukan pencarian yang tepat (atau mereka menyalin dan menempel jawaban kata demi kata yang ada, tetapi tidak berhasil!) Jadi saya tidak yakin ini mungkin benar-benar membantu. :) BTW ditulis dengan baik.
Shyju
@Stephen ini bukan cara yang benar kamu bertanya dan kamu menjawab
Dilip Oganiya
8
@DilipN, Apa maksudmu bukan dengan cara yang benar ? Ini sebenarnya didorong pada SO. Anda harus membaca ini dan meluangkan waktu untuk meta.
4
@DilipN, Karena saya akan menggunakannya untuk menandai banyak pertanyaan serupa sebagai duplikat yang entah tidak dijawab, atau dijawab tetapi tidak diterima sehingga dapat ditutup (dan agar orang lain tidak membuang waktu mereka). Saya juga membuatnya menjadi wiki komunitas sehingga siapa pun dapat mengedit dan memperbaikinya seiring waktu.
6

menurut jawaban stephens (user3559349) , ini bisa berguna:

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

atau di ProjectVM:

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

Paling Mungkin Menyebabkan semacam kesalahan saat mengarahkan ke halaman Anda dan Anda tidak menginisialisasi daftar tarik-turun model Anda lagi.

Pastikan Anda menginisialisasi drop-down baik di konstruktor model atau setiap kali sebelum Anda mengirim model tersebut ke halaman.

Jika tidak, Anda perlu mempertahankan status daftar drop-down baik melalui view bag atau melalui hidden value helers.

Gavin Rotty
sumber
0

Saya memiliki masalah yang sama, saya mendapatkan ModelState yang tidak valid saat mencoba mengirim formulir. Bagi saya, ini disebabkan oleh pengaturan CategoryId ke int, ketika saya mengubahnya menjadi string ModelState itu valid dan metode Buat bekerja seperti yang diharapkan.

tanda tanda
sumber
0

Oke, jawaban terekam poster dengan rapi menjelaskan mengapa kesalahan terjadi, tetapi tidak bagaimana membuatnya bekerja. Saya tidak yakin itu benar-benar jawaban, tetapi itu mengarahkan saya ke arah yang benar.

Saya mengalami masalah yang sama dan menemukan cara yang apik untuk mengatasinya. Saya akan mencoba menangkapnya di sini. Penafian - Saya mengerjakan halaman web sekali setahun atau lebih dan benar-benar tidak tahu apa yang saya lakukan sebagian besar waktu. Jawaban ini sama sekali tidak boleh dianggap sebagai jawaban "ahli", tetapi berhasil dengan sedikit kerja ...

Mengingat bahwa saya memiliki beberapa objek data (kemungkinan besar Objek Transfer Data) yang ingin saya gunakan daftar drop-down untuk memberikan nilai yang valid untuk suatu bidang, seperti:

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

Kemudian ViewModel terlihat seperti ini:

public class MyDataObjectVM
{
  public int id;

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

Masalah sebenarnya di sini, seperti yang dijelaskan @Stephen dengan fasih di atas, adalah daftar pilihan tidak diisi pada metode POST di pengontrol. Jadi metode pengontrol Anda akan terlihat seperti ini:

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

Itu dia. Ini BUKAN kode yang berfungsi, saya salin / tempel dan edit untuk membuatnya sederhana, tetapi Anda mendapatkan idenya. Jika anggota data dalam model data asli dan model tampilan turunan memiliki nama yang sama, UpdateModel () melakukan pekerjaan luar biasa dengan hanya mengisi data yang tepat untuk Anda dari nilai FormCollection.

Saya memposting ini di sini sehingga saya dapat menemukan jawabannya ketika saya pasti mengalami masalah ini lagi - semoga ini akan membantu orang lain juga.

DaveN59
sumber
0

Dalam kasus saya, ID pertama dalam daftar saya adalah nol, begitu saya mengubah ID menjadi mulai dari 1, itu berhasil.

JayKayOf4
sumber