使用AutoFixture测试时Entity Framework GroupBy行为不符合预期
测试中GroupBy无法聚合实体的问题排查与解决
问题根源
你的GroupBy使用自定义类SupportRoleKey作为分组键,其中包含EmployeeEntity引用类型。默认情况下,.NET对自定义类型的相等性检查是引用比较,而非基于实体的业务标识:
- 真实数据库场景下:EF Core上下文会缓存已加载的实体,同一个员工的
EmployeeEntity只会被实例化一次,因此两个SupportRoleEntity的EmployeeInfo指向同一个引用,分组键的引用相等,能正确聚合。 - 测试场景下:从数据库查询数据时,EF Core会为每个
SupportRoleEntity的EmployeeInfo加载新实例(即使是同一个员工),导致两个SupportRoleKey中的EmployeeInfo引用不同,被判定为不同分组,最终返回3组而非预期的2组。
另外,SupportRoleKey类未重写Equals和GetHashCode方法,即使实体业务属性完全相同,只要引用不同就会被视为不同键,进一步放大了问题。
解决方案
方案1:重写SupportRoleKey的相等性判断
修改SupportRoleKey,基于业务标识实现相等性检查,替代默认的引用比较:
public class SupportRoleKey { public EmployeeEntity EmployeeInfo { get; set; } = null!; public string SupportRolePropertyId { get; set; } = null!; public string RoleType { get; set; } public override bool Equals(object? obj) { if (obj is not SupportRoleKey other) return false; return EmployeeInfo.EmployeeId == other.EmployeeInfo.EmployeeId && SupportRolePropertyId == other.SupportRolePropertyId && RoleType == other.RoleType; } public override int GetHashCode() { return HashCode.Combine(EmployeeInfo.EmployeeId, SupportRolePropertyId, RoleType); } }
方案2:改用实体标识作为分组键(更推荐)
避免在分组键中使用整个实体,直接用实体的唯一业务标识(如EmployeeId),从根源消除引用比较的问题:
// 修改GroupBy的键定义 .GroupBy(s => new { EmployeeId = s.EmployeeInfo.EmployeeId, s.SupportRolePropertyId, RoleType = s.SupportRoleProperties.RoleType })
同时调整后续Select逻辑,使用标识而非实体构建DTO(若有需要)。
测试验证
可在测试中添加以下代码,验证查询返回的EmployeeInfo引用是否相同,确认问题:
var supportRoles = _dbContext.SupportRoles .Include(s => s.EmployeeInfo) .Where(s => s.EmployeeId == employeeEntity.EmployeeId) .ToList(); // 检查前两个SupportRole的EmployeeInfo是否为同一引用 Console.WriteLine(object.ReferenceEquals(supportRoles[0].EmployeeInfo, supportRoles[1].EmployeeInfo));
测试环境中该代码会返回false,真实环境中返回true,验证诊断结论。
内容的提问来源于stack exchange,提问作者Benjamin Parsons
相关产品推荐
相关产品推荐

