构建查询表达式时出现重复参数插入异常问题排查
解决EF Core中WhereAnyMatch二次调用参数重复的问题
你遇到的System.ArgumentException: 已添加了具有相同键的项。键: f错误,核心原因是表达式树参数名称冲突:
尽管每次调用都使用了新的DbContext,但两次调用生成的查询表达式复用了相同名称的参数(比如原match表达式中的f)。EF Core在解析表达式、构建内部参数映射字典时,会将同名参数判定为重复键,从而抛出异常。
原方法的问题点
原WhereAnyMatch方法直接复用了传入的match表达式的参数对象,两次调用生成的Lambda表达式参数名称完全一致,导致EF Core处理时触发重复键校验。
修复方案
修改辅助方法,为每次调用生成唯一名称的新参数,并替换原表达式中的参数引用,避免名称冲突:
public static IQueryable<T> WhereAnyMatch<T, V>(this IQueryable<T> source, IEnumerable<V> values, Expression<Func<T, V, bool>> match) { // 生成唯一名称的新参数,避免重复 var newParameter = Expression.Parameter(typeof(T), $"param{Guid.NewGuid():N}"); // 替换原match表达式中的旧参数为新参数 var parameterReplacer = new ParameterReplacer(match.Parameters[0], newParameter); var newMatchBody = parameterReplacer.Visit(match.Body); var newMatch = Expression.Lambda<Func<T, V, bool>>(newMatchBody, newParameter, match.Parameters[1]); var body = values .Select(value => ((Expression<Func<V>>)(() => value)).Body) .Select(value => Expression.Invoke(newMatch, newParameter, value)) .Aggregate(Expression.OrElse); var predicate = Expression.Lambda<Func<T, bool>>(body, newParameter); return source.Where(predicate); } // 表达式参数替换辅助类 private class ParameterReplacer : ExpressionVisitor { private readonly ParameterExpression _oldParam; private readonly ParameterExpression _newParam; public ParameterReplacer(ParameterExpression oldParam, ParameterExpression newParam) { _oldParam = oldParam; _newParam = newParam; } protected override Expression VisitParameter(ParameterExpression node) { return node == _oldParam ? _newParam : base.VisitParameter(node); } }
修复原理
通过ParameterReplacer这个表达式访问器,将原match表达式中的参数替换为每次调用生成的唯一名称参数,确保两次调用生成的查询表达式参数名称完全不同,EF Core构建参数映射时就不会出现重复键的问题。
内容的提问来源于stack exchange,提问作者SuperMenthol
相关产品推荐
相关产品推荐

