具有键 'ShelfId' 的 ViewData 项是 'System.Int32' 类型,但必须是 'IEnumerable<SelectListItem>' 类型

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

问题

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

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>

尝试次数

我试过了:

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

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

错误的原因是在POST方法中当你return视图时,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 class 仅包含字段,不包含属性(没有 { 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的新实例传递给视图, post 回到

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
}