Entity Framework Linq OfType<T>在测试程序集调用时触发InvalidCastException
针对开发环境正常、Xunit测试中OfType<Buyer>抛出InvalidCastException的问题,整理了几个排查方向和解决办法:
1. 检查测试项目的DbContext继承映射配置
开发环境的AppDbContext可能在OnModelCreating方法中显式配置了TPH(表继承)的鉴别器规则,但测试项目的上下文未同步该配置。EF Core需要明确的映射规则才能正确将数据库记录实例化为对应的子类。
解决:确保测试项目的AppDbContext的OnModelCreating和开发环境完全一致,比如添加鉴别器配置代码:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置User的鉴别器,指定Buyer和Seller对应的标识值 modelBuilder.Entity<User>() .HasDiscriminator<string>("Discriminator") .HasValue<Buyer>("Buyer") .HasValue<Seller>("Seller"); }
2. 验证测试数据库的记录鉴别器值
测试数据库中目标Id对应的Users记录,其鉴别器字段值可能不是Buyer(或你配置的对应值),导致EF Core将该记录实例化为基类User,后续OfType<Buyer>在内存中过滤时,强制转换就会报错。
解决:直接查询测试数据库的对应记录,确认鉴别器值:
SELECT Id, Discriminator FROM Users WHERE Id = '你的userGuid值'
如果值不正确,修正测试数据的鉴别器字段,确保符合Buyer的映射规则。
3. 排查查询执行方式的差异
开发环境中EF Core会将OfType<Buyer>转换为包含Discriminator='Buyer'的SQL语句,在数据库端完成过滤,返回的都是Buyer实例;但测试环境可能因配置(如跟踪行为、查询编译)导致OfType在客户端执行,此时若存在不符合类型的记录就会触发转换错误。
解决:
- 强制查询在服务器端执行,可在查询前添加
AsQueryable()(确保链式调用的是EF的IQueryable而非内存集合); - 对比开发和测试环境的生成SQL,通过
ToQueryString()查看是否包含鉴别器过滤:var sql = context.Users.OfType<Buyer>().Where(x => x.Id == userGuid).ToQueryString(); // 检查sql是否包含 WHERE [Discriminator] = N'Buyer' - 检查测试项目的DbContext配置,是否开启了
UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)等可能影响查询行为的设置。
4. 确认测试项目的依赖版本一致
测试项目的EF Core相关NuGet包版本可能与开发项目的.NET 8.0版本不匹配,旧版本的EF Core在TPH处理上可能存在bug。
解决:检查测试项目的NuGet包,确保Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer等包的版本与开发项目完全一致(均为8.0.x系列)。
内容的提问来源于stack exchange,提问作者HEDGEWIZARDS

