解决联动DropdownList值存入数据库时的InvalidOperationException错误
问题:动态加载DropdownList选中值无法保存到数据库,且视图报错
错误信息
InvalidOperationException: 不存在键为'StockItems[0].StockId'的'IEnumerable'类型ViewData项
现有代码
Controller代码
public IActionResult Create() { List<Category> categories = _dbcontext.Category.ToList(); ViewBag.ListCategories = new SelectList(categories, "CategoryId", "CategoryName"); Invoice invoice= new Invoice(); invoice.StockItems.Add(new StockItems() { StockItemsId = 1}); return View(decharge); // 此处存在笔误,应为return View(invoice); } [HttpPost] public IActionResult Create(Invoice invoice) { if (invoice!= null) { _dbcontext.Invoice.Add(invoice); _dbcontext.SaveChanges(); return RedirectToAction("Index"); } return View(); } [HttpPost] public IActionResult GetStockByCat(int CatId) { List<Stock> stocks = new List<Stock>(); stocks = _dbcontext.Stock.Where(s => s.CategoryId == CatId).ToList(); SelectList stocksList = new SelectList(stocks, "StockId", "Name"); return Json(stocksList); }
Create视图代码
<table class="table table-bordered table-hover"> <thead> <tr> .... </tr> </thead> <tbody> @for (int i = 0; i < Model.StockItems.Count; i++) { <tr> <td>@Html.DropDownList("Category", ViewBag.ListCategories, null, new { @class = "form-control", @onchange = "GetStocks(this)" })</td> <td>@Html.DropDownListFor(x => x.StockItems[i].StockId,null, "--Select --", new { htmlAttributes = new { @class = "form-control",@id= "stocks", @name = "stocks"} })</td> </tr> } </tbody> </table> <script language="javascript" type="text/javascript"> function GetStocks(dropdown1) { var procemessage = "<option value='0'> Please wait...</option>"; var dropdown2 = $(dropdown1).closest('tr').find('#stocks'); dropdown2.html(procemessage).show(); var url = '@Url.Action("GetStockByCat", "Stock")'; $.ajax({ url: url, data: { CatId: dropdown1.value}, cache: false, type: "POST", success: function (data) { var markup = "<option value='0'>---------</option>"; for (var x = 0; x < data.length; x++) { markup += '<option value="' + data[x].value + '">' + data[x].text + '</option>'; } dropdown2.html(markup).show(); }, error: function (reponse) { alert("error : " + reponse); } }); } </script>
解决方案
1. 修复视图加载报错
DropDownListFor的第二个参数不能传null,否则会自动去ViewData中查找对应键的选项集合。由于我们是动态加载选项,直接传入空的SelectList即可避免报错,同时修正重复ID和属性嵌套问题:
修改视图中第二个DropdownList代码:
<td>@Html.DropDownListFor(x => x.StockItems[i].StockId, new SelectList(Enumerable.Empty<SelectListItem>()), "--Select --", new { @class = "form-control stocks-dropdown" })</td>
- 用
stocks-dropdown类代替重复的id="stocks",保证HTML元素ID唯一性 - 移除错误的
htmlAttributes嵌套,直接设置class属性 - 传入空
SelectList,跳过ViewData查找逻辑
2. 修正JS元素查找逻辑
因为现在用类选择器,修改JS中的dropdown2查找代码:
var dropdown2 = $(dropdown1).closest('tr').find('.stocks-dropdown');
3. 修复Controller笔误
把Create方法中的return View(decharge);改为return View(invoice);,否则视图接收的Model为空,循环逻辑会直接报错。
4. 完善表单提交回退逻辑
当表单提交失败时,需要重新填充分类列表,避免视图再次报错:
[HttpPost] public IActionResult Create(Invoice invoice) { if (ModelState.IsValid) // 用ModelState判断更严谨 { _dbcontext.Invoice.Add(invoice); _dbcontext.SaveChanges(); return RedirectToAction("Index"); } // 提交失败时重新填充分类列表 ViewBag.ListCategories = new SelectList(_dbcontext.Category.ToList(), "CategoryId", "CategoryName"); return View(invoice); // 返回原Model保留用户已输入内容 }
5. 优化AJAX请求(可选)
将GetStockByCat改为HttpGet请求,符合REST数据获取的规范:
[HttpGet] public IActionResult GetStockByCat(int CatId) { var stocks = _dbcontext.Stock.Where(s => s.CategoryId == CatId) .Select(s => new SelectListItem { Value = s.StockId.ToString(), Text = s.Name }) .ToList(); return Json(stocks); }
同时修改JS中的请求类型:type: "GET"。
最终效果
- 视图加载不再报错
- 选择分类后可动态加载对应库存选项
- 提交表单时
StockItems[i].StockId能正确绑定到Invoice对象并保存到数据库
内容的提问来源于stack exchange,提问作者user3309231
相关产品推荐
相关产品推荐

