Глядя на вопрос
Что, если я хочу что-то вроде% Test, если% it работает%, как мне это сделать?
тогда я ожидаю чего-то от
LIKE '%Test if%it work%'
означает, что строка должна содержать «Test if» и «it works», в этом порядке .
Это не будет работать:
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 это так.
IndexOf найдет строку поиска и вернет ее положение в строке. Позволяет найти слова в правильном порядке.
- Обновление -
С моим первоначальным ответом моей целью было не дать общее решение, а скорее пример другого подхода, который не зависит от SQL. Поэтому правильно, что оригинальный пример отвечает только на буквальный вопрос. Но так как ответ может быть более полезным, если он универсальный, я написал расширение IQuerable, которое позволяет добавлять оператор like в запрос так же просто, как оператор where. Расширение работает как для Linq, так и для Linq-Sql.
Здесь будут найдены все записи с «Test if» и «it work» в указанном порядке.
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))
По поводу производительности, кажется, есть некоторые дискуссии о том, что лучше: LIKE или CHARINDEX. И из того, что я прочитал, CHARINDEX кажется любимым.