繁体   English   中英

将LINQ-to-SQL谓词组合成单个谓词

[英]Compose LINQ-to-SQL predicates into a single predicate

(一个较早的问题,以递归(?)方式将LINQ谓词组合为一个谓词 ,与此类似,但是我实际上提出了错误的问题...那里的解决方案满足了提出的问题,但实际上不是我所需要的。是不同的。诚实。

给出以下搜索文本:

"keyword1 keyword2 ... keywordN"

我想结束以下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%'
    )

有效地,我们将搜索文本分割成空格,修剪每个标记,基于each构造一个多部分的OR子句,然后将这些子句AND在一起。

我正在用Linq-to-SQL进行此操作,而且我不知道如何基于任意长的子谓词列表动态组成谓词。 对于已知数量的子句,手动组合谓词很容易:

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

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

您可以使用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);
}

(这实际上是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);
}

您可以这样使用它:

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

foreach (var Customer in query)
{
    ...
}

通常,您将链接.Where(...)调用。 例如:

var a = dataContext.Customers;
if (kwd1 != null)
    a = a.Where(t => t.Customer.Forenames.Contains(kwd1));
if (kwd2 != null)
    a = a.Where(t => t.Customer.Forenames.Contains(kwd2));
// ...
return a;

LINQ-to-SQL会将其重新焊接到单个WHERE子句中。

但是,这不适用于OR 您可以使用联合和交叉,但是我不确定LINQ-to-SQL(或SQL Server)是否足够聪明,可以将其折叠回单个WHERE子句。 OTOH,性能不受影响也没关系。 无论如何,它看起来像这样:

<The type of dataContext.Customers> ff = null, ss = null;

foreach (k in keywords) {
    if (keywords != null) {
        var f = dataContext.Customers.Where(t => t.Customer.Forenames.Contains(k));
        ff = ff == null ? f : ff.Union(f);

        var s = dataContext.Customers.Where(t => t.Customer.Surname.Contains(k));
        ss = ss == null ? s : ss.Union(s);
    }
}
return ff.Intersect(ss);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM