【问题标题】:PredicateBuilder and nested predicatesPredicateBuilder 和嵌套谓词
【发布时间】:2009-11-19 17:30:35
【问题描述】:

我正在尝试使用来自 Albahari 的谓词来创建一个 TSQL 语句,例如:

select * from channel
where channel.VendorID IN (@vendorIDs)
AND channel.FranchiseID IN (@franchiseIDs)

或类似的谓词:c => (c.VendorID = x || c.VendorID == x2 ...) && (c.FranchiseID == f || c.FranchiseID == f2 ...)

但我遇到了麻烦。这是我的尝试:

    var vendorPredicate = PredicateBuilder.False<Channel>();
    foreach (Vendor vendor in workOrderSessionData.SelectedVendors)
    {
        int tempId = vendor.VendorID;
        vendorPredicate = vendorPredicate.Or(c => c.VendorID == tempId);
    }

    var franchisePredicate = PredicateBuilder.False<Channel>();
    foreach (Franchise franchise in workOrderSessionData.SelectedFranchises)
    {
        int tempId = franchise.FranchiseID;
        franchisePredicate = franchisePredicate.Or(c => c.FranchiseID == tempId);
        // doesn't work franchisePredicate.Or(vendorPredicate);
    }

Channel.SelectByPredicate(franchisePredicate);

我的表有 60,000 行,所以转到数据库并全部选择它们,然后过滤不是一个选项。 Channel 是一个 LinqToSql 实体。此外,SelectedFranchises 或 SelectedVendors 可以为空,但不能同时为空。 编辑:我还需要通过 channel.Franchise.Name 来区分这个列表。也许我应该只使用存储过程?

你会怎么做?

【问题讨论】:

    标签: linq-to-sql predicatebuilder


    【解决方案1】:

    为了避开整个谓词问题,为什么不使用“Contains()”语句?

    IE

    var myResults =
        from c in Channel
        where
            workOrderSessionData.SelectedVendors.Select(sv => 
                sv.VendorID).Contains(c.VendorID)
    
            && workOrderSessionData.SelectedFranchises.Select(sf => 
                sf.FranchiseID).Contains(c.FranchiseID)
        select c;
    Channel.SelectByPredicate(franchisePredicate);
    

    或者,要使用谓词方法,您不希望用“或”连接它们,因为这两个条件在您的示例 SQL 语句中是“和”。相反,只需通过 where 子句连续运行它们。我不知道您的 SelectByPredicate 函数是如何工作的,但您可能会按照相同的模式成功:

    var myResults = Channel.SelectByPredicate(franchisePredicate);
    myResults = myResults.SelectByPredicate(vendorPredicate);
    
    var myResults = Channel.Where(franchisePredicate).Where(vendorPredicate);
    

    评论中讨论的更新

    如果您只想匹配供应商 ID/特许经营 ID(如果 workordersessiondata 至少包含其中一个 ID),则可以使用以下逻辑:

    List<int> VendorIDs = workOrderSessionData.SelectedVendors.Select(sv => 
        sv.VendorID).ToList();
    
    List<int> FranchiseIDs = workOrderSessionData.SelectedFranchises.Select(sf => 
        sf.FranchiseID).ToList();
    
    var myResults = Channel;
    
    if(VendorIDs.Count > 0)
        myResults = MyResults.Where(c => VendorIDs.Contains(c.VendorID));
    
    if(FranchiseIDs.Count > 0)
        myResults = MyResults.Where(c => FranchiseIDs.Contains(c.FranchiseID));
    

    【讨论】:

    • 如果 SelectedVendors 为空或 SelectedFranchises 为空(这是可能的),第一个示例将失败。你会怎么处理? Sql Profiler 中的 where 子句是“WHERE 0 = 1”
    • 失败是指返回0行还是抛出异常?
    • 失败是指数据库中的空结果。感谢您的帮助。
    • 没问题。我问的原因是,如果这些 IN 语句中的任何一个失败,您提供的 SQL 查询也不会返回任何结果,因为它说两个 ID 必须在各自的列表中。听起来您要求这样做,除非其中一个列表为空,然后您只想匹配完整列表。对吗?
    • 当其中一个列表为空时,我仍然需要根据填充的另一个列表进行过滤。所以我需要获取供应商和特许经营商的所有渠道或供应商的所有渠道或特许经营商的所有渠道。
    【解决方案2】:

    好吧,我只是这样做了:

    var prope = outerWhere.ToString();
    if (prope.Equals("f => True") == false || prope.Equals("f => False") == false)
      query = query.Where(outerWhere);
    

    也许是所有版本中最干净的......

    【讨论】:

      【解决方案3】:

      鉴于扩展:

      public static class PredicateExtensions
      {
          public static Predicate<T> Or<T>(this Predicate<T> @this, Predicate<T> or) {
              return value => @this(value) || or(value);
          }
      
          public static Predicate<T> And<T>(this Predicate<T> @this, Predicate<T> and) {
              return value => @this(value) && and(value);
          }
      }
      

      你可以这样做:

          Predicate<Channel> vendorPredicate = c => false;
      
          foreach (var vendor in workOrderSessionData.SelectedVendors)
          {
              int tempId = vendor.VendorID;       
              vendorPredicate = vendorPredicate.Or(c => c.VendorID == tempId);
          }
      
          Predicate<Channel> franchisePredicate = c => false;
          foreach (var franchise in workOrderSessionData.SelectedFranchises)
          {
              int tempid = franchise.FranchiseID;
              franchisePredicate = franchisePredicate.Or(c => c.FranchiseID == tempid);
          }
      
          var channelPredicate = vendorPredicate.And(franchisePredicate);
      
          var query = Channels.Where (c => channelPredicate(c));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-16
        • 1970-01-01
        相关资源
        最近更新 更多