Оператор LIKE в LINQ
Есть ли способ сравнить строки в выражении С# LINQ, аналогичном оператору SQL LIKE
?
Предположим, что у меня есть список строк. В этом списке я хочу найти строку. В SQL я мог бы написать:
SELECT * FROM DischargePort WHERE PortName LIKE '%BALTIMORE%'
Вместо вышесказанного запрос требует синтаксиса linq.
using System.Text.RegularExpressions;
…
var regex = new Regex(sDischargePort, RegexOptions.IgnoreCase);
var sPortCode = Database.DischargePorts
.Where(p => regex.IsMatch(p.PortName))
.Single().PortCode;
Мой синтаксис LINQ не работает. Что я не понял?
Ответы
Ответ 1
Обычно вы используете String.StartsWith
/EndsWith
/Contains
. Например:
var portCode = Database.DischargePorts
.Where(p => p.PortName.Contains("BALTIMORE"))
.Single()
.PortCode;
Я не знаю, есть ли способ делать правильные регулярные выражения с помощью LINQ to SQL. (Обратите внимание, что это действительно зависит от того, какой провайдер вы используете - в LINQ to Objects это будет хорошо, и вопрос о том, может ли провайдер преобразовать вызов в свой собственный формат запроса, например SQL.)
EDIT: Как говорит BitKFu, Single
следует использовать, когда вы ожидаете ровно один результат - когда это ошибка, если это не так. Параметры SingleOrDefault
, FirstOrDefault
или First
должны использоваться в зависимости от того, что ожидалось.
Ответ 2
Regex? нет. Но для этого запроса вы можете просто использовать:
string filter = "BALTIMORE";
(blah) .Where(row => row.PortName.Contains(filter)) (blah)
Если вы действительно хотите SQL LIKE
, вы можете использовать System.Data.Linq.SqlClient.SqlMethods.Like(...)
, который LINQ-to-SQL сопоставляет с LIKE
в SQL Server.
Ответ 3
Ну... иногда может быть неудобно использовать Contains
, StartsWith
или EndsWith
, особенно когда поиск определяет значение LIKE
statment, например. прошлое 'value%' требует от разработчика использовать функцию StartsWith
в выражении. Поэтому я решил написать расширение для объектов IQueryable
.
Использование
// numbers: 11-000-00, 00-111-00, 00-000-11
var data1 = parts.Like(p => p.Number, "%11%");
// result: 11-000-00, 00-111-00, 00-000-11
var data2 = parts.Like(p => p.Number, "11%");
// result: 11-000-00
var data3 = parts.Like(p => p.Number, "%11");
// result: 00-000-11
код
public static class LinqEx
{
private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains");
private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
public static Expression<Func<TSource, bool>> LikeExpression<TSource, TMember>(Expression<Func<TSource, TMember>> property, string value)
{
var param = Expression.Parameter(typeof(TSource), "t");
var propertyInfo = GetPropertyInfo(property);
var member = Expression.Property(param, propertyInfo.Name);
var startWith = value.StartsWith("%");
var endsWith = value.EndsWith("%");
if (startWith)
value = value.Remove(0, 1);
if (endsWith)
value = value.Remove(value.Length - 1, 1);
var constant = Expression.Constant(value);
Expression exp;
if (endsWith && startWith)
{
exp = Expression.Call(member, ContainsMethod, constant);
}
else if (startWith)
{
exp = Expression.Call(member, EndsWithMethod, constant);
}
else if (endsWith)
{
exp = Expression.Call(member, StartsWithMethod, constant);
}
else
{
exp = Expression.Equal(member, constant);
}
return Expression.Lambda<Func<TSource, bool>>(exp, param);
}
public static IQueryable<TSource> Like<TSource, TMember>(this IQueryable<TSource> source, Expression<Func<TSource, TMember>> parameter, string value)
{
return source.Where(LikeExpression(parameter, value));
}
private static PropertyInfo GetPropertyInfo(Expression expression)
{
var lambda = expression as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("expression");
MemberExpression memberExpr = null;
switch (lambda.Body.NodeType)
{
case ExpressionType.Convert:
memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
break;
case ExpressionType.MemberAccess:
memberExpr = lambda.Body as MemberExpression;
break;
}
if (memberExpr == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
var output = memberExpr.Member as PropertyInfo;
if (output == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
return output;
}
}
Ответ 4
Как уже упоминал Джон Скит и Марк Гравелл, вы можете просто принять условие наличия. Но в случае вашего подобного запроса очень опасно принимать оператор Single(), потому что это означает, что вы найдете только 1 результат. В случае большего количества результатов вы получите прекрасное исключение:)
Поэтому я бы предпочел использовать FirstOrDefault() вместо Single():
var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE"));
var portcode = first != null ? first.PortCode : string.Empty;
Ответ 5
В родном LINQ вы можете использовать комбинацию Contains/StartsWith/EndsWith
или RegExp.
В методе LINQ2SQL SqlMethods.Like()
from i in db.myTable
where SqlMethods.Like(i.field, "tra%ata")
select i
добавьте Assembly: System.Data.Linq(в System.Data.Linq.dll), чтобы использовать эту функцию.
Ответ 6
Прост, как этот
string[] users = new string[] {"Paul","Steve","Annick","Yannick"};
var result = from u in users where u.Contains("nn") select u;
Результат → Annick, Yannick
Ответ 7
Вы можете вызывать единственный метод с предикатом:
var portCode = Database.DischargePorts
.Single(p => p.PortName.Contains("BALTIMORE"))
.PortCode;
Ответ 8
.Where(e => e.Value.StartsWith("BALTIMORE"))
Это работает как "LIKE" SQL...
Ответ 9
В идеале вы должны использовать StartWith
или EndWith
.
Вот пример:
DataContext dc = new DCGeneral();
List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList();
return lstPerson;
Ответ 10
public static class StringEx
{
public static bool Contains(this String str, string[] Arr, StringComparison comp)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.IndexOf(s, comp)>=0)
{ return true; }
}
}
return false;
}
public static bool Contains(this String str,string[] Arr)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.Contains(s))
{ return true; }
}
}
return false;
}
}
var portCode = Database.DischargePorts
.Single(p => p.PortName.Contains( new string[] {"BALTIMORE"}, StringComparison.CurrentCultureIgnoreCase) ))
.PortCode;
Ответ 11
Просто добавьте методы расширения объектов строки.
public static class StringEx
{
public static bool Contains(this String str, string[] Arr, StringComparison comp)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.IndexOf(s, comp)>=0)
{ return true; }
}
}
return false;
}
public static bool Contains(this String str,string[] Arr)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.Contains(s))
{ return true; }
}
}
return false;
}
}
использование:
use namespase that contains this class;
var sPortCode = Database.DischargePorts
.Where(p => p.PortName.Contains(new string [] {"BALTIMORE"}, StringComparison.CurrentCultureIgnoreCase) )
.Single().PortCode;
Ответ 12
Я нашел решение для имитации оператора SQL LIKE. См. Ответ, который я написал здесь.
fooobar.com/questions/19890/...
Ответ 13
List<Categories> categoriess;
private void Buscar()
{
try
{
categoriess = Contexto.Categories.ToList();
categoriess = categoriess.Where(n => n.CategoryID >= Convert.ToInt32(txtCatID.Text) && n.CategoryID <= Convert.ToInt32(txtCatID1.Text) && (n.CategoryName.Contains(txtCatName.Text)) ).ToList();