EF Core 5的InMemory数据库能否用于集成测试及报错解决
这个问题可以修复,不需要直接更换数据库模拟方案,你的问题确实和EF Core 5禁用隐式客户端计算的破坏性变更相关。
问题根源
EF Core 5开始默认禁止LINQ查询中无法被提供程序翻译的部分隐式落到客户端执行,你之前在EF Core 3中能正常运行,是因为当时InMemory提供程序隐式做了客户端计算兼容了自定义类型的比较逻辑,升级后该逻辑被禁用就会抛出翻译失败的错误。
更深层的原因是你实现的UUIdTypeMapperPlugin是针对关系型数据库的类型映射插件,而InMemory提供程序不会加载关系型的类型映射插件,因此在InMemory中无法识别UUId类型的相等比较逻辑,导致Join查询的表达式无法被翻译。
修复方案
方案1:快速兼容现有InMemory测试方案
只需给模型中的UUId属性全局配置值转换器和比较器,InMemory提供程序可以识别模型内配置的转换规则,无需修改现有测试代码:
可以直接在业务DbContext的OnModelCreating方法中添加全局配置,也可以单独为测试创建DbContext子类重写该方法,不影响正式环境代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 全局为所有UUId类型属性配置转换和比较规则 var uuidConverter = new ValueConverter<UUId, byte[]>( uuid => uuid.ToByteArray(), bytes => new UUId(bytes) ); var uuidComparer = new ValueComparer<UUId>( (left, right) => left.Equals(right), uuid => uuid.GetHashCode(), uuid => uuid ); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(UUId)) { property.SetValueConverter(uuidConverter); property.SetValueComparer(uuidComparer); } } } // 原有模型配置逻辑 }
配置完成后InMemory就能正确识别UUId的比较逻辑,报错即可解决。
方案2:切换到更贴近生产的测试方案(解决多上下文建库问题)
如果担心InMemory和真实生产数据库行为有差异,想要切换到真实数据库测试,你提到的多上下文建库问题可以通过聚合上下文实现,步骤非常简单:
- 在测试项目中单独创建一个仅用于建库的统一DbContext,无需包含业务逻辑,只需要注册所有实体配置:
// 测试项目专用统一上下文 public class TestUnifiedDbContext : DbContext { public TestUnifiedDbContext(DbContextOptions<TestUnifiedDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 加载所有业务上下文所在程序集的实体配置 modelBuilder.ApplyConfigurationsFromAssembly(typeof(RatingsContext).Assembly); // 如果有其他业务上下文,追加对应程序集即可 // modelBuilder.ApplyConfigurationsFromAssembly(typeof(OtherBusinessContext).Assembly); } }
- 测试启动时,仅用该统一上下文调用
context.Database.EnsureCreated()完成一次建库,后续所有业务上下文直接连接该数据库即可,无需每个上下文单独建库。 - 速度问题可以通过使用SQLite内存模式、MySQL临时数据库配合用例结束后清表的逻辑解决,整体运行速度和InMemory差距极小,且测试结果更贴近生产行为。
内容的提问来源于stack exchange,提问作者Fallingsappy
相关产品推荐
相关产品推荐

