У меня есть поле "Название", и вы хотите частично искать данные, введенные пользователем, которые могут быть разделены пробелами. Скажем, что пользователь вводит "ABC India PVT Ltd", запрос должен искать все записи, содержащие какие-либо одно или несколько введенных слов. как в sql, это было бы
Select *
from Company
where CompanyName like '%ABC%' or
CompanyName like '%India%' or
CompanyName like '%PVT%' or
CompanyName like '%Ltd%'
Я пытаюсь сделать что-то подобное
string search = "ABC India PVT Ltd"
String[] searchArray = search.Split(' ');
IEnumerable<Account> accountInfo = acctInfo.Get(Filter: a
=>searchArray.AsQueryable().Contains(a.CompanyName));
но это дает мне точно противоположность тому, чего я пытаюсь достичь. Есть ли способ, которым я могу это достичь.
Основная идея заключается в подстановочном поиске, в поле companyname для любого из значений searchArray.
a.CompanyName.Contains("Любое значение из searchArray")
Это делает трюк. Использовать Any() для фильтрации записей, содержащих одно из ключевых слов:
string searchTerm = "ABC India PVT Ltd";
string[] keywords = searchTerm.Split(' ');
List<Records> records = new List<Records>();
records.Add(new Records() { CompanyName = "Foo name" });
records.Add(new Records() { CompanyName = "ABC name" });
records.Add(new Records() { CompanyName = "Foo India" });
records.Add(new Records() { CompanyName = "PVT name" });
records.Add(new Records() { CompanyName = "Foo name" });
records.Add(new Records() { CompanyName = "Foobar" });
records.Add(new Records() { CompanyName = "Stackoverflow" });
var results = records.Where(x => keywords.Any(keyword => x.CompanyName
.Contains(keyword))).ToList();
Класс манекена:
public class Records
{
public Records() { }
public string CompanyName { get; set; }
}
Дам вам List<Record>
записей которого CompanyName
содержит любые из ключевых слов. Например
Решение с Any
предложенное DGibbs, подходит для небольшого количества элементов (что, вероятно, будет иметь место для вас, так что я его поддержал), но EF не может создать эффективный запрос из этой конструкции. Вот как выглядит запрос с 4 элементами:
SELECT
-- some fields
FROM [dbo].[Company] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM (SELECT
[UnionAll2].[C1] AS [C1]
FROM (SELECT
[UnionAll1].[C1] AS [C1]
FROM (SELECT
N'a' AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
N'b' AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]
UNION ALL
SELECT
N'c' AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
UNION ALL
SELECT
N'd' AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable4]) AS [UnionAll3]
WHERE ( CAST(CHARINDEX([UnionAll3].[C1], [Extent1].[Name]) AS int)) > 0
)
Это не масштабируемо. Помимо определенного количества элементов (только десятки) запрос будет генерировать исключение SQL, в котором был превышен максимальный уровень вложенности.
Чтобы предотвратить это, вы должны создать запрос с предложениями OR
. Здесь PredicateBuilder поможет:
var predicate = PredicateBuilder.False<Company>();
foreach (var keyword in searchArray)
{
predicate = predicate.Or(c => c.Name.Contains(keyword));
}
var query = Companies.Where(predicate.Expand());
Есть, кстати, альтернатива исходному коду LINQKit PredicateBuilder, которая делает то же самое, но без Expand
.