使用Evaluator.cs仍无法解析表达式变量常量,求问题排查
问题原因及修复方案
核心问题
你的代码存在两个关键错误:
- 部分求值时机错误:未对
Where传入的整个表达式做整体求值,导致编译器生成的局部变量包装匿名类仍留在表达式树中。 - VisitMember处理逻辑错误:即便对
m.Expression执行了PartialEval,也未用求值后的常量替换原表达式节点,更没有将常量值写入SQL。
修复步骤
1. 先对整个Where表达式做部分求值
修改Where方法,在传入Visitor前先对整个表达式执行PartialEval,将所有可计算节点(包括包装局部变量的匿名类成员)替换为常量:
public virtual SqlBuilder<T> Where(Expression<Func<T, bool>> expression) { // 先对整个表达式做部分求值,消除匿名类包装 var evaluatedExpression = Evaluator.PartialEval(expression); BuiltObject.Append(" WHERE"); ExpressionToSQLVisitor<T> expressionToSQL = new(this); expressionToSQL.Visit(evaluatedExpression); return this; }
2. 修正VisitMember的常量处理逻辑
当处理ExpressionType.Constant类型的成员访问时,直接获取求值后的常量值并写入SQL,而非返回原MemberExpression:
protected override Expression VisitMember(MemberExpression m) { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter) { sqlBuilder.AddColumn(m.Member.GetFieldName()); return m; } else if (m.Expression != null) { // 对整个MemberExpression做求值,得到实际常量 var constantExpr = Evaluator.PartialEval(m) as ConstantExpression; if (constantExpr != null) { // 将常量值写入SQL,注意需处理字符串转义、类型适配等逻辑 sqlBuilder.AddValue(constantExpr.Value); return constantExpr; } throw new NotSupportedException($"无法解析成员 '{m.Member.Name}' 的值"); } throw new NotSupportedException($"不支持成员 '{m.Member.Name}'"); }
原代码失效原因
C#编译器捕获lambda中的局部变量时,会自动生成匿名类存储该变量,因此你写的obj.Name == name会被编译为obj.Name == <>匿名类.name。
你之前仅对m.Expression(匿名类实例的ConstantExpression)做PartialEval,得到的仍是匿名类实例,并未解析到字段值。只有对整个访问匿名类字段的MemberExpression执行PartialEval,才能拿到真正的常量值;同时必须将该值写入SQL,而非返回原节点,才能让SQL构建器生成Name = 'Test1'这类正确语句。
内容的提问来源于stack exchange,提问作者Aren Devel
相关产品推荐
相关产品推荐

