有时我们会碰到这样的状况,就是要把两个LambdaExpression合并起来变成一个LambdaExpression.
例如我们有如下两个用来筛选DataRow的表达式:
Expression<Func<DataRow, bool>> exp1 = r => r["name"].ToString() == "Rose";
Expression<Func<DataRow, bool>> exp2 = r => (int)r["age"] > 20;
那现在我们想即根据name又根据age来筛选呢,也就是说我们想要这样的表达式:
Expression<Func<DataRow, bool>> exp1 = r => r["name"].ToString() == "Rose" && (int)r["age"]>20;
很容易想到,可以用Expression.AndAlso来做,即
var newExp = Expression.Lambda(Expression.AndAlso(exp1.Body, exp2.Body),
Expression.Parameter(typeof (DataRow), "r"));
一切看起来很美好;p
但是接下来我们试图去compile时就会出错了,
这个错误就是由于两个LambdaExpression的Body引用的是两个不同的parameter instance,尽管parameter 的名字看起来是一样的。关于这个错误这篇stackoverflow文章说的很好:http://stackoverflow.com/questions/10613514/how-can-i-combine-two-lambda-expressions-without-using-invoke-method
并且这篇文章也很出了一个基于ExpressionVisitor的解决方案,这个解决方案很棒并且切中了要点。但我觉得实现不是很直观也,我的另一个解决方案是实现别一个ParameterReplacementVisitor。
代码如下:
public class ParameterReplacementVisitor : ExpressionVisitor { private readonly ParameterExpression _parameter; public ParameterReplacementVisitor(ParameterExpression parameter) { _parameter = parameter; } public ParameterExpression Parameter { get { return _parameter; } } protected override Expression VisitParameter(ParameterExpression node) { if (node.Name == _parameter.Name) { return _parameter; } return base.VisitParameter(node); } }
这样很容易理解,并且采用的策略很简单,就是根据Parameter Name来把parameter 统一替换成给定的parameter,问题解决。