ASP.NET Core MVC主从模型中ArgumentOutOfRangeException异常排查
问题分析与解决:Stock主从模型添加Article时索引越界错误
问题背景
我拥有Stock和Article主从模型,Article以StockId作为外键。我在表格中展示Stock数据,希望创建局部视图为表格每行添加Article,但运行时出现索引越界错误。
现有代码
StockController.cs
public IActionResult AddArticle(int id) { Stock stock = _dbcontext.Stock.Where(e => e.StockId == id).FirstOrDefault(); stock.Articles.Add(new Article() { ArticleId = 1 }); return PartialView("_AddArticlePartialView", stock); } public IActionResult AddArticle(Article article) { if (article != null) { _dbcontext.Article.Add(article); _dbcontext.SaveChanges(); return RedirectToAction("Index"); } return View(); }
Index.cshtml
@model IEnumerable<Stock> @{ ViewData["Title"] = "Stock"; Layout = "~/Views/Shared/_Theme.cshtml"; } <table class="table table-bordered table-hover" id="display"> <thead> <tr> <th> @Html.DisplayNameFor(model => model.CategoryId) </th> <th> @Html.DisplayNameFor(model => model.Designation) </th> <th> @Html.DisplayNameFor(model => model.Quantite) </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Category.CategoryName) </td> <td> @Html.DisplayFor(modelItem => item.Designation) </td> <td> @Html.DisplayFor(modelItem => item.Quantite) </td> <td> <div class="btn-group-sm"> <button class="btn btn-primary" data-toggle="modal" data-target="@("#AddArticle-"+item.StockId)" data-url="@Url.Action($"AddArticle/{item.StockId}")"><i class="fa fa-plus"></i>Ajouter Articles</button> @await Html.PartialAsync("_AddArticlePartialView", item) </div> </td> </tr> } </tbody> </table>
_AddArticlePartialView.cshtml
@model StockProject.Models.Stock @{ ViewData["Title"] = "_AddArticlePartialView"; } <div class="modal fade" role="dialog" tabindex="-1" id="@("#AddArticle-"+Model.StockId)" aria-labelledby="addArticleLabel" aria-hidden="true" > <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document"> <div class="modal-content"> <div class="modal-header"> <h3 class="modal-title">Articles</h3> </div> <div class="modal-body"> <form asp-action="AddArticle" method="post"> <div class="card"> <div class="card-header"> <h3 class="card-title">Articles</h3> </div> <div class="card-body"> <table class="table table-bordered" id="articleTable"> <thead> <tr> <th>Numero de Serie</th> <th>Marque</th> <th>Etat</th> </tr> </thead> <tbody> @for (int i = 0; i < Model.Quantite; i++) { <tr> <td> @Html.EditorFor(x => x.Articles[i].NumeroSerie, new { htmlAttributes = new { @class = "form-control" } }) </td> <td> @Html.EditorFor(x => x.Articles[i].Marque, new { htmlAttributes = new { @class = "form-control" } }) </td> <td> @Html.DropDownListFor(x => x.Articles[i].Etat, new List<SelectListItem> { new SelectListItem { Value = "En Marche" , Text = "En Marche" }, new SelectListItem { Value = "En Panne" , Text = "En Panne" }, }, new { @class = "form-control" }) </td> </tr> } </tbody> </table> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button> <button type="submit" class="btn btn-primary">Sauvegarder</button> </div> </form> </div> </div> </div> </div>
错误信息
Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
错误发生在局部视图的这一行:
@Html.EditorFor(x => x.Articles[i].NumeroSerie, new { htmlAttributes = new { @class = "form-control" } })
问题原因与解决方案
核心问题
- 页面加载时直接渲染局部视图导致集合为空:Index页面循环中直接调用
@await Html.PartialAsync("_AddArticlePartialView", item),此时传入的Stock实例的Articles集合为空,但局部视图用Model.Quantite作为循环次数访问Articles[i],当Quantite > 0时必然索引越界。 - 控制器GET方法未被实际调用:按钮的
data-url未通过AJAX触发请求,而是直接渲染了未初始化的局部视图。 - 批量添加逻辑不匹配:POST方法仅接收单个Article,但视图生成多个输入框,无法正确绑定集合数据。
修复步骤
1. 修改Index页面,改用AJAX加载模态框
移除页面中直接渲染局部视图的代码,添加JS处理按钮点击事件,通过AJAX请求控制器获取模态框内容:
<!-- 在Index页面末尾添加JS --> <script> $(document).ready(function() { $('[data-toggle="modal"]').click(function() { var url = $(this).data('url'); var targetModal = $(this).data('target'); $.get(url, function(data) { $(targetModal).remove(); $('body').append(data); $(targetModal).modal('show'); }); }); }); </script>
同时修正按钮的data-target格式:
data-target="#AddArticle-@item.StockId"
2. 修改控制器GET方法,初始化对应数量的Article
根据Stock的Quantite属性,向Articles集合添加对应数量的空实例:
public IActionResult AddArticle(int id) { Stock stock = _dbcontext.Stock.Where(e => e.StockId == id).FirstOrDefault(); stock.Articles.Clear(); // 根据库存数量生成对应数量的Article for (int i = 0; i < stock.Quantite; i++) { stock.Articles.Add(new Article() { StockId = id }); } return PartialView("_AddArticlePartialView", stock); }
3. 修改POST方法,支持批量添加Article
修改方法接收List<Article>集合,并确保外键正确:
public IActionResult AddArticle(List<Article> articles, int stockId) { if (articles != null && articles.Any()) { foreach (var article in articles) { article.StockId = stockId; _dbcontext.Article.Add(article); } _dbcontext.SaveChanges(); return RedirectToAction("Index"); } return View(); }
同时在局部视图表单中添加隐藏字段传递stockId:
<form asp-action="AddArticle" method="post"> <input type="hidden" name="stockId" value="@Model.StockId" /> <!-- 其余表单内容不变 --> </form>
4. 修正模态框ID格式
局部视图中模态框ID去掉多余的#符号:
<div class="modal fade" role="dialog" tabindex="-1" id="AddArticle-@Model.StockId" aria-labelledby="addArticleLabel" aria-hidden="true" >
内容的提问来源于stack exchange,提问作者user3309231
相关产品推荐
相关产品推荐

