(早期的一个问题,Recursively (?) compose LINQ predicates into a single predicate,与此类似,但我实际上提出了错误的问题......那里的解决方案满足了所提出的问题,但实际上并不是我所需要的。它们是不同的,虽然。诚实。)
给出以下搜索文本:
"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%'
)
实际上,我们将搜索文本拆分为空格,修剪每个标记,根据每个标记构建一个多部分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明确支持的运算符。有什么想法吗?
答案 0 :(得分: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);
}
(这实际上是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)
{
...
}
答案 1 :(得分:0)
通常你会链接.Where(...)
的调用。 E.g:
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);