5

我需要通过将文档列表传递给我正在努力使用循环动态构建的自定义过滤器来过滤文档列表: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”参数在哪里丢失了。我不知道这个工作缺少什么。我的代码与许多其他完美运行的示例相同。欢迎任何有关调用表达式及其在幕后工作的额外理论理论信息。

4

3 回答 3

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')。

躲避Invokewithfirst.Or(second).Expand()会产生完全明智的结果:

p1 => ((p1 > 4) OrElse (p1 < 2)) // much better now...
于 2013-05-10T14:47:22.607 回答
1

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

鉴于每个谓词代表一个不同的过滤器,并且我希望最终的组合过滤器成为一系列必须遵守的条件,我们可以说每个谓词都必须返回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. 这正是我想做的。

这比我自己破解表达式树要容易。

于 2013-05-13T12:53:38.400 回答
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);
    }

}
于 2013-05-10T14:55:51.380 回答