2012-02-07 105 views
2

我使用謂詞建設者寫下面的代碼:嵌套或使用LINQ PredicateBuilder

IEnumerable<int> ids= new List<int> { 47, 48 }; 

var predicate = PredicateBuilder.False<Customer>(); 

predicate = predicate.And(x => x.CreatedAt >= fromDate && x.CreatedAt <= toDate); 

foreach (var id in ids) 
{ 
    predicate = predicate.Or(x => x.Source.Id == id); 
} 

var result = Database.Set<Customer>().AsExpandable() 
            .Where(predicate) 
            .ToList(); 

生成的SQL的樣子(只是在WHERE子句):

WHERE ([Filter6].[SourceId] IN (@p__linq__0,@p__linq__1)) 
AND ([Filter6].[CreatedAt] >= @p__linq__2) 
AND ([Filter6].[CreatedAt] <= @p__linq__3)', 
N'@p__linq__0 int, 
@p__linq__1 int, 
@p__linq__2 datetime2(7), 
@p__linq__3 datetime2(7)', 
@p__linq__0=48, 
@p__linq__1=48, 
@p__linq__2='2012-02-07 21:59:55.0437985', 
@p__linq__3='2012-02-07 22:04:55.5748288' 

它看起來像ID 48在SQL中分配了兩次。不知道爲什麼?

回答

6
foreach (var id in ids) 
{ 
    predicate = predicate.Or(x => x.Source.Id == id); 
} 

您正在關閉循環變量。讓你的id變量的本地副本,而不是:

foreach (var id in ids) 
{ 
    int localId = id; 
    predicate = predicate.Or(x => x.Source.Id == localId); 
} 

由於LINQ的懶惰你Or謂語,因此id將只進行評估時,執行查詢,並在當時的id是最後一個項目在ids集合中。在這方面,foreach的行爲將在C#5中更改,這不會再成爲問題。欲瞭解更多信息,請參閱"Closing over the loop variable considered harmful"

3

如果這就是你所做的一切,並且列表不長,則根本不需要謂詞構建器。

var result = Database.Set<Customer>().AsExpandable() 
             .Where(x => x.CreatedAt >= fromDate 
                && x.CreatedAt <= toDate 
                && ids.Contains(x.Source.Id)) 
             .ToList(); 

如果你要使用的謂詞建設者,那麼你需要建立或條款完全,然後和所有的它一氣呵成。此外,您需要使用本地id變量,否則它將關閉迭代變量並僅獲取其最後的值邊界。

// This should translate to True AND (u AND x) AND (FALSE OR y OR z) 
var predicate = PredicateBuilder.True<Customer>(); 

predicate = predicate.And(x => x.CreatedAt >= fromDate && x.CreatedAt <= toDate);  

var idPredicate = PredicateBuilder.False<Customer>();  
foreach (var id in ids)  
{ 
    var localId = id; 
    idPredicate = idPredicate.Or(x => x.Source.Id == localId);  
} 

predicate = predicate.And(idPredicate);