【问题标题】:Nesting OR using Linq PredicateBuilder使用 Linq PredicateBuilder 嵌套 OR
【发布时间】:2012-02-07 22:25:24
【问题描述】:

我正在使用谓词生成器编写以下代码:

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 中被分配了两次。不知道为什么?

【问题讨论】:

    标签: linq predicatebuilder


    【解决方案1】:
    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 是 @ 中的最后一项987654328@收藏。 foreach 在这方面的行为将在 C# 5 中改变,这将不再是问题。更多信息请阅读"Closing over the loop variable considered harmful"

    【讨论】:

      【解决方案2】:

      如果这就是你所做的全部并且你的列表不长,那么你根本不需要谓词构建器。

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

      如果您要使用谓词构建器,那么您需要完全构建 OR 子句,然后一次性完成所有操作。此外,您需要使用本地 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 );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多