DLINQ投射到具有表达树的具体类型

时间:2014-04-28 14:03:40

标签: c# linq entity-framework expression-trees

我有一个Purchase

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

我希望将投影创建为类似PurchaseSummaryInfo

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

但要使用我的“预制”表达树。

能否完成,如果是的话 - 怎么做?

1 个答案:

答案 0 :(得分:0)

我们需要Combine方法。此方法将接受一个接受值并计算中间结果的表达式,然后接受与第一个表达式相同的输入的第二个表达式,接受中间结果作为第二个参数,然后计算新值。

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);
}

作为一种实现,它依赖于将一个表达式的所有实例替换为另一个表达式的能力,这可以使用以下方法完成:

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);
}

现在我们可以写:

var ps = Db.Purchases.Select(CurrentPaidSumLambda.Combine((p, sum) => 
    new PurchaseSummaryInfo
    {
        paidSum = sum ?? 0
    });