We'll need a Combine
method. This method will take an expression that accepts a value and computes an intermediate result, and then a second expression that accepts the same input as the first expression, accepts the intermediate result as a second parameter, and then computes a new value.
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
As an implementation, it relies on the ability to replace all instances of one expression with another, which can be done using this:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
So now we can write:
var ps = Db.Purchases.Select(CurrentPaidSumLambda.Combine((p, sum) =>
new PurchaseSummaryInfo
{
paidSum = sum ?? 0
});