Mục ViewData có khóa 'XXX' thuộc loại 'System.Int32' nhưng phải thuộc loại 'IEnumerable <SelectListItem>'


113

Tôi có mô hình xem sau

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

và phương pháp điều khiển sau để tạo một Dự án mới và gán một 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
}

và trong khung cảnh

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

Chế độ xem hiển thị chính xác nhưng khi gửi biểu mẫu, tôi nhận được thông báo lỗi sau

InvalidOperationException: Mục ViewData có khóa 'CategoryID' thuộc loại 'System.Int32' nhưng phải thuộc loại 'IEnumerable <SelectListItem>'.

Lỗi tương tự cũng xảy ra khi sử dụng @Html.DropDownList()phương pháp và nếu tôi chuyển Danh sách chọn bằng cách sử dụng ViewBaghoặc ViewData.

Câu trả lời:


109

Lỗi có nghĩa là giá trị của CategoryList là null (và kết quả là DropDownListFor()phương thức mong đợi rằng tham số đầu tiên là kiểu IEnumerable<SelectListItem>).

Bạn không tạo đầu vào cho từng thuộc tính của mỗi SelectListItemtrong CategoryList(và bạn cũng không nên) vì vậy không có giá trị nào cho thuộc SelectListtính được đăng lên phương thức controller và do đó giá trị của model.CategoryListphương thức POST là null. Nếu bạn trả về dạng xem, trước tiên bạn phải gán lại giá trị của CategoryList, giống như bạn đã làm trong phương thức 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
}

Để giải thích các hoạt động bên trong (mã nguồn có thể xem ở đây )

Mỗi lần quá tải DropDownList()DropDownListFor()cuối cùng gọi phương thức sau

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

kiểm tra xem selectList(tham số thứ hai của @Html.DropDownListFor()) là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;
}

đến lượt nó gọi

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

đánh giá tham số đầu tiên của @Html.DropDownListFor()(trong trường hợp này 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>"));
}

Bởi vì thuộc tính CategoryIDlà typeof int, nó không thể được truyền đến IEnumerable<SelectListItem>và ngoại lệ được ném (được định nghĩa trong MvcResources.resxtệp là)

<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, Vâng, tôi đã hỏi và trả lời nó (với tư cách là wiki cộng đồng) hoàn toàn với mục đích đánh lừa nhiều câu hỏi tương tự khác trên SO mà vẫn chưa được trả lời hoặc không được chấp nhận. Nhưng tôi thấy những người bình chọn trả thù đã bắt đầu - lần đầu tiên chưa đầy 2 giây sau khi đăng - không đủ thời gian để đọc nó chứ đừng nói đến câu trả lời.

1
Tôi hiểu rồi. Có 100 câu hỏi với cùng một vấn đề. Thông thường những người hỏi những câu hỏi đó không thực hiện tìm kiếm thích hợp (hoặc họ sao chép và dán một câu trả lời hiện có từng từ một, nhưng không hoạt động!) Vì vậy, tôi không chắc điều này có thực sự hữu ích. :) BTW bằng văn bản độc đáo.
Shyju

@Stephen đây không phải là cách bạn đang hỏi và bạn đang trả lời
Dilip Oganiya

8
@DilipN, Ý bạn là gì không đúng cách ? Nó thực sự được khuyến khích trên SO. Bạn nên đọc và dành thời gian cho meta.

4
@DilipN, Bởi vì tôi sẽ sử dụng nó để đánh dấu nhiều câu hỏi tương tự dưới dạng trùng lặp hoặc chưa được trả lời hoặc đã trả lời nhưng không được chấp nhận để chúng có thể bị đóng lại (và để những người khác không lãng phí thời gian của họ). Tôi cũng đã biến nó thành wiki cộng đồng để mọi người có thể chỉnh sửa và cải thiện nó theo thời gian.

6

theo câu trả lời của stephens (user3559349) , điều này có thể hữu ích:

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

hoặc trong ProjectVM:

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

1

Rất có thể đã gây ra một số loại lỗi khi chuyển hướng đến trang của bạn và bạn không khởi chạy lại danh sách thả xuống của mô hình.

Đảm bảo rằng bạn khởi tạo trình đơn thả xuống của mình trong hàm tạo của mô hình hoặc mọi lúc trước khi bạn gửi mô hình đó đến trang.

Nếu không, bạn sẽ cần phải duy trì trạng thái của danh sách thả xuống thông qua túi chế độ xem hoặc thông qua trình trợ giúp giá trị ẩn.


0

Tôi đã gặp vấn đề tương tự, tôi nhận được ModelState không hợp lệ khi tôi cố gắng đăng biểu mẫu. Đối với tôi, điều này là do đặt CategoryId thành int, khi tôi thay đổi nó thành chuỗi ModelState hợp lệ và phương thức Create hoạt động như mong đợi.


0

OK, câu trả lời đóng hộp của người đăng đã giải thích gọn gàng lý do tại sao lỗi xảy ra, nhưng không phải là làm thế nào để nó hoạt động. Tôi không chắc đó thực sự là một câu trả lời, nhưng nó đã hướng tôi đi đúng hướng.

Tôi gặp phải vấn đề tương tự và tìm ra một cách khéo léo để giải quyết nó. Tôi sẽ cố gắng nắm bắt điều đó ở đây. Tuyên bố từ chối trách nhiệm - Tôi làm việc trên các trang web mỗi năm một lần và thực sự không biết mình đang làm gì trong hầu hết thời gian. Câu trả lời này không nên được coi là câu trả lời "chuyên gia", nhưng nó thực hiện công việc rất ít ...

Cho rằng tôi có một số đối tượng dữ liệu (rất có thể là Đối tượng truyền dữ liệu) mà tôi muốn sử dụng danh sách thả xuống để cung cấp các giá trị hợp lệ cho một trường, như sau:

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

Sau đó ViewModel trông như thế này:

public class MyDataObjectVM
{
  public int id;

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

Vấn đề thực sự ở đây, như @Stephen được mô tả một cách hùng hồn ở trên, là danh sách lựa chọn không được điền vào phương thức POST trong bộ điều khiển. Vì vậy, các phương thức bộ điều khiển của bạn sẽ giống như sau:

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

Đây là bạn có nó. Đây KHÔNG phải là mã làm việc, tôi sao chép / dán và chỉnh sửa để làm cho nó đơn giản, nhưng bạn có được ý tưởng. Nếu các thành viên dữ liệu trong cả mô hình dữ liệu gốc và mô hình chế độ xem dẫn xuất có cùng tên, UpdateModel () thực hiện một công việc tuyệt vời là chỉ điền đúng dữ liệu cho bạn từ các giá trị FormCollection.

Tôi đăng bài này ở đây để tôi có thể tìm thấy câu trả lời khi tôi chắc chắn gặp phải vấn đề này một lần nữa - hy vọng nó cũng sẽ giúp ích cho người khác.


0

Trong trường hợp của tôi, ID đầu tiên trong danh sách của tôi là 0, khi tôi thay đổi ID để bắt đầu từ 1, nó đã hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.