【问题标题】:The ViewData item that has the key 'ShelfId' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'具有键“ShelfId”的 ViewData 项的类型为“System.Int32”,但必须为“IEnumerable<SelectListItem>”类型
【发布时间】:2015-03-18 21:00:10
【问题描述】:

问题

我在应用程序的其他地方非常相似地使用以下代码,但它不起作用。我完全被难住了。

The ViewData item that has the key 'ShelfId' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'

这是在 post 方法期间抛出的。我的模型状态无效。

代码

模型

架子

public class Shelf 
{
      [Key]
      public int ShelfId 

      [Display(Name = "Shelf Id")]
      [Required]
      public string ShelfName

      public virtual List<Book> Books {get; set;}

}

图书

public class Book 
{
      public int BookId 

      [Required]
      [StrengthLength(160, MinimumLength = 8)]
      public string BookName

      public int ShelfId

      public Shelf shelf {get; set;}


}

控制器

// GET: Units/Create
        public async Task<IActionResult> Create()
        {


            var shelves = await _db.Shelves.OrderBy(q => q.Name).ToListAsync();
            ViewBag.SelectedShelves = new SelectList(shelves, "ShelfId", "Name");
            return View();
        }

        // POST: Units/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(Book book)
        {
            book.CreatedBy = User.Identity.GetUserName();
            book.Created = DateTime.UtcNow;
            book.UpdatedBy = User.Identity.GetUserName();
            book.Updated = DateTime.UtcNow;

            if (ModelState.IsValid)
            {
                db.Units.Add(unit);
                await db.SaveChangesAsync();
                return RedirectToAction("Index");
            }

            return View(book);
        }

查看

@model AgentInventory.Models.Book

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Create Unit</title>
</head>
<body>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal well bs-component" style="margin-top:20px">
        <h4>Unit</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            <div class="control-label col-md-2">Room</div>
            <div class="col-md-10">
                @Html.DropDownListFor(model => model.ShelfId, (SelectList)ViewBag.SelectedShelves, "All", new { @class = "form-control" })
             </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.BookName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.BookName, "", new { @class = "text-danger" }
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

尝试

我试过了:

  • 在创建视图中添加@Html.HiddenFor(model=&gt;model.ShelfId),但这不起作用。
  • 我在 stackoverflow 上查看过类似的问题,但没有一个修复对我有用。 (IE - hiddenfor,不同种类的选择列表)

由于我是 MVC 框架的新手,我将不胜感激任何帮助。我不明白为什么这段代码适用于其他两种模型(建筑物和房间),但不适用于我目前的两种模型?有点奇怪。

PS - 有没有办法在不使用 viewbag 的情况下轻松做到这一点?

【问题讨论】:

  • @CodeCaster 当我这样做时,它会直接返回到 Create 视图而没有错误。我想我需要仔细看看无效状态模型的调试器。我怀疑那里有什么东西搞砸了,希望会暴露出来。
  • 是的,因为当您返回视图时,SelectListnull。返回视图时需要在POST方法中使用ViewBag.SelectedShelves = new SelectList(shelves, "ShelfId", "Name");
  • 什么意思,你得到了创建视图?这就是你应该得到的。如果ModelState 无效,它会返回视图以便用户更正错误。你期望发生什么
  • 那么您的Book 模型显然还有其他属性,例如您未显示的CreatedBy。毫无疑问,其中一些具有验证属性,因此 ModelState 无效。您确实需要开始使用视图模型来表示您想要显示/编辑的内容。

标签: asp.net-mvc asp.net-core viewbag visual-studio-2015 asp.net-core-mvc


【解决方案1】:

错误的原因是在POST方法中当你返回视图时,ViewBag.SelectedShelves的值为null,因为你没有设置它(就像你在get方法中所做的那样。我建议你在一个可以从 GET 和 POST 方法调用的私有方法

private void ConfigureViewModel(Book book)
{
  var shelves = await _db.Shelves.OrderBy(q => q.Name).ToListAsync();
  // Better to have a view model with a property for the SelectList
  ViewBag.SelectedShelves = new SelectList(shelves, "ShelfId", "Name");
}

然后在控制器中

public async Task<IActionResult> Create()
{
  // Always better to initialize a new object and pass to the view
  Book model = new Book();
  ConfigureViewModel(model)
  return View(model);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Book book)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(book)
    return View(book);
  }
  // No point setting these if the model is invalid
  book.CreatedBy = User.Identity.GetUserName();
  book.Created = DateTime.UtcNow;
  book.UpdatedBy = User.Identity.GetUserName();
  book.Updated = DateTime.UtcNow;
  // Save and redirect
  db.Units.Add(unit);
  await db.SaveChangesAsync();
  return RedirectToAction("Index");
}

请注意,您的 Book 类仅包含字段,不包含属性(没有 { get; set; }),因此不会设置任何属性并且模型将始终无效,因为 BookName 具有 RequiredStringLength 属性。

此外,您还没有显示模型中的所有属性(例如,您有CreatedByCreated 等,ModelState 也可能无效,因为您只为少数几个属性生成控件。如果任何其他属性包含验证属性,则ModelState 将无效。要处理此问题,您需要创建一个仅包含要显示编辑的属性的视图模型。

public class BookVM
{
  public int Id { get; set; }
  [Required]
  [StrengthLength(160, MinimumLength = 8)]
  public string Name { get; set; }
  public int SelectedShelf { get; set; }
  public SelectList ShelfList { get; set; }
}

然后修改私有方法将SelectList分配给视图模型(不是ViewBag,并在控制器方法中,将BookVM的新实例传递给视图,并回传给

public async Task<IActionResult> Create(BookVM model)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(model)
    return View(model);
  }
  // Initialize a new Book and set the properties from the view model
}

【讨论】:

  • 这个答案是一件艺术品。我不能足够支持这个答案。此外,您对错误发生原因的解释非常棒。
猜你喜欢
  • 2014-04-17
  • 2014-10-02
  • 2012-01-25
  • 2013-10-22
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多