キー「XXX」を持つViewDataアイテムはタイプ「System.Int32」ですが、タイプは「IEnumerable」である必要があります」

c# asp.net-mvc


私は以下のようなビューモデルを持っています。

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:キー「CategoryID」を持つViewDataアイテムのタイプは「System.Int32」ですが、タイプは「IEnumerable <SelectListItem>」である必要があります。

@Html.DropDownList() メソッドを使用した場合、および ViewBag または ViewData を使用してSelectListを渡した場合も、同じエラーが発生します。





Answer 1 user3559349


エラーは、 CategoryList の値 がnull であることを意味します(その結果、 DropDownListFor() メソッドは、最初のパラメーターが IEnumerable<SelectListItem> 型であると想定しています)。

あなたはそれぞれの各プロパティの入力生成されていない SelectListItemCategoryList を(しても、あなたがすべき)のための値よう SelectList のは、コントローラのメソッドに掲載されていないので、の値 model.CategoryList POSTメソッドでは、 null を。ビューを返す場合は、GETメソッドの場合と同様に、まず CategoryList の値を再割り当てする必要があります。

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() 2番目のパラメーター)が 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>



Answer 2 Omid-RH


stephens(user3559349)の回答によれば、これは便利です:

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

またはProjectVM内の

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



Answer 3 Gavin Rotty


ほとんどの場合、あなたのページにリダイレクトするために何らかのエラーが発生し、モデルのドロップダウンリストを再度初期化していないことが原因です。

モデルのコンストラクタで、またはモデルをページに送信する前に毎回、ドロップダウンを初期化するようにしてください。

そうでなければ、ドロップダウンリストの状態を維持するためには、ビューバッグまたは非表示の値ヘルパーを使用する必要があります。




Answer 4 ardmark


私も同じ問題を抱えていました。フォームを投稿しようとしたときに無効なModelStateを取得していました。私の場合、これはCategoryIdをintに設定したことが原因でしたが、それを文字列に変更するとModelStateは有効になり、Createメソッドは期待通りに動作しました。




Answer 5 DaveN59


わかりました。投稿者の返信定型文は、エラーが発生した理由をきちんと説明しましたが、それを機能させる方法は説明していません。それが本当に答えかどうかはわかりませんが、正しい方向に向けられました。

私も同じ問題にぶつかりましたが、それを解決するための巧妙な方法を見つけました。私はここでそれをキャプチャしようとします。免責事項-私は年に一度かそこらのウェブページで仕事をしていて、本当に自分が何をしているのかほとんどわかりません。この答えは、決して "専門家 "の答えと見なされるべきではありませんが、それは少しの作業で仕事をしています...

あるデータオブジェクト(おそらくデータ転送オブジェクト)を持っていて、ドロップダウンリストを使ってフィールドに有効な値を与えたいと思っています。

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 メソッドに select リストが入力されていないことです。そのため、コントローラのメソッドは次のようになります。

// 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 の値から適切なデータを埋めてくれる素晴らしい仕事をしてくれます。

私はここにこれを投稿しているので、私は必然的に再びこの問題に遭遇したときに答えを見つけることができます-うまくいけば、それが同様に誰か他の人の助けになることを願っています。