【问题标题】:Having trouble Aggregating over a list of expressions with Expression.AndAlso使用 Expression.AndAlso 聚合表达式列表时遇到问题
【发布时间】:2013-12-04 16:01:59
【问题描述】:

我是显式构建 LINQ 表达式的新手,我试图弄清楚如何使用 Aggregate 和 Expression.AndAlso 将 IEnumerable>> 组合成一个表达式。

我觉得我快要接近了,但我显然错过了一些东西。

public static Expression<Func<T, bool>> CombineExpressions<T>(
IEnumerable<Expression<Func<T, bool>>> expressions)
{

  if (expressions == null || expressions.Count() == 0)
  {
    return t => true;
  }
  var combined = expressions
                .Cast<Expression>()
                .Aggregate((a, b) => Expression.AndAlso(a, b));

  ParameterExpression pe = Expression.Parameter(typeof(T), "x");

  return Expression.Lambda<Func<T, bool>>(combined, pe);
}

当我调用这个方法时,我得到以下异常:

System.ArgumentException: 
       Expression of type 'System.Func`2[SomeEntity,System.Boolean]'
       cannot be used for return type 'System.Boolean'

请帮忙!

【问题讨论】:

  • 在哪里你得到这个异常?
  • 你用什么参数调用CombineExpressions?您正在构建一个类似于someFunction &amp;&amp; someOtherFunction 的表达式。这可能不是你的意思。

标签: c# linq lambda expression


【解决方案1】:

您在这里遇到的问题是您有需要组合的函数(从技术上讲,是表示函数的表达式)。 AndAlso 在两个函数上调用时没有任何意义;它需要在两个直接解析为布尔值的表达式上调用。您需要获取每个函数的 body,而不是整个函数,并将 AndAlso 这些 body 放在一起。

但仅仅抓住尸体是不够的;如果你这样做,你会遇到每个身体的参数不同的问题。您需要用您为结果函数创建的新参数替换每个函数参数的所有用途。

要处理替换这些参数,我们可以使用以下辅助类和调用它的辅助方法来进行替换:

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

使用这个辅助方法,您已经完成了大部分工作;剩下的不多了:

public static Expression<Func<T, bool>> CombineExpressions<T>(
    IEnumerable<Expression<Func<T, bool>>> expressions)
{
    if (expressions == null || expressions.Count() == 0)
    {
        return t => true;
    }
    ParameterExpression param = Expression.Parameter(typeof(T), "x");
    var combined = expressions
                    .Select(func => func.Body.Replace(func.Parameters[0], param))
                    .Aggregate((a, b) => Expression.AndAlso(a, b));

    return Expression.Lambda<Func<T, bool>>(combined, param);
}

当然,另一种方法是创建一个PredicateBuilder 类,该类可以AndOr 任意两个函数,每个函数采用一个通用类型并返回一个布尔值。它们都比您的示例稍微简单一些:

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                         Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
    }
}

如果我们先花时间制作这种可重用类型,那么将它从两个函数泛化到 N 就很简单了:

public static Expression<Func<T, bool>> CombineExpressions<T>(
    IEnumerable<Expression<Func<T, bool>>> expressions)
{
    if (expressions == null || expressions.Count() == 0)
    {
        return t => true;
    }
    return expressions.Aggregate((a, b) => a.And(b));
}

【讨论】:

  • 这个答案只有两个(现在)赞成票是犯罪的。我花了几个小时试图弄清楚我的表情积累出了什么问题,结果发现我只需要单独获取表情主体。
【解决方案2】:

为什么要重新发明轮子?优秀的 LinqKit 库已经通过其 PredicateBuilder

提供了此功能

用你的例子:

return expressions.Aggregate((accumulate, current) => accumulate.And(current));

【讨论】:

    猜你喜欢
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2012-07-03
    • 2014-06-23
    • 1970-01-01
    相关资源
    最近更新 更多