キー「XXX」を持つViewDataアイテムはタイプ「System.Int32」ですが、タイプ「IEnumerable <SelectListItem>」でなければなりません


112

次のビューモデルがあります

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またはを使用してSelectListを渡した場合ViewData

回答:


108

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

あなたはそれぞれの各プロパティの入力が発生していないSelectListItemCategoryList(ともあなたがすべき)ようには値がSelectListコントローラメソッドに掲載されていないので、の値がmodel.CategoryListPOSTメソッドではあります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(の2番目のパラメータ@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、はい、私はそれについて(コミュニティwikiとして)質問し、回答しました。SOに関する他の多くの同様の質問をダメハンマーで処理するためであり、未回答または未承認のままです。しかし、私は復讐の有権者がすでに始まっていることを確認します-最初の投票者は投稿後2秒未満でした-答えを言うまでもなくそれを読むのに十分な時間もありませんでした。

1
そうですか。同じ問題に関する質問が100あります。通常、これらの質問をする人は適切な検索を行わない(または既存の回答を単語ごとにコピーして貼り付けたが機能しなかった)ので、これが本当に役立つかどうかはわかりません。:)うまく書かれたところで。
Shyju

@Stephenこれは正しい方法ではありません。質問し、回答します
Dilip Oganiya '19

8
@DilipN、あなたは正しい方法とはどういう意味ですか?そのSOで実際に奨励されています。これを読んで、メタに少し時間をかける必要があります。

4
@DilipN、私はそれを使用して、多数の同様の質問を、未回答のままにされているか、回答されているが受け入れられないように重複としてマークするので、それらを閉じることができます(他の人が時間を無駄にしないようにします)。また、コミュニティのwikiを作成したので、だれでも時間をかけて編集および改善できます。

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

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

私は同じ問題に遭遇し、それを解決するための巧妙な方法を見つけました。ここでそれをキャプチャしようとします。免責事項-私は1年に1度程度Webページに取り組んでおり、ほとんどの場合何をしているのか本当にわかりません。この回答は決して「専門家」の回答と見なされるべきではありませんが、ほとんど作業をせずに仕事をします...

以下のように、ドロップダウンリストを使用してフィールドに有効な値を提供したいデータオブジェクト(おそらくデータ転送オブジェクト)があるとします。

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.