ASP.NET Core 6中DropdownList选中值无法传递至控制器致模型验证失败
ASP.NET Core 6 DropDownListFor选中值无法传递到控制器的解决方法
问题描述
在ASP.NET Core 6项目中使用@Html.DropDownListFor控件让用户选择创建交易的客户,但选中的客户信息无法传递到控制器,导致模型状态验证不通过。刚接触ASP.NET Core MVC,相关代码如下:
原视图模型类
namespace MyShop.ViewModels { public class CreateDealVM { public Deals deal { get; set; } public IEnumerable<SelectListItem> CustomerListVM { get; set; } } }
原模型类
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; } } }
原控制器代码
public IActionResult Create() { CreateDealVM vm = new CreateDealVM(); vm.CustomerListVM = _context.Customers.Select(x => new SelectListItem { Value = x.customerId.ToString(), Text = x.customerName }).ToList(); return View(vm); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind("dealId,customer,product,saleDate,quantity,price")] Deals deal, CreateDealVM vm) { if (ModelState.IsValid) { try { vm.deal.customer = _context.Customers.Find(vm.CustomerListVM); vm.deal = deal; _context.Deals.Add(vm.deal); _context.SaveChanges(); return RedirectToAction("Index"); } catch (Exception e) { Console.WriteLine(e.Message); } return View(vm); } else { var errors = ModelState.Select(x => x.Value.Errors) .Where(y => y.Count > 0) .ToList(); } return View(vm); }
原视图代码
@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.deal.customer, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.CustomerListVM,Model.CustomerListVM,"Select Customer", htmlAttributes: new { @class = "form-control"}) </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>
核心问题分析与修正步骤
1. 完善Deals模型,添加外键字段
EF Core中导航属性Customer需要对应的外键字段才能正确绑定和保存,修改Deals模型:
namespace MyShop.Models { [Table("Deals")] public class Deals { [Key] [Display(Name = "ID")] public int dealId { get; set; } // 添加外键字段,对应Customer的主键 [Display(Name = "Customer")] public int CustomerId { get; set; } // 导航属性,标注外键关联 [ForeignKey("CustomerId")] 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. 修改视图模型,添加选中客户ID字段
视图模型需要存储用户选中的客户ID,用于接收下拉框提交的值:
namespace MyShop.ViewModels { public class CreateDealVM { public Deals deal { get; set; } public IEnumerable<SelectListItem> CustomerListVM { get; set; } // 新增:存储选中的客户ID public int SelectedCustomerId { get; set; } } }
3. 修正视图中的DropDownListFor绑定
将下拉框绑定目标改为SelectedCustomerId,而非选项列表本身:
@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.SelectedCustomerId, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <!-- 绑定到SelectedCustomerId,选项列表使用CustomerListVM --> @Html.DropDownListFor(model => model.SelectedCustomerId, Model.CustomerListVM, "Select Customer", htmlAttributes: new { @class = "form-control"}) <span asp-validation-for="SelectedCustomerId" class="text-danger"></span> </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>
4. 重构控制器Post方法逻辑
只接收视图模型CreateDealVM,通过选中的ID关联客户并保存:
public IActionResult Create() { CreateDealVM vm = new CreateDealVM(); // 初始化下拉选项列表 vm.CustomerListVM = _context.Customers.Select(x => new SelectListItem { Value = x.customerId.ToString(), Text = x.customerName }).ToList(); // 初始化Deal对象,避免视图空引用 vm.deal = new Deals(); return View(vm); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(CreateDealVM vm) { // 验证选中的客户ID是否有效 if (!_context.Customers.Any(c => c.customerId == vm.SelectedCustomerId)) { ModelState.AddModelError(nameof(vm.SelectedCustomerId), "请选择有效的客户"); } if (ModelState.IsValid) { try { // 关联选中的客户到交易 vm.deal.CustomerId = vm.SelectedCustomerId; _context.Deals.Add(vm.deal); _context.SaveChanges(); return RedirectToAction("Index"); } catch (Exception e) { Console.WriteLine(e.Message); ModelState.AddModelError("", "保存交易时发生错误,请重试"); } } // 验证失败后重新加载下拉选项列表 vm.CustomerListVM = _context.Customers.Select(x => new SelectListItem { Value = x.customerId.ToString(), Text = x.customerName }).ToList(); return View(vm); }
关键说明
- 下拉框提交的是客户ID(数字/字符串),无法直接绑定到
Customer对象,必须通过外键字段中转。 - 控制器Post方法只需接收视图模型,避免同时接收多个实体类导致参数绑定混乱。
- 验证失败后需重新加载下拉选项列表,防止视图中下拉框为空。
内容的提问来源于stack exchange,提问作者Alloylo
相关产品推荐
相关产品推荐

