Поиск по подстановочным знакам для LINQ
Вопрос
Я хотел бы знать, можно ли выполнить поиск по подстановочным знакам с помощью LINQ.
Я вижу, что в LINQ есть contains, StartsWith, EndsWith и т. д.
Что, если мне нужно что-то вроде %Проверить, если%это работает%, как мне это сделать?
С уважением
Решение
Я бы использовал регулярные выражения, поскольку вы не всегда можете использовать Linq to SQL.
Как этот пример Linq to Objects
List<string> list = new List<string>();
list.Add("This is a sentence.");
list.Add("This is another one.");
list.Add("C# is fun.");
list.Add("Linq is also fun.");
System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex("This");
var qry = list
.Where<string>(item => regEx.IsMatch(item))
.ToList<string>();
// Print results
foreach (var item in qry)
{
Console.WriteLine(item);
}
Другие советы
Вы можете использовать SqlMethods.Like().
Пример использования:
var results =
from u in users
where SqlMethods.Like(u.FirstName, "%John%")
select u;
добавьте System.Data.Linq.SqlClient в список использования или импорта, а затем попробуйте:
var results= from x in data
where SqlMethods.Like(x.SearchField, “%something%like%this%”)
select x;
Для Entity Framework Core 2.0
есть LIKE
оператор (объявлено в августе 2017 г.):
var query = from e in _context.Employees
where EF.Functions.Like(e.Title, "%developer%")
select e;
Глядя на вопрос
Что, если мне нужно что-то вроде %Проверить, если%это работает%, как мне это сделать?
тогда я ожидаю чего-то
LIKE '%Test if%it work%'
это означает, что строка должна содержать «Проверить, если» и «это работает», в этой последовательности.
Так не пойдет:
context.SomeTable.Where(s => s.Name.Contains("Test if%it work")).ToList();
И если я использую:
context.SomeTable.Where(s => s.Name.Contains("Test if") && s.Name.Contains("it work")).ToList();
тогда я найду все записи, содержащие как «Проверить, если», так и «это работает», но не конкретно в таком порядке.
Итак, с Содержит это невозможно.Но с Индекс это.
IndexOf найдет строку поиска И вернет ее позицию в строке.Даем возможность найти слова в правильном порядке.
-- Обновлять --
В моем первоначальном ответе моей целью было не предоставить общее решение, а скорее пример другого подхода, не зависящего от sql.Так что правильно, что исходный пример отвечает только на буквальный вопрос.Но поскольку ответ может быть более полезным, если он является общим, я написал расширение IQuerable, которое позволяет добавлять в запрос оператор Like так же просто, как операторwhere.Расширение работает как для Linq, так и для Linq-Sql.
При этом будут найдены все записи как с «Проверить, если», так и с «работает» в указанном порядке.
context.SomeTable.Like("test if%it work", "Name").ToList();
listOfString.Like("test if%it work").ToList();
Расширение допускает любое количество подстановочных знаков:
/// <summary>
/// Allow to search the string with wildcards.
/// </summary>
/// <typeparam name="T">String or an object with a string member.</typeparam>
/// <param name="q">Original query</param>
/// <param name="searchstring">The searchstring</param>
/// <param name="memberName">The name of the field or null if not a field.</param>
/// <returns>Query filtered by 'LIKE'.</returns>
public static IQueryable<T> Like<T>(this IQueryable<T> q, string searchstring, string memberName = null)
{
// %a%b%c% --> IndexOf(a) > -1 && IndexOf(b) > IndexOf(a) && IndexOf(c) > IndexOf(b)
var eParam = Expression.Parameter(typeof(T), "e");
MethodInfo methodInfo;
// Linq (C#) is case sensitive, but sql isn't. Use StringComparison ignorecase for Linq.
// Sql however doesn't know StringComparison, so try to determine the provider.
var isLinq = (q.Provider.GetType().IsGenericType && q.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>));
if (isLinq)
methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
else
methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) });
Expression expr;
if (string.IsNullOrEmpty(memberName))
expr = eParam;
else
expr = Expression.Property(eParam, memberName);
// Split the searchstring by the wildcard symbol:
var likeParts = searchstring.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < likeParts.Length; i++)
{
MethodCallExpression e;
if (isLinq)
e = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
else
e = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i], typeof(string)));
if (i == 0)
{
// e.IndexOf("likePart") > -1
q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(e, Expression.Constant(-1, typeof(int))), eParam));
}
else
{
// e.IndexOf("likePart_previous")
MethodCallExpression ePrevious;
if (isLinq)
ePrevious = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i - 1], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
else
ePrevious = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i - 1], typeof(string)));
// e.IndexOf("likePart_previous") < e.IndexOf("likePart")
q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.LessThan(ePrevious, e), eParam));
}
}
return q;
}
Поскольку SqlMethods не требуется, я предполагаю, что вы можете использовать его для любой базы данных, например MySql или Postgresql.Но я не знаю наверняка.Я проверил это на Sql Server, используя Entity Framework 6.Приведенный выше оператор генерирует следующий код на Sql Server.
SELECT [Extent1].* FROM SomeTable AS [Extent1]
WHERE ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) > -1)
AND ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) <
(( CAST(CHARINDEX(N'it work', [Extent1].[Name]) AS int)) - 1))
Что касается производительности, похоже, идет дискуссия о том, что «лучше»:ЛАЙК или ЧАРИНДЕКС.И судя по тому, что я прочитал, ЧАРИНДЕКС кажется моим фаворитом.
.Where( column LIKE "Pattern")
var result = (from x in db.Members
where x.IDNumber.Contains(idnumber)
&& x.InstitutionIdentifier == institution.Identifier
select x).ToList();
return result;
Будет работать как для Linq to SQL, так и для Linq в памяти.
Я знаю, что это старая тема, но вот мое очень простое решение:
string s=Regex.Escape("pattern - escaped for sanity").Replace("%", ".*").Replace("_", ".?");
user => Regex.IsMatch(user.FullName, s, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
В этом коде я использую общие escape-символы для языка SQL.Если вы хотите использовать, скажите *
и ?
, экранированная строка будет содержать \*
и \?
соответственно, обязательно включите обратная косая черта персонаж в .Replace(...)
заявления).Конечно, если вы хотите предоставить пользователю возможность поиска RexEx, просто не экранируйте строку шаблона.
Найдите в учебнике Regex другие варианты.
Я считаю, что нормально %
будет соответствовать хотя бы один персонаж, а RegEx .*
будет соответствовать ноль или больше персонажи.Так что на самом деле, %
подстановочный знак больше похож на .+
(жадный), а не .*
(ленивый).
Надеюсь это поможет.
не уверен, говорите ли вы на LinqToSql или просто на linq...но вы можете использовать такие регулярные выражения:
.Where(dto => System.Text.RegularExpressions.Regex.IsMatch(dto.CustomerName, @"Ad"));
В коде .Net, включая LINQ to Objects, я использую реализацию IsSqlLikeMatch функция из потока Использование Regex для создания подобной функции SQL..
Пример использования
bool ret = message.IsSqlLikeMatch(pattern);
Подробности в моем постеШаблоны "like" SQL для сравнения в .Net
Вы также можете использовать «содержит»
var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring));
Вы говорите о LINQ to object или LINQ to SQL?
Для LINQ to объектов вам придется прибегнуть к обычные выражения я думаю.