Оператор 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();