EF Core 不支持用 [Key] 配置复合主键,因其仅识别首个 [Key] 并静默忽略其余,易致迁移失败或运行时异常;必须在 OnModelCreating 中用 HasKey 显式声明匿名类型主键,且字段顺序、可空性及 Find 参数顺序均需严格匹配。

EF Core 中必须用 HasKey 配置复合主键,数据注解(如 [Key])在 EF Core 里不支持多字段主键,硬编码字符串数组方式也不安全。
为什么不能用 [Key] 特性?
EF Core 完全忽略多个 [Key] 标记——它只认第一个,其余被静默丢弃,甚至可能引发迁移失败或运行时异常。早期 EF6 支持 [Key] + [Column(Order = n)] 组合,但 EF Core 已移除该能力。如果你看到旧教程用这种方式,那基本是 EF6 或更早的写法,套到 EF Core 上会出问题。
-
[Key]在 EF Core 中仅表示“单字段主键”,不是“主键组成部分” - 即使加了
[Required]或[Column],也不会改变主键语义 - 实体类中所有复合主键字段应保持干净:不加
[Key]、不加[DatabaseGenerated]、不加[Required](除非是引用类型)
HasKey 必须在 OnModelCreating 里写
这是唯一合法入口。任何在构造函数、属性初始化器或 DbContext 构造中尝试配置的行为都无效。
- 正确写法:
modelBuilder.Entity<OrderDetail>().HasKey(od => new { od.OrderId, od.ProductId }); - 顺序影响数据库索引物理排列:先
OrderId后ProductId,意味着WHERE OrderId = ?能走索引,而只查ProductId无法高效利用 - 匿名类型必须用属性名完全匹配,不能改名或漏字段;
new { od.OrderId, ProductId = od.ProductId }是错的
值类型 vs 引用类型:可空性必须显式处理
EF Core 要求复合主键所有字段非空,但处理方式不同:
- 值类型(
int、Guid、DateTime)天然不可空,无需额外操作 - 引用类型(
string、byte[])必须标记为不可为空:用[Required],或启用#nullable enable并声明为string! - 如果用了可空引用类型但没加
[Required],迁移能生成,但插入 null 时数据库会报错(NOT NULL constraint failed)
Find 和 FindAsync 的参数顺序不能错
它们严格按 HasKey 中 lambda 表达式的属性顺序传参,不是按字段名或数据库列顺序。
- ✅ 正确:
context.OrderDetails.Find(1001, 205)(对应od => new { od.OrderId, od.ProductId }) - ❌ 错误:
context.OrderDetails.Find(205, 1001)(顺序反了,返回 null) - ✅ 异步版可用 object 数组:
context.OrderDetails.FindAsync(new object[] { 1001, 205 }) - LINQ 查询不受影响,仍用常规条件:
.Where(x => x.OrderId == 1001 && x.ProductId == 205)
最容易被忽略的是引用类型的可空性检查和 Find 参数顺序——这两点不报编译错误,但会在运行时或数据库层突然失败,且错误信息不直接指向根源。


















