Как оператор в Entity Framework?

Мы пытаемся реализовать оператор "LIKE" в Entity Framework для наших объектов со строковыми полями, но он, похоже, не поддерживается. Кто-нибудь еще пытался сделать что-то подобное?

В этом сообщении сообщается о проблеме, которую мы имеем. Мы могли бы использовать contains, но это соответствует только самому тривиальному случаю LIKE. Комбинирование содержит, startswith, endswith, и indexof получает нас там, но требует перевода между стандартными подстановочными знаками и кодом Linq to Entities.

Ответ 1

Это старый пост сейчас, но для тех, кто ищет ответ, эта ссылка должна помочь.

Укороченная версия:

Метод SqlFunctions.PatIndex - возвращает начальную позицию первого вхождения шаблона в указанном выражении или нули, если шаблон не найден, для всех допустимых типов текстовых и символьных данных

Пространство имен: System.Data.Objects.SqlClient Assembly: System.Data.Entity (в System.Data.Entity.dll)

Немного объяснения также появляется в этой теме форума.

Ответ 2

Я ничего не знаю о EF, но в LINQ to SQL вы обычно выражаете предложение LIKE с помощью String.Contains:

where entity.Name.Contains("xyz")

переводится на

WHERE Name LIKE '%xyz%'

(Используйте StartsWith и EndsWith для другого поведения.)

Я не совсем уверен, что это полезно, потому что я не понимаю, что вы имеете в виду, когда говорите, что пытаетесь реализовать LIKE. Если я неправильно понял, дайте мне знать, и я удалю этот ответ:)

Ответ 3

У меня была та же проблема.

В настоящее время я установил фильтрацию подстановочных знаков на стороне клиента на основе http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - он прост и работает так, как ожидалось.

Я нашел еще одно обсуждение этой темы: http://forums.asp.net/t/1654093.aspx/2/10
Это сообщение выглядит многообещающим, если вы используете Entity Framework >= 4.0:

Использовать SqlFunctions.PatIndex:

http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

Вот так:

var q = EFContext.Products.Where(x =>
SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);

Примечание. Это решение предназначено только для SQL-Server, поскольку оно использует нестандартную функцию PATINDEX.

Ответ 4

Обновление: в EF 6.2 есть похожий оператор

Where(i => DbFunctions.Like(searchstring ,like expression)

Ответ 5

В Entity Framework Core 2.0 добавлен оператор LIKE:

var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;

Сравнивая с ... where e.Title.Contains("developer") ..., он действительно переведен на SQL LIKE, а не CHARINDEX, который мы видим для метода Contains.

Ответ 6

Он специально упоминается в документации как часть Entity SQL. Вы получаете сообщение об ошибке?

// LIKE and ESCAPE
// If an AdventureWorksEntities.Product contained a Name 
// with the value 'Down_Tube', the following query would find that 
// value.
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name LIKE 'DownA_%' ESCAPE 'A'

// LIKE
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name like 'BB%'

http://msdn.microsoft.com/en-us/library/bb399359.aspx

Ответ 7

Если вы используете MS Sql, я написал 2 метода расширения для поддержки символа% для поиска по шаблону. (Требуется LinqKit)

public static class ExpressionExtension
{
    public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> expr, string likeValue)
    {
        var paramExpr = expr.Parameters.First();
        var memExpr = expr.Body;

        if (likeValue == null || likeValue.Contains('%') != true)
        {
            Expression<Func<string>> valExpr = () => likeValue;
            var eqExpr = Expression.Equal(memExpr, valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
        }

        if (likeValue.Replace("%", string.Empty).Length == 0)
        {
            return PredicateBuilder.True<T>();
        }

        likeValue = Regex.Replace(likeValue, "%+", "%");

        if (likeValue.Length > 2 && likeValue.Substring(1, likeValue.Length - 2).Contains('%'))
        {
            likeValue = likeValue.Replace("[", "[[]").Replace("_", "[_]");
            Expression<Func<string>> valExpr = () => likeValue;
            var patExpr = Expression.Call(typeof(SqlFunctions).GetMethod("PatIndex",
                new[] { typeof(string), typeof(string) }), valExpr.Body, memExpr);
            var neExpr = Expression.NotEqual(patExpr, Expression.Convert(Expression.Constant(0), typeof(int?)));
            return Expression.Lambda<Func<T, bool>>(neExpr, paramExpr);
        }

        if (likeValue.StartsWith("%"))
        {
            if (likeValue.EndsWith("%") == true)
            {
                likeValue = likeValue.Substring(1, likeValue.Length - 2);
                Expression<Func<string>> valExpr = () => likeValue;
                var containsExpr = Expression.Call(memExpr, typeof(String).GetMethod("Contains",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
            }
            else
            {
                likeValue = likeValue.Substring(1);
                Expression<Func<string>> valExpr = () => likeValue;
                var endsExpr = Expression.Call(memExpr, typeof(String).GetMethod("EndsWith",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(endsExpr, paramExpr);
            }
        }
        else
        {
            likeValue = likeValue.Remove(likeValue.Length - 1);
            Expression<Func<string>> valExpr = () => likeValue;
            var startsExpr = Expression.Call(memExpr, typeof(String).GetMethod("StartsWith",
                new[] { typeof(string) }), valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(startsExpr, paramExpr);
        }
    }

    public static Expression<Func<T, bool>> AndLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var andPredicate = Like(expr, likeValue);
        if (andPredicate != null)
        {
            predicate = predicate.And(andPredicate.Expand());
        }
        return predicate;
    }

    public static Expression<Func<T, bool>> OrLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var orPredicate = Like(expr, likeValue);
        if (orPredicate != null)
        {
            predicate = predicate.Or(orPredicate.Expand());
        }
        return predicate;
    }
}

Использование

var orPredicate = PredicateBuilder.False<People>();
orPredicate = orPredicate.OrLike(per => per.Name, "He%llo%");
orPredicate = orPredicate.OrLike(per => per.Name, "%Hi%");

var predicate = PredicateBuilder.True<People>();
predicate = predicate.And(orPredicate.Expand());
predicate = predicate.AndLike(per => per.Status, "%Active");

var list = dbContext.Set<People>().Where(predicate.Expand()).ToList();    

в ef6, и он должен перевести на

....
from People per
where (
    patindex(@p__linq__0, per.Name) <> 0
    or per.Name like @p__linq__1 escape '~'
) and per.Status like @p__linq__2 escape '~'

', @p__linq__0 ='% He% llo% ', @p__linq__1 ='% Hi% ', @p__linq_2 ='% Active '

Ответ 8

re: "мы хотели бы иметь возможность сопоставлять blah blah foobar foo? bar? foo * bar? и другие сложные шаблоны". Я на самом деле не пробовал это (пока не нужно), но попробовали ли вы использовать System.Text.RegularExpressions.RegEx?

Ответ 9

Вы можете использовать реальное, как в Link to Entities, довольно легко

Добавить

    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>

для вашего EDMX в этом теге:

EDMX: EDMX/EDMX: Продолжительность /EDMX: ConceptualModels/Схема

Также помните пространство имен в атрибуте <schema namespace="" />

Затем добавьте класс расширения в указанное пространство имен:

public static class Extensions
{
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")]
    public static Boolean Like(this String searchingIn, String lookingFor)
    {
        throw new Exception("Not implemented");
    }
}

Этот метод расширения теперь будет отображаться в функции EDMX.

Дополнительная информация здесь: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html