1

I have an expression tree for Purchase class

public static readonly Expression<Func<Purchase, double?>> CurrentPaidSumLambda = 
p => (double?)p.Payments
   .Where(pa => pa.Status == SystemConstants.PaymentStatus.Paid)
   .Sum(pa => pa.Sum);

I wish to create projection into a type PurchaseSummaryInfo like this

 var ps = Db.Purchases.Select(p =>
    new PurchaseSummaryInfo
    {
        paidSum = (double?)p.Payments
        .Where(pa => pa.Status == SystemConstants.PaymentStatus.Paid)
        .Sum(pa => pa.Sum) ?? 0
    });

But to use my "pre-canned" expression tree.

Can it be done, and if yes - how?

Servy
  • 202,030
  • 26
  • 332
  • 449
alex440
  • 1,647
  • 3
  • 20
  • 35

1 Answers1

0

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
    });
Servy
  • 202,030
  • 26
  • 332
  • 449