使用foreach循环构建自定义谓词以充当过滤器

时间:2013-05-09 19:08:15

标签: c# linq-to-entities expression-trees linq-expressions linqkit

我需要过滤一个文档列表,方法是将它们传递给我正在努力使用foreach循环动态构建的自定义过滤器:

var mainPredicate = PredicateBuilder.True<Document>();

// mainPredicate is combined to other filters successfully here ...

var innerPredicate = PredicateBuilder.False<Document>();
foreach (var period in periods)
{
    var p = period;
    Expression<Func<Document, bool>> inPeriod =
        d => d.Date >= p.DateFrom && d.Date <= p.DateTo;

    innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));
}

mainPredicate = mainPredicate.And(innerPredicate);

最后一行:

documents = this.ObjectSet.AsExpandable().Where(mainPredicate).ToList();

引发此异常:

  

参数“d”未绑定在指定的LINQ to Entities中   查询表达式。

任何人都知道为什么我得到这个例外?我不明白我传递给InPeriod方法的'd'参数丢失了。我不知道为什么缺少这个工作。我的代码与许多其他完美运行的示例相同。任何关于调用表达式的理论理论信息及其在幕后的工作方式都是值得欢迎的。

3 个答案:

答案 0 :(得分:2)

我不明白你为什么这样做:

innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));

当你可以完全避开Invoke时,就像这样:

innerPredicate = innerPredicate.Or(inPeriod);

这应该完全正常。


顺便说一句,我觉得LINQKit存在一个错误(除非有一些文档暗示它不支持这种情况)。

当我尝试这个类似的代码时:

 Expression<Func<int, bool>> first = p1 => p1 > 4;
 Expression<Func<int, bool>> second = p2 => p2 < 2;

// Expand is similar to AsExpandable, except it works on 
// expressions, not queryables.
var composite = first.Or(d => second.Invoke(d))
                     .Expand();

... LINQKit生成了以下复合表达式:

p1 => ((p1 > 4) OrElse (d < 2)) // what on earth is d?

...确实有未绑定的参数 d (NodeType = Parameter,Name ='d')。

Invoke躲避first.Or(second).Expand()会产生完全明智的效果:

p1 => ((p1 > 4) OrElse (p1 < 2)) // much better now...

答案 1 :(得分:1)

最后,我找到了一种避免将多个谓词组合到主表达式树的方法。

鉴于每个谓词代表不同的过滤器,并且我希望最终的组合过滤器是一系列必须被尊重的条件,我们可以说每个谓词都必须返回< strong> true 表示最终谓词返回true。

要实现这一点,谓词必须与 AND 结合使用。因此,生成的SQL查询必须如下所示:

predicate1 AND predicate2 AND predicate3 ...

将这些谓词与AND结合起来的更好方法是将Where个查询运算符链接到最终查询,如下所示:

var documents = this.ObjectSet.AsExpandable()
    .Where(mainPredicate)
    .Where(otherPredicate)
    .Where(yetAnotherPredicate)
    .ToList();

生成的SQL查询将每个谓词与AND组合在一起。这正是我想要做的。

这比自己破解表达树更容易。

答案 2 :(得分:0)

我使用这些扩展方法:

public static class Extensions
{
    public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate)
    {
        InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(source.Body, invokedExpression), source.Parameters);
    }

    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate)
    {
        InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(source.Body, invokedExpression), source.Parameters);
    }

}