You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 17:31:04