ASP.NET MVC C#中遍历并编辑勾选复选框的数据库实体记录
问题与修复方案:批量更新订单及关联库存
问题描述
刚接触MVC与Web开发,需要实现以下功能:在展示ProductOrders表记录的表单中,为每行添加复选框,勾选任意数量行后提交,遍历并更新这些行的多个字段,同时关联更新Products表的库存,必须通过ProductOrderID确保更新准确性。目前视图的foreach部分提示对象引用错误,附上原视图与控制器代码,寻求解决方法。
原视图代码
@model IEnumerable<AMS_ITAMSdb.Models.ProductOrder> @using AMS_ITAMSdb.Models @using AMS_ITAMSdb.Controllers @{ ViewBag.Title = "ReturnOrder"; } @using (Html.BeginForm()) { <div class="title"> <h4>Return Order</h4> </div> <p class="filter-section"> <label class="filter-lbl1">Order #: </label>@Html.TextBox("orderNum", "", new { @class = "num" })<label class="lbl-txt"> <i>(Type in order number here)</i></label> <input type="submit" value="Search" /> <button type="button" onclick="location.href='@Url.Action("ReturnOrder")'">Reset</button> </p> } <table class="table"> <tr> <th> Order # </th> <th> Product Code </th> <th> Qty Ordered </th> <th> Qty Available </th> <th> Return? </th> <th> New Qty Available </th> </tr> @foreach(var item in Model) { using(Html.BeginForm()) { <tr> <td> @Html.HiddenFor(modelitem => item.ProductOrderID) @Html.DisplayFor(modelitem => item.OrderID) </td> <td> @Html.DisplayFor(modelitem => item.Product_Code) </td> <td> @Html.DisplayFor(modelitem => item.Qty_Ordered) </td> <td> @Html.DisplayFor(modelitem => item.Qty_Available) </td> <td class="checkbox"> <input type="checkbox" id="@item.Returned" name="checkRec" value="true" /> </td> <td> </td> </tr> } } </table><br /><br /> <div class="tbl-footer"> @using(Html.BeginForm("ReturnedOrder", "ProductOrders", FormMethod.Post)) { <input type="submit" class="button" /> } </div><br /><br />
原控制器代码
//GET: ProductOrders/ReturnOrder public ActionResult ReturnOrder(string orderNum, ProductOrder productOrder) { if (!String.IsNullOrEmpty(orderNum)) { //Filter results based on OrderID entered in textbox var prod = db.ProductOrders.Where(x => x.OrderID.ToString() == orderNum); return View(prod.ToList()); } else { var product = db.ProductOrders.OrderBy(x => x.OrderID).Where(x => x.Returned == null); return View(product.ToList()); } } //POST: ProductOrders/ReturnedOrder/ [HttpPost] public ActionResult ReturnedOrder(ProductOrder productOrder, bool checkRec = false) { if (checkRec == true) { productOrder.Qty_Returned = productOrder.Qty_Returned; productOrder.Qty_Available = productOrder.Qty_Ordered + productOrder.Qty_Available; productOrder.Qty_On_Hand = productOrder.Qty_On_Hand - productOrder.Qty_Ordered; db.Entry(productOrder).State = EntityState.Modified; db.SaveChanges(); Product product = db.Products.Where(x => x.ProductID == productOrder.ProductID).FirstOrDefault(); product.Qty_Available = productOrder.Qty_Available; db.Entry(product).State = EntityState.Modified; db.SaveChanges(); }; return View("ReturnOrder"); }
问题分析与修复步骤
1. 视图层核心问题修复
- 解决对象引用错误:遍历Model前先判断是否为null,避免空引用
- 统一表单结构:把整个表格包裹在提交表单内,取消每行单独的form,确保批量提交选中项
- 修正复选框配置:将复选框name设为
selectedOrderIds,value绑定item.ProductOrderID,提交时可获取所有选中的ID数组 - 修正Hidden字段:手动生成hidden字段存储ProductOrderID,避免Html辅助器的命名问题
修改后的视图代码:
@model IEnumerable<AMS_ITAMSdb.Models.ProductOrder> @using AMS_ITAMSdb.Models @using AMS_ITAMSdb.Controllers @{ ViewBag.Title = "ReturnOrder"; } @using (Html.BeginForm("ReturnOrder", "ProductOrders", FormMethod.Get)) { <div class="title"> <h4>Return Order</h4> </div> <p class="filter-section"> <label class="filter-lbl1">Order #: </label>@Html.TextBox("orderNum", "", new { @class = "num" })<label class="lbl-txt"> <i>(Type in order number here)</i></label> <input type="submit" value="Search" /> <button type="button" onclick="location.href='@Url.Action("ReturnOrder")'">Reset</button> </p> } <!-- 批量提交表单包裹整个表格 --> @using (Html.BeginForm("ReturnedOrder", "ProductOrders", FormMethod.Post)) { <table class="table"> <tr> <th>Order #</th> <th>Product Code</th> <th>Qty Ordered</th> <th>Qty Available</th> <th>Return?</th> <th>New Qty Available</th> </tr> <!-- 先判断Model是否为空,避免空引用错误 --> @if (Model != null && Model.Any()) { foreach(var item in Model) { <tr> <td> <!-- 手动生成hidden字段,确保提交时能传递ProductOrderID --> <input type="hidden" name="selectedOrderIds" value="@item.ProductOrderID" /> @Html.DisplayFor(modelitem => item.OrderID) </td> <td>@Html.DisplayFor(modelitem => item.Product_Code)</td> <td>@Html.DisplayFor(modelitem => item.Qty_Ordered)</td> <td>@Html.DisplayFor(modelitem => item.Qty_Available)</td> <td class="checkbox"> <!-- 复选框name统一为selectedOrderIds,value为ProductOrderID --> <input type="checkbox" name="selectedOrderIds" value="@item.ProductOrderID" /> </td> <td> <!-- 可显示计算后的新库存,方便预览 --> @(item.Qty_Available + item.Qty_Ordered) </td> </tr> } } else { <tr> <td colspan="6">No records found.</td> </tr> } </table> <div class="tbl-footer"> <input type="submit" class="button" value="Process Returns" /> </div> }
2. 控制器层核心问题修复
- 修改参数接收方式:接收
int[] selectedOrderIds获取所有选中的订单ID - 修正更新逻辑:从数据库重新获取实体,避免直接使用传入的对象导致的错误,同时修正库存计算逻辑
- 返回视图时传递Model:处理完成后重新获取数据并传递给视图,避免空引用
修改后的控制器代码:
//GET: ProductOrders/ReturnOrder public ActionResult ReturnOrder(string orderNum) { // 移除不必要的ProductOrder参数 IEnumerable<ProductOrder> orders; if (!String.IsNullOrEmpty(orderNum)) { // 处理OrderID可能不是字符串的情况,建议用int.TryParse if (int.TryParse(orderNum, out int orderId)) { orders = db.ProductOrders.Where(x => x.OrderID == orderId).ToList(); } else { orders = new List<ProductOrder>(); } } else { orders = db.ProductOrders.OrderBy(x => x.OrderID).Where(x => x.Returned == null).ToList(); } return View(orders); } //POST: ProductOrders/ReturnedOrder/ [HttpPost] public ActionResult ReturnedOrder(int[] selectedOrderIds) { if (selectedOrderIds != null && selectedOrderIds.Any()) { foreach (int orderId in selectedOrderIds) { // 从数据库获取要更新的订单实体 var productOrder = db.ProductOrders.FirstOrDefault(x => x.ProductOrderID == orderId); if (productOrder != null) { // 更新订单字段 productOrder.Returned = true; // 标记为已退货 productOrder.Qty_Returned = productOrder.Qty_Ordered; // 退货数量等于订购数量 productOrder.Qty_Available += productOrder.Qty_Ordered; // 可用库存增加 productOrder.Qty_On_Hand -= productOrder.Qty_Ordered; // 在手库存减少 db.Entry(productOrder).State = EntityState.Modified; // 更新关联的Products表库存 var product = db.Products.FirstOrDefault(x => x.ProductID == productOrder.ProductID); if (product != null) { product.Qty_Available += productOrder.Qty_Ordered; db.Entry(product).State = EntityState.Modified; } } } db.SaveChanges(); // 批量保存,提升性能 } // 处理完成后重新获取数据,返回视图 return RedirectToAction("ReturnOrder"); }
额外说明
- 建议在数据库操作中使用事务,确保订单和库存更新的原子性,避免部分更新失败的情况
- 可以添加错误处理逻辑,捕获数据库操作异常并返回友好提示
- 原代码中
OrderID.ToString() == orderNum存在类型转换隐患,改用int.TryParse更安全
内容的提问来源于stack exchange,提问作者Kip M.
相关产品推荐
相关产品推荐

