รายการ ViewData ที่มีคีย์ 'XXX' เป็นประเภท 'System.Int32' แต่ต้องเป็นประเภท 'IEnumerable <SelectListItem>'


114

ฉันมีโมเดลมุมมองต่อไปนี้

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

และวิธีการควบคุมต่อไปนี้เพื่อสร้างโครงการใหม่และกำหนดไฟล์ 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.

คำตอบ:


110

ข้อผิดพลาดหมายความว่าค่าCategoryList เป็นโมฆะ (และด้วยเหตุDropDownListFor()นี้เมธอดจึงคาดว่าพารามิเตอร์ตัวแรกเป็นประเภทIEnumerable<SelectListItem>)

คุณยังไม่ได้สร้างการป้อนข้อมูลสถานที่ให้บริการของแต่ละSelectListItemในCategoryList(และไม่ควรคุณ) เพื่อให้ค่าสำหรับไม่มีSelectListการโพสต์ไปยังวิธีการควบคุมและดังนั้นค่าของในโพสต์วิธีการคือmodel.CategoryList 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>"));
}

เนื่องจากคุณสมบัติCategoryIDเป็น typeof 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 ที่เขียนอย่างสวยงาม
Shyju

@Stephen นี่ไม่ใช่วิธีที่ถูกต้องที่คุณกำลังถามและคุณกำลังตอบ
Dilip Oganiya

8
@DilipN คุณหมายถึงอะไรไม่ใช่ทางที่ถูกต้อง ? มันได้รับการสนับสนุนจาก SO คุณควรอ่านสิ่งนี้และใช้เวลากับ meta

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

ตกลงคำตอบสำเร็จรูปของผู้โพสต์อธิบายอย่างละเอียดว่าเหตุใดจึงเกิดข้อผิดพลาด แต่ไม่สามารถใช้งานได้อย่างไร ฉันไม่แน่ใจว่านั่นเป็นคำตอบจริงๆ แต่มันชี้ให้ฉันไปในทิศทางที่ถูกต้อง

ฉันพบปัญหาเดียวกันและพบวิธีง่ายๆในการแก้ไข ผมจะพยายามจับภาพตรงนี้ ข้อจำกัดความรับผิดชอบ - ฉันทำงานบนหน้าเว็บปีละครั้งและไม่รู้จริงๆว่าฉันทำอะไรอยู่เกือบตลอดเวลา คำตอบนี้ไม่ควรถือเป็นคำตอบของ "ผู้เชี่ยวชาญ" แต่ได้ผลเพียงเล็กน้อย ...

เนื่องจากฉันมีออบเจ็กต์ข้อมูลบางอย่าง (ซึ่งน่าจะเป็น Data Transfer Object) ที่ฉันต้องการใช้รายการแบบเลื่อนลงเพื่อระบุค่าที่ถูกต้องสำหรับฟิลด์ดังนี้:

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

ในกรณีของฉัน ID แรกในรายการของฉันเป็นศูนย์เมื่อฉันเปลี่ยน ID ให้เริ่มจาก 1 มันก็ใช้งานได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.