具有键 '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>
尝试次数
我试过了:
- 在创建视图中添加
@Html.HiddenFor(model=>model.ShelfId)
,但这没有用。
- 我在 Whosebug 上查看过类似的问题,但 none 的修复对我有用。 (IE - hiddenfor,不同类型的选择列表)
由于我是 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
具有 Required
和 StringLength
属性。
此外,您还没有显示模型中的所有属性(例如,您有 CreatedBy
、Created
等,并且 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
}
问题
我在我的应用程序的其他地方非常相似地使用了以下代码,但它不起作用。我完全被难住了。
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=>model.ShelfId)
,但这没有用。 - 我在 Whosebug 上查看过类似的问题,但 none 的修复对我有用。 (IE - hiddenfor,不同类型的选择列表)
由于我是 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
具有 Required
和 StringLength
属性。
此外,您还没有显示模型中的所有属性(例如,您有 CreatedBy
、Created
等,并且 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
}