2012-02-07 75 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);