Есть ли способ сравнить строки в выражении LINQ C #, аналогичном оператору 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 не работает. Что я не так?
Like
или функция, которая преобразуетсяLike
в ORM, который вы используете? Если последнее, то какой ORM? Также примите один из ответов, если он вам помог. Люди продолжают накапливать ответы, основанные только на смутных предположениях о том, о чем вы спрашиваете.Ответы:
Обычно вы используете
String.StartsWith
/EndsWith
/Contains
. Например:var portCode = Database.DischargePorts .Where(p => p.PortName.Contains("BALTIMORE")) .Single() .PortCode;
Я не знаю, есть ли способ делать правильные регулярные выражения через LINQ to SQL. (Обратите внимание, что это действительно зависит от того, какого поставщика вы используете - это было бы хорошо в LINQ to Objects; это вопрос того, может ли поставщик преобразовать вызов в свой собственный формат запроса, например, SQL.)
РЕДАКТИРОВАТЬ: Как говорит BitKFu,
Single
следует использовать, когда вы ожидаете ровно один результат - когда это ошибка, чтобы этого не было. ВариантыSingleOrDefault
,FirstOrDefault
илиFirst
должны быть использованы в зависимости от точности то , что ожидается.источник
Single()
,SingleOrDefault()
был бы моим следующим шагом, если мы не поймем полный контекст ...Регулярное выражение? нет. Но для этого запроса вы можете просто использовать:
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.источник
Что ж ... иногда это может быть неудобно использовать
Contains
,StartsWith
илиEndsWith
особенно при поиске состояния определения значения,LIKE
например, переданное значение "%" требует от разработчика использовать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; } }
источник
IEnumerable
?В собственном 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), чтобы использовать эту функцию.
источник
StartsWith()
, что иContains()
т. Д. Не работают с Linq2SQL (по крайней мере, я получаю «Выражение LINQ… не может быть переведено…» и инструкцию использовать ToList () для «оценки клиента» - что я m уже работает. Обратите внимание, что в EF Core он перемещен вEF.Functions.Like()
Как уже упоминали Джон Скит и Марк Грейвелл, вы можете просто взять условие содержания. Но в случае вашего подобного запроса очень опасно использовать оператор Single (), потому что это означает, что вы найдете только 1 результат. В случае большего количества результатов вы получите приятное исключение :)
Поэтому я бы предпочел использовать FirstOrDefault () вместо Single ():
var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE")); var portcode = first != null ? first.PortCode : string.Empty;
источник
.Where(e => e.Value.StartsWith("BALTIMORE"))
Это работает как "КАК" SQL ...
источник
Такой простой
string[] users = new string[] {"Paul","Steve","Annick","Yannick"}; var result = from u in users where u.Contains("nn") select u;
Результат -> Анник, Янник
источник
В идеале вы должны использовать
StartWith
илиEndWith
.Вот пример:
DataContext dc = new DCGeneral(); List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList(); return lstPerson;
источник
Вы можете вызвать единственный метод с предикатом:
var portCode = Database.DischargePorts .Single(p => p.PortName.Contains("BALTIMORE")) .PortCode;
источник
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;
источник
Просто добавьте в строковые методы расширения объекта.
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;
источник
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();
источник
У @adobrzyc была отличная настраиваемая
LIKE
функция - я просто хотел поделиться ееIEnumerable
версией.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) }); private static 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).Compile(); } public static IEnumerable<TSource> Like<TSource, TMember>(this IEnumerable<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; } }
источник
Как расширение Linq / SQL
Класс LikeExtension
Протестировано в .NET 5
public static class LikeExtension { private static string ColumnDataBase<TEntity, TKey>(IModel model, Expression<Func<TEntity, TKey>> predicate) where TEntity : class { ITable table = model .GetRelationalModel() .Tables .First(f => f .EntityTypeMappings .First() .EntityType == model .FindEntityType(predicate .Parameters .First() .Type )); string column = (predicate.Body as MemberExpression).Member.Name; string columnDataBase = table.Columns.First(f => f.PropertyMappings.Count(f2 => f2.Property.Name == column) > 0).Name; return columnDataBase; } public static IQueryable<TEntity> Like<TEntity, TKey>(this DbContext context, Expression<Func<TEntity, TKey>> predicate, string text) where TEntity : class { string columnDataBase = ColumnDataBase(context.Model, predicate); return context.Set<TEntity>().FromSqlRaw(context.Set<TEntity>().ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text); } public static async Task<IEnumerable<TEntity>> LikeAsync<TEntity, TKey>(this DbContext context, Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) where TEntity : class { string columnDataBase = ColumnDataBase(context.Model, predicate); return await context.Set<TEntity>().FromSqlRaw(context.Set<TEntity>().ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text).ToListAsync(cancellationToken); } public static async Task<IEnumerable<TEntity>> LikeAsync<TEntity, TKey>(this IQueryable<TEntity> query, Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) where TEntity : class { DbSet<TEntity> entities = query as DbSet<TEntity>; string columnDataBase = ColumnDataBase(entities.EntityType.Model, predicate); return await entities.FromSqlRaw(query.ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text).ToListAsync(cancellationToken); } public static IQueryable<TEntity> Like<TEntity, TKey>(this IQueryable<TEntity> query, Expression<Func<TEntity, TKey>> predicate, string text) where TEntity : class { DbSet<TEntity> entities = query as DbSet<TEntity>; string columnDataBase = ColumnDataBase(entities.EntityType.Model, predicate); return entities.FromSqlRaw(query.ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text); } }
Репозиторий
public async Task<IEnumerable<TEntity>> LikeAsync<TKey>(Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) { return await context.LikeAsync(predicate, text, cancellationToken); } public IQueryable<TEntity> Like<TKey>(Expression<Func<TEntity, TKey>> predicate, string text) { return context.Like(predicate, text); }
Использовать
IQueryable<CountryEntity> result = countryRepository .Like(k => k.Name, "%Bra[sz]il%") /*Use Sync*/ .Where(w => w.DateRegister < DateTime.Now) /*Example*/ .Take(10); /*Example*/
Или
IEnumerable<CountryEntity> result = await countryRepository .LikeAsync(k => k.Name, "%Bra[sz]il%", cancellationToken); /*Use Async*/
Или
IQueryable<CountryEntity> result = context.Countries .Like(k => k.Name, "%Bra[sz]il%") .Where(w => w.Name != null); /*Example*/
Или
List<CountryEntity> result2 = await context.Countries .Like(k => k.Name, "%Bra[sz]il%") .Where(w => w.Name != null) /*Example*/ .ToListAsync(); /*Use Async*/
Или
IEnumerable<CountryEntity> result3 = await context.Countries .Where(w => w.Name != null) .LikeAsync(k => k.Name, "%Bra[sz]il%", cancellationToken); /*Use Async*/
источник