ASP.NET Core 6中Html.DropDownListFor引发NullReferenceException问题
问题:创建交易时出现NullReferenceException异常
我有Customer和Deals两个类,客户与交易是外键关联关系。为了让用户通过下拉列表选择发起交易的客户,我创建了包含客户SelectListItem集合和Deals实例的CreateDealVM视图模型,更新了DealController中的Create方法,但创建交易时抛出以下异常:
System.NullReferenceException HResult=0x80004003 Message=未将对象引用设置到对象的实例。
ViewModel代码
public class CreateDealVM { public Deals deal { get; set; } public IEnumerable<SelectListItem> selectedCustomer { get; set; } public int selectedCustomerId { get; set; } }
DealController方法代码
public IActionResult Create() { Deals deal = new Deals(); List<Customer> customerList = _context.Customers.ToList(); CreateDealVM vm = new CreateDealVM(); vm.selectedCustomer = new SelectList(customerList, "customerId", "customerName"); vm.deal = deal; return View(vm); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(CreateDealVM vm) { if (ModelState.IsValid) { try { vm.deal.customer =_context.Customers.Find(vm.selectedCustomerId); _context.Deals.Add(vm.deal); _context.SaveChanges(); return RedirectToAction("Index"); } catch (Exception e) { Console.WriteLine(e.Message); } return View(vm); } return View(); }
Deal模型类代码
namespace MyShop.Models { [Table("Deals")] public class Deals { [Key] [Display(Name = "ID")] public int dealId { get; set; } [ForeignKey("Customer")] [Display(Name = "Customer")] public Customer customer { get; set; } [Display(Name = "CustomerName")] public string? parentCustomerName { get; set; } [Display(Name = "product")] public DealTypeEnum product { get; set; } [Display(Name = "Date")] public DateTime saleDate { get; set; } [Display(Name = "Quantity")] public float quantity { get; set; } [Display(Name = "Price")] public float price { get; set; } } }
View视图页面代码
@model MyShop.ViewModels.CreateDealVM @{ ViewData["Title"] = "Create"; } <h1>Create</h1> <h4>Deals</h4> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="Create"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> @Html.LabelFor(model => model.selectedCustomer, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.selectedCustomerId, Model.selectedCustomer,"--Select--" ) </div> </div> <div class="form-group"> <label asp-for="deal.product" class="control-label"></label> <select asp-for="deal.product" class="form-control"></select> <span asp-validation-for="deal.product" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="deal.saleDate" class="control-label"></label> <input asp-for="deal.saleDate" class="form-control" /> <span asp-validation-for="deal.saleDate" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="deal.quantity" class="control-label"></label> <input asp-for="deal.quantity" class="form-control" /> <span asp-validation-for="deal.quantity" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="deal.price" class="control-label"></label> <input asp-for="deal.price" class="form-control" /> <span asp-validation-for="deal.price" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> </div> </div>
我尝试过相关解决方案但未解决问题,寻求有效修复方案。
解决方案
问题根源
- Post方法中
vm.deal可能为null:表单提交时模型绑定未正确实例化deal对象,导致访问vm.deal.customer触发空引用异常。 - ModelState无效时未重新填充下拉数据:验证失败直接返回
View(),视图所需的selectedCustomer集合为空,渲染时触发空引用。 - Deals模型外键配置不规范:仅定义导航属性
customer,未显式声明外键字段,EF Core无法正确处理关联关系。
修复步骤
1. 修正Deals模型,添加显式外键字段
namespace MyShop.Models { [Table("Deals")] public class Deals { [Key] [Display(Name = "ID")] public int dealId { get; set; } // 添加显式外键字段 [Display(Name = "Customer ID")] public int CustomerId { get; set; } [ForeignKey("CustomerId")] [Display(Name = "Customer")] public Customer customer { get; set; } [Display(Name = "CustomerName")] public string? parentCustomerName { get; set; } [Display(Name = "product")] public DealTypeEnum product { get; set; } [Display(Name = "Date")] public DateTime saleDate { get; set; } [Display(Name = "Quantity")] public float quantity { get; set; } [Display(Name = "Price")] public float price { get; set; } } }
2. 改进Post方法,确保视图模型完整性
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(CreateDealVM vm) { // 初始化deal对象(防止模型绑定未创建) vm.deal ??= new Deals(); if (ModelState.IsValid) { try { // 直接设置外键ID,无需加载整个Customer对象,提升性能 vm.deal.CustomerId = vm.selectedCustomerId; _context.Deals.Add(vm.deal); _context.SaveChanges(); return RedirectToAction("Index"); } catch (Exception e) { Console.WriteLine(e.Message); ModelState.AddModelError("", $"创建交易失败:{e.Message}"); } } // 验证失败时重新填充下拉列表 vm.selectedCustomer = new SelectList(_context.Customers.ToList(), "customerId", "customerName"); return View(vm); }
3. 优化视图,确保表单绑定正确
- 显式声明表单提交方法为
post:
<form asp-action="Create" method="post">
- 给下拉列表添加样式并确保绑定正确:
@Html.DropDownListFor(model => model.selectedCustomerId, Model.selectedCustomer, "--Select--", new { @class = "form-control" })
4. 可选:初始化ViewModel的deal属性
在CreateDealVM构造函数中初始化deal,避免null:
public class CreateDealVM { public CreateDealVM() { deal = new Deals(); } public Deals deal { get; set; } public IEnumerable<SelectListItem> selectedCustomer { get; set; } public int selectedCustomerId { get; set; } }
内容的提问来源于stack exchange,提问作者Alloylo
相关产品推荐
相关产品推荐

