ASP.NET Core MVC下拉框Tag Helper报SelectListGroup无主键错误求解
错误原因
该异常的触发逻辑非常明确:作为EF Core数据库映射实体的Pending类中定义了List<SelectListItem>类型的BP_Item属性。EF Core默认会扫描实体类的所有引用、集合类型属性,尝试将其映射为数据库表结构;而SelectListItem是MVC框架专门用于视图下拉框绑定的UI类型,它内部关联了SelectListGroup类型,这两个类型都没有定义主键,EF Core无法完成表映射,就会抛出主键缺失的异常。
你当前的实现还存在几个附带问题:
- 下拉框提交的是单个选中值,用集合类型属性接收不符合表单绑定规则
- 视图中硬编码的下拉选项和实体类中定义的选项列表不一致,还存在option标签未闭合的语法错误
- GET Action中写的
model.BP_Item.ToList();是无效代码,没有赋值给任何对象,也不会实际传递到视图 - 实体类中写的日期格式字符串
{dd-mm-yyyy}不符合规则,编辑场景下不会正常生效
修复步骤
1. 修正数据库实体类定义
删除实体中不合理的SelectListItem集合属性,改为存储下拉框选中值的普通基础类型属性,数据库只需要存储用户最终选中的项的值,不需要存储整个选项列表:
namespace ComSoft.Models { public class Pending { [Key] public int Id { get; set; } [Required] [DisplayName("SUPPLIER NAME")] public string BP_SupplierName { get; set; } [DisplayName("COUNTRY")] public string BP_Country { get; set; } [DisplayName("ITEM")] // 存储选中的选项值,不要放UI层的SelectListItem集合 public int BP_Item { get; set; } [DisplayName("VALUE")] public int BP_Value { get; set; } [DisplayName("P/I No.")] public char BP_PINo { get; set; } [DisplayName("ERP REMARKS")] public string BP_ERPRemarks { get; set; } [DisplayName("TRADE TERM")] public string BP_TradeTerm { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)] [DisplayName("SHIP. DATE")] public DateTime BP_ShipDate { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)] [DisplayName("PI RECEIVED DATE")] public DateTime BP_PIReceivedDate { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)] [DisplayName("BANK APPLIED DATE")] public DateTime BP_BankAppliedDate { get; set; } [DisplayName("BUYER")] public string BP_Buyer { get; set; } [DisplayName("STYLE")] public char BP_Style { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)] [DisplayName("L/C OPEN DATE")] public DateTime BP_LCOpenDate { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)] [DisplayName("STATUS")] public string BP_Status { get; set; } [DisplayName("BTB LC NO.")] public int BP_LCNo { get; set; } [DisplayName("EXP LC/CONTRACT")] public string BP_LCContract { get; set; } [DisplayName("UD/AMEND")] public string BP_UDAmend { get; set; } [DisplayName("REMARKS")] public string BP_Remarks { get; set; } [DisplayName("PREPARED BY")] public string BP_PrepaidBy { get; set; } } }
2. 将下拉选项逻辑移到控制器层
下拉选项属于UI展示逻辑,不要和数据库实体混放,可以在控制器中构造选项列表,通过ViewBag传入视图:
public IActionResult PendingUser1() { var model = new Pending(); // 构造下拉选项列表 var itemOptions = new List<SelectListItem> { new SelectListItem { Value = "1", Text = "POLYESTER STAPLE FIBER" }, new SelectListItem { Value = "2", Text = "MECHINARY(TSUDAKOMA)" }, new SelectListItem { Value = "3", Text = "SIDE RECYCLING MACHINE" }, new SelectListItem { Value = "4", Text = "QUILTING MACHINE" }, new SelectListItem { Value = "5", Text = "INDUSTRIAL SEWING MACHINE" }, new SelectListItem { Value = "6", Text = "SEWING MACHINE NEEDLE" }, }; ViewBag.ItemOptions = itemOptions; return View(model); } [HttpPost] [ValidateAntiForgeryToken] public IActionResult PendingUser1(Pending obj) { // 加模型校验,校验不通过时返回视图,避免无效数据写入数据库 if (!ModelState.IsValid) { // 模型校验失败时重新传下拉选项,否则页面刷新后下拉框会为空 var itemOptions = new List<SelectListItem> { new SelectListItem { Value = "1", Text = "POLYESTER STAPLE FIBER" }, new SelectListItem { Value = "2", Text = "MECHINARY(TSUDAKOMA)" }, new SelectListItem { Value = "3", Text = "SIDE RECYCLING MACHINE" }, new SelectListItem { Value = "4", Text = "QUILTING MACHINE" }, new SelectListItem { Value = "5", Text = "INDUSTRIAL SEWING MACHINE" }, new SelectListItem { Value = "6", Text = "SEWING MACHINE NEEDLE" }, }; ViewBag.ItemOptions = itemOptions; return View(obj); } _db.BTB_Pending.Add(obj); _db.SaveChanges(); return RedirectToAction("Index"); }
3. 修正视图下拉框代码
用标签辅助方法绑定传入的选项列表,删除硬编码的option,修复标签语法错误:
<div class="form-group"> <label asp-for="BP_Item" class="control-label"></label> <select asp-for="BP_Item" asp-items="ViewBag.ItemOptions" class="form-control form-control-sm"> <option value="">请选择对应ITEM</option> </select> <span asp-validation-for="BP_Item" class="text-danger"></span> </div>
特殊场景处理
如果确实需要在Pending实体类中保留SelectListItem类型的属性,只需要给属性加上[NotMapped]特性,明确告知EF Core该属性不需要映射到数据库,也不会触发这个映射异常:
[NotMapped] public List<SelectListItem> BP_Item { get; set; }
注意:这种方式不符合关注点分离原则,UI相关逻辑尽量不要放在数据库实体中,避免后续出现更多映射、绑定相关的问题。
内容的提问来源于stack exchange,提问作者Masum Rayhan
相关产品推荐
相关产品推荐

