НРАВИТСЯ оператор в LINQ - PullRequest
82 голосов
/ 21 марта 2011

Есть ли способ сравнить строки в выражении C # LINQ, похожем на оператор LIKE в SQL?

Предположим, у меня есть список строк. В этом списке я хочу найти строку. В 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 не работает. Что я понял не так?

Ответы [ 12 ]

128 голосов
/ 21 марта 2011

Обычно вы используете 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 в зависимости от точно того, что ожидается.

30 голосов
/ 21 марта 2011

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.

10 голосов
/ 25 февраля 2016

Ну ... иногда может быть неудобно использовать 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;
    }
}
8 голосов
/ 21 марта 2011

Как уже упоминали Джон Скит и Марк Гравелл, вы можете просто принять условие содержания.Но в случае с вашим запросом like очень опасно использовать оператор Single (), потому что это означает, что вы найдете только 1 результат.В случае получения большего количества результатов вы получите приятное исключение:)

Поэтому я бы предпочел использовать FirstOrDefault () вместо Single ():

var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE"));
var portcode = first != null ? first.PortCode : string.Empty;
7 голосов
/ 06 апреля 2016

В родном LINQ вы можете использовать комбинацию Contains/StartsWith/EndsWith или RegExp.

В LINQ2SQL используйте метод SqlMethods.Like()

    from i in db.myTable
    where SqlMethods.Like(i.field, "tra%ata")
    select i

добавить сборку: System.Data.Linq (в System.Data.Linq.dll), чтобы использовать эту функцию.

3 голосов
/ 01 января 2015

Простой, как этот

string[] users = new string[] {"Paul","Steve","Annick","Yannick"};    
var result = from u in users where u.Contains("nn") select u;

Результат -> Анник, Янник

2 голосов
/ 09 июня 2016

В идеале вы должны использовать StartWith или EndWith.

Вот пример:

DataContext  dc = new DCGeneral();
List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList();

return lstPerson;
2 голосов
/ 27 декабря 2012
  .Where(e => e.Value.StartsWith("BALTIMORE"))

Это работает как "НРАВИТСЯ" в SQL ...

2 голосов
/ 21 марта 2011

Вы можете вызвать единственный метод с предикатом:

var portCode = Database.DischargePorts
                   .Single(p => p.PortName.Contains("BALTIMORE"))
                   .PortCode;
0 голосов
/ 05 октября 2017

Я нашел решение для имитации оператора SQL LIKE. Обратитесь к ответу, который я напишу здесь https://stackoverflow.com/a/46592475/1186073

...