为什么运行时表达式会导致Entity Framework Core 5缓存键冲突?
问题根本成因
这是 EF Core 5.x版本的已知官方Bug,问题出在ExpressionEqualityComparer的内部实现逻辑:
- EF的查询编译缓存是全局生效的,和查询是否为非跟踪状态无关,只要执行查询就会走缓存键的相等性比较逻辑
- 你动态生成多个过滤Lambda时用了多个同名(均为
e)但不同实例的ParameterExpression参数,在用And/Or扩展方法拼接表达式后,EF的表达式相等性比较器在对比Lambda时,内部使用了没有做重复键校验的普通Dictionary存储参数映射,遇到同名不同实例的参数时就会直接抛出"相同键已存在"的异常。
你当前的动态生成参数名的临时方案虽然可以绕开报错,但会导致逻辑完全相同的查询因为参数名不一致无法复用编译缓存,缓存命中率大幅下降,高并发场景下会有明显的性能损耗,不建议长期使用。
更合理的修复方案
方案1:升级EF Core版本(最优解)
该Bug在EF Core 6.0及以上版本已经被官方修复,如果你的项目可以升级.NET运行时版本,直接升级到.NET 6+配套使用EF Core 6.0+即可,无需修改现有业务代码。
方案2:修改表达式拼接逻辑(兼容EF Core 5.x)
如果你暂时无法升级版本,只需调整And/Or扩展方法的实现,将所有待拼接Lambda的参数替换为同一个ParameterExpression实例即可,既可以绕开Bug,也不会影响查询缓存的复用:
- 首先实现参数替换的表达式访问器
public class ParameterReplaceVisitor : ExpressionVisitor { private readonly ParameterExpression _oldParam; private readonly ParameterExpression _newParam; public ParameterReplaceVisitor(ParameterExpression oldParam, ParameterExpression newParam) { _oldParam = oldParam; _newParam = newParam; } protected override Expression VisitParameter(ParameterExpression node) { return node == _oldParam ? _newParam : base.VisitParameter(node); } }
- 修改
And/Or扩展方法实现
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { if (left == null) return right; if (right == null) return left; // 将右侧Lambda的参数替换为和左侧一致的实例 var adjustedRightBody = new ParameterReplaceVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body); return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, adjustedRightBody), left.Parameters); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { if (left == null) return right; if (right == null) return left; var adjustedRightBody = new ParameterReplaceVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body); return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, adjustedRightBody), left.Parameters); }
该问题属于EF Core 5.x的原生Bug,并非你的业务实现疏漏导致,不需要提交新的Github Issue。
内容的提问来源于stack exchange,提问作者Patricio Leonel Filice
相关产品推荐
相关产品推荐

