如何向包含关联数据的MVC模型提交POST请求?
解决Products模型POST提交时Supplier/Category导致ModelState无效的问题
问题原因
在C# 8及以上的可为null引用类型模式下,你的Products模型中Supplier和Category导航属性属于未标记可空的引用类型,模型绑定器会默认将它们判定为必填项。但表单仅提交了SupplierID和CategoryID外键值,无法为这两个导航属性赋值,导致它们为null,触发验证错误,最终ModelState.IsValid返回false。
解决方案
方案1:修改模型,标记导航属性为可空
直接在模型中给导航属性添加?,明确其为可空引用类型:
public class Products { [Key] public int ProductID { get; set; } [Required] public string ProductName { get; set; } = string.Empty; [ForeignKey("Supplier")] public int SupplierID { get; set; } // 标记为可空引用类型 public Suppliers? Supplier { get; set; } [ForeignKey("Category")] public int CategoryID { get; set; } // 标记为可空引用类型 public Categories? Category { get; set; } public string? QuantityPerUnit { get; set; } public decimal UnitPrice { get; set; } public int UnitsInStock { get; set; } public int UnitsOnOrder { get; set; } public int ReorderLevel { get; set; } public string? Discontinued { get; set; } }
修改后,模型验证仅会校验SupplierID和CategoryID的有效性,不再强制要求导航属性有值,匹配表单提交逻辑。
方案2:在控制器中手动移除无效验证项
若暂时不想修改模型,可在POST方法中手动清除导航属性的验证错误:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(int id, [Bind("ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued")] Products products) { // 移除导航属性的验证错误 ModelState.Remove(nameof(Products.Supplier)); ModelState.Remove(nameof(Products.Category)); if (id != products.ProductID) { return NotFound(); } if (ModelState.IsValid) { try { _context.Update(products); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!ProductsExists(products.ProductID)) { return NotFound(); } else { throw; } } return RedirectToAction(nameof(Index)); } // 重新填充下拉选单数据 ViewData["CategoryID"] = new SelectList(_context.Categories, "CategoryID", "CategoryID", products.CategoryID); ViewData["SupplierID"] = new SelectList(_context.Suppliers, "SupplierID", "SupplierID", products.SupplierID); return View(products); }
方案3:使用视图模型推荐
创建仅包含表单所需字段的视图模型,避免导航属性引发的验证问题:
public class ProductEditViewModel { public int ProductID { get; set; } [Required] public string ProductName { get; set; } = string.Empty; public int SupplierID { get; set; } public int CategoryID { get; set; } public string? QuantityPerUnit { get; set; } public decimal UnitPrice { get; set; } public int UnitsInStock { get; set; } public int UnitsOnOrder { get; set; } public int ReorderLevel { get; set; } public string? Discontinued { get; set; } }
在控制器GET方法中,将Products实体映射到视图模型传递给视图;POST方法中接收视图模型,再映射回Products实体完成数据库更新。这种方式更贴合MVC分层设计,也能避免过度暴露实体属性。
内容的提问来源于stack exchange,提问作者Jacob M.
相关产品推荐
相关产品推荐

