这是问题的一个例子:
var source = new LambdasTestEntity[] {
new LambdasTestEntity {Id = 1},
new LambdasTestEntity {Id = 2},
new LambdasTestEntity {Id = 3},
new LambdasTestEntity {Id = 4},
};
Expression<Func<LambdasTestEntity, bool>> expression1 = x => x.Id == 1;
Expression<Func<LambdasTestEntity, bool>> expression2 = x => x.Id == 3;
Expression<Func<LambdasTestEntity, bool>> expression3 = x => x.Id > 2;
// try to chain them together in a following rule
// Id == 1 || Id == 3 && Id > 2
// as && has higher precedence, we expect getting two entities
// with Id=1 and Id=3
// see how default LINQ works first
Expression<Func<LambdasTestEntity, bool>> expressionFull = x => x.Id == 1 || x.Id == 3 && x.Id > 2;
var filteredDefault = source.AsQueryable<LambdasTestEntity>()
.Where(expressionFull).ToList();
Assert.AreEqual(2, filteredDefault.Count); // <-this passes
// now create a chain with predicate builder
var totalLambda = expression1.Or(expression2).And(expression3);
var filteredChained = source.AsQueryable<LambdasTestEntity>()
.Where(totalLambda).ToList();
Assert.AreEqual(2, filteredChained.Count);
// <- this fails, because PredicateBuilder has regrouped the first expression,
// so it now looks like this: (Id == 1 || Id == 3) && Id > 2
当我在 Watches 中查找这两个表达式时,我看到以下内容:
expressionFull as it is coming from Linq:
(x.Id == 1) OrElse ((x.Id == 3) AndAlso (x.Id > 2))
totalLambda for PredicateBuilder:
((x.Id == 1) OrElse Invoke(x => (x.Id == 3), x)) AndAlso Invoke(x => (x.Id > 2), x)
如果 PredicateBuilder 的行为与默认的 Linq Expression 构建器不同,我发现使用它有点不安全。
现在有几个问题:
1) Linq 为什么要创建这些组?即使我创建一个 Or 表达式
x => x.Id == 1 || x.Id == 3 || x.Id > 2
我仍然得到像这样分组的前两个标准:
((x.Id == 1) OrElse (x.Id == 3)) OrElse (x.Id > 2)
为什么不只是
(x.Id == 1) OrElse (x.Id == 3) OrElse (x.Id > 2)
?
2) 为什么 PredicateBuilder 添加这些调用?我在默认的 Linq 表达式结果中看不到 Invokes,所以它们似乎没用......
3)有没有其他方法来构造表达式“离线”,然后传递给默认的 Linq 表达式构建器?像这样的东西:
ex = x => x.Id == 1;
ex = ex || x.Id == 3;
ex = ex && x.Id > 2;
然后 Linq Expression builder 解析它并创建与 x => x.Id == 1 || 相同的表达式 x.Id == 3 && x.Id > 2 (给予 && 更高的优先级)?或者也许我可以调整 PredicateBuilder 来做同样的事情?