這是問題的一個例子:LINQ的PredicateBuilder,分組和運算符優先級
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
當我在手錶找兩個表達式,我看到以下內容:
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)
我發現這是如果它的行爲與默認的Linq Expression構建器不同,則使用PredicateBuilder會有點不安全。
現在的一些問題:
1)爲什麼LINQ的創建這些組?即使我創建一個或者表達
我還是老樣子得到分組這樣的前兩個標準:
((x.Id == 1) OrElse (x.Id == 3)) OrElse (x.Id > 2)
爲什麼它不只是
(x.Id == 1) OrElse (x.Id == 3) OrElse (x.Id > 2)
?
2)爲什麼PredicateBuilder正在添加這些Invokes?我沒有看到在默認的Linq表達式結果中的調用,所以他們似乎沒用...
3)是否有任何其他方式來構建表達式「脫機」,然後傳遞給默認的Linq Expression構建器?是這樣的:
ex = x => x.Id == 1;
ex = ex || x.Id == 3;
ex = ex && x.Id > 2;
然後LINQ表達式構建器然後解析它並創建相同的表達因爲它對於x => x.Id == 1 || x.Id == 3 & & x.Id> 2(給予& &更高的優先級)? 或者我可以調整PredicateBuilder來做同樣的事情?
它不是因爲'&&'對c中的'||'具有優先權,所以擴展方法'And'在PredicateBuilder中的擴展方法'Or'具有優先權。你可以做'expression1.Or(expression2.And(expression3));'但是我不知道這是你想要的,還是如果它的工作) –
而這正是問題 - C#和默認的Linq表達式生成器都給&&更高的優先級,但PredicateBuilder.And沒有。有時候可能會讓人困惑。 – JustAMartin
那麼,對於(我至少認爲)大多數人來說,運算符優先級的使用不像可靠的(或「第一眼」可以理解的)比好的舊括號...因此,帶括號的解決方案對你來說並不合適? –