将LINQ-to-SQL谓词编写为单个谓词

Dyl*_*tie 5 c# linq functional-programming composition linq-to-sql

(早期的问题,递归(?)将LINQ谓词组合成单个谓词,与此类似但我实际上问了错误的问题......那里的解决方案满足了提出的问题,但实际上并不是我需要的问题.虽然不一样.诚实.)

鉴于以下搜索文本:

"keyword1 keyword2 ... keywordN"
Run Code Online (Sandbox Code Playgroud)

我想最终得到以下SQL:

SELECT [columns] FROM Customer 
  WHERE (
        Customer.Forenames LIKE '%keyword1%' 
        OR 
        Customer.Forenames LIKE '%keyword2%'
        OR
        ...
        OR
        Customer.Forenames LIKE '%keywordN%'
    ) AND (
        Customer.Surname LIKE '%keyword1%' 
        OR 
        Customer.Surname LIKE '%keyword2%'
        OR
        ....
        OR
        Customer.Surname LIKE '%keywordN%'
    )
Run Code Online (Sandbox Code Playgroud)

实际上,我们将搜索文本拆分为空格,修剪每个标记,根据每个标记构造一个多部分OR子句,然后将这些子句一起进行AND运算.

我在Linq-to-SQL中这样做,我不知道如何根据任意长的子预测列表动态编写谓词.对于已知数量的子句,可以轻松地手动编写谓词:

dataContext.Customers.Where(
    ( 
      Customer.Forenames.Contains("keyword1") 
      ||
      Customer.Forenames.Contains("keyword2")
    ) && (
      Customer.Surname.Contains("keyword1") 
      ||
      Customer.Surname.Contains("keyword2")
    )
);
Run Code Online (Sandbox Code Playgroud)

简而言之,我需要一种技术,给定两个谓词,将返回使用提供的运算符组成两个源谓词的单个谓词,但仅限于Linq-to-SQL明确支持的运算符.有任何想法吗?

Tho*_*que 6

你可以使用这PredicateBuilder门课

IQueryable<Customer> SearchCustomers (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Customer>();

  foreach (string keyword in keywords)
  {
    // Note that you *must* declare a variable inside the loop
    // otherwise all your lambdas end up referencing whatever
    // the value of "keyword" is when they're finally executed.
    string temp = keyword;
    predicate = predicate.Or (p => p.Forenames.Contains (temp));
  }
  return dataContext.Customers.Where (predicate);
}
Run Code Online (Sandbox Code Playgroud)

(这实际上是PredicateBuilder页面上的例子,我只是根据你的情况调整了......)


编辑:

实际上我误读了你的问题,我上面的例子只涉及解决方案的一部分......以下方法应该做你想要的:

IQueryable<Customer> SearchCustomers (string[] forenameKeyWords, string[] surnameKeywords)
{
    var predicate = PredicateBuilder.True<Customer>();

    var forenamePredicate = PredicateBuilder.False<Customer>();
    foreach (string keyword in forenameKeyWords)
    {
      string temp = keyword;
      forenamePredicate = forenamePredicate.Or (p => p.Forenames.Contains (temp));
    }
    predicate = PredicateBuilder.And(forenamePredicate);

    var surnamePredicate = PredicateBuilder.False<Customer>();
    foreach (string keyword in surnameKeyWords)
    {
      string temp = keyword;
      surnamePredicate = surnamePredicate.Or (p => p.Surnames.Contains (temp));
    }
    predicate = PredicateBuilder.And(surnamePredicate);

    return dataContext.Customers.Where(predicate);
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

var query = SearchCustomers(
    new[] { "keyword1", "keyword2" },
    new[] { "keyword3", "keyword4" });

foreach (var Customer in query)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)