.NET5运行单元测试时EF Core复合外键不兼容冲突问题
问题背景
- 技术栈:C#、.NET 5
- 场景:在已稳定运行的现有解决方案中新增两个实体,分别对应数据库表TableA、TableB,主程序运行流畅,向两个新表插入数据的操作可正常执行
- 异常触发场景:编写基于
IClassFixture的测试类运行基础测试方法时抛出异常,主程序运行无同类报错
抛出的异常信息如下:
System.AggregateException : The relationship from 'EntityB.FK' to 'EntityA' with foreign key properties {'MTime' : DateTime, 'MId' : long} cannot target the primary key {'Id' : long} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.
相关代码定义
EntityA 实体定义
public class EntityA { [Key] [Column("id")] public long Id { get; set; } [Column("time")] [Required] public DateTime Time { get; set; } [Column("value")] [Required] public float Value { get; set; } }
EntityB 实体定义
public class EntityB { [Key] [Column("id")] public long Id { get; set; } [Column("value", TypeName = "jsonb")] [Required] public string Value { get; set; } [Column("m_id")] [Required] public long MId { get; set; } [Column("m_time")] public DateTime MTime { get; set; } [ForeignKey("MTime, MId")] public virtual EntityA FK{ get; set; } }
已配置的Fluent API规则
已通过Fluent API将EntityA配置为由Time、Id字段组成的复合主键,对应表结构已在数据库中成功创建,配置代码:
modelBuilder.Entity<EntityA>().HasKey(m => new { m.Time, m.Id });
问题产生原因
- 主程序运行时完整加载了所有Fluent API配置,EF Core正确识别
EntityA的主键为{Time, Id}组成的复合主键,和EntityB上配置的{MTime, MId}外键的字段数量、类型、顺序完全匹配,关系配置合法,因此无异常。 - 测试项目的DbContext初始化逻辑没有加载到
EntityA的复合主键配置,EF Core按照数据注解约定优先识别EntityA上打了[Key]特性的Id字段作为单字段主键,此时EntityB的双字段外键指向单字段主键,属性数量、类型匹配关系不成立,就抛出了上述外键不兼容异常。
测试环境加载不到配置的常见诱因:
- 测试用自定义DbContext重写
OnModelCreating方法时,未调用主项目的实体配置逻辑,比如遗漏base.OnModelCreating(modelBuilder)调用,或未加载主项目中存放IEntityTypeConfiguration实体配置的程序集 - 测试项目使用独立的轻量DbContext,仅手动注册了部分实体映射,漏写
EntityA的复合主键Fluent配置
可行解决方案
- 统一主程序与测试环境的DbContext配置
优先直接复用主项目的DbContext类型编写测试用例,不要在测试项目中自定义残缺配置的DbContext。如果必须自定义测试用DbContext,要确保完整加载所有实体配置:如果主项目使用独立的IEntityTypeConfiguration类管理实体映射,加载配置时需指定实体配置所在的程序集,示例代码:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(EntityA).Assembly); base.OnModelCreating(modelBuilder); } - 移除冗余配置消除歧义
既然已经通过Fluent API指定EntityA的复合主键,直接删除EntityA.Id属性上的[Key]特性,避免配置加载不全时EF Core按数据注解识别出错误的单主键,减少配置冲突概率。 - 配置正确性校验
测试初始化阶段可增加校验逻辑,确认EF Core识别到的实体主键配置符合预期,示例代码:var entityAMetadata = dbContext.Model.FindEntityType(typeof(EntityA)); var primaryKey = entityAMetadata.FindPrimaryKey(); // 合法配置下primaryKey.Properties应包含Time、Id两个字段
内容的提问来源于stack exchange,提问作者Raziel
相关产品推荐
相关产品推荐

