【问题标题】:Recursively (?) compose LINQ predicates into a single predicate递归 (?) 将 LINQ 谓词组合成单个谓词
【发布时间】:2011-04-16 12:24:43
【问题描述】:

(编辑:我问错了问题。我遇到的真正问题已经在Compose LINQ-to-SQL predicates into a single predicate 结束了——但是这个问题得到了一些很好的答案,所以我把它留了下来!)

给定以下搜索文本:

"keyword1 keyword2 keyword3   ... keywordN"

我希望得到以下 SQL:

SELECT [columns] FROM Customer 
  WHERE 
    (Customer.Forenames LIKE '%keyword1%' OR Customer.Surname LIKE '%keyword1%')
  AND
     (Customer.Forenames LIKE '%keyword2%' OR Customer.Surname LIKE '%keyword2%')
  AND 
    (Customer.Forenames LIKE '%keyword3%' OR Customer.Surname LIKE '%keyword3%')
  AND
    ...
  AND 
    (Customer.Forenames LIKE '%keywordN%' OR Customer.Surname LIKE '%keywordN%')

实际上,我们将搜索文本拆分为空格,修剪每个标记,基于每个标记构造一个多部分 OR 子句,然后将这些子句“与”在一起。

我在 Linq-to-SQL 中执行此操作,但我不知道如何根据任意长的子谓词列表动态组合谓词。对于已知数量的子句,手动组合谓词很容易:

dataContext.Customers.Where(
    (Customer.Forenames.Contains("keyword1") || Customer.Surname.Contains("keyword1")
    &&
    (Customer.Forenames.Contains("keyword2") || Customer.Surname.Contains("keyword2")
    &&
    (Customer.Forenames.Contains("keyword3") || Customer.Surname.Contains("keyword3")
);

但我想处理任意搜索词列表。我做到了

Func<Customer, bool> predicate = /* predicate */;
foreach(var token in tokens) {
    predicate = (customer 
        => predicate(customer) 
        && 
         (customer.Forenames.Contains(token) || customer.Surname.Contains(token));
}

这会产生 StackOverflowException - 大概是因为赋值的 RHS 上的 predicate() 直到运行时才真正被评估,此时它最终会调用自己......或其他东西。

简而言之,我需要一种技术,在给定两个谓词的情况下,将返回一个由提供的运算符组成两个源谓词的单个谓词,但仅限于 Linq-to-SQL 明确支持的运算符。有什么想法吗?

【问题讨论】:

  • @KennyTM - 不确定我是否理解你的意思......你能详细说明(请)吗?

标签: linq linq-to-sql recursion functional-programming composition


【解决方案1】:

我会建议另一种技术

你可以这样做:

var query = dataContext.Customers;

然后,在一个循环中做

foreach(string keyword in keywordlist)
{
    query = query.Where(Customer.Forenames.Contains(keyword) || Customer.Surname.Contains(keyword));
}

【讨论】:

  • 当然......当你这样说的时候,它看起来很简单:) 太棒了。谢谢。
  • 哦。我问错了问题。看看stackoverflow.com/questions/3782940/… 了解我真正想要实现的目标。哎呀。
【解决方案2】:

如果您想要更简洁和声明式的编写方式,您还可以使用Aggregate 扩展方法而不是foreach 循环和可变变量:

var query = keywordlist.Aggregate(dataContext.Customers, (q, keyword) => 
    q.Where(Customer.Forenames.Contains(keyword) || 
            Customer.Surname.Contains(keyword)); 

这会将dataContext.Customers 作为初始状态,然后使用给定的聚合函数(它只调用Where,正如 Gnomo 建议的那样)为列表中的每个关键字更新此状态(查询)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-09
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多