有時我們會碰到這樣的狀況,就是要把兩個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,問題解決。