Pergunta sobre entity-framework, linq-to-entities, sql-server, .net – Como operador no Entity Framework?

79

Estamos tentando implementar o operador "LIKE" no Entity Framework para nossas entidades com campos de string, mas não parece ser suportado. Alguém já tentou fazer algo assim?

estepostagem no blog resume a questão que estamos tendo. Poderíamos usar contém, mas isso só corresponde ao caso mais trivial de LIKE. Combinar contém, startswith, endswith e indexof nos leva até lá, mas requer uma tradução entre curingas padrão e o código Linq to Entities.

Vamos paraesta resposta  se você já estiver usando o EF 6.2.x. Paraesta resposta se você estiver usando o EF Core 2.x CodeNotFound

Sua resposta

9   a resposta
5

É especificamente mencionado na documentação como parte do Entity SQL. Você está recebendo uma mensagem de erro?

<code>// 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%'
</code>

http://msdn.microsoft.com/pt-br/library/bb399359.aspx

Mensagem de erro?.. Robert Harvey
Parece que Contains () é o seu bilhete. Mas, como Jon Skeet apontou, você pode ter que descer para alguma SQL real manipulando o banco de dados diretamente, se Contains não atender às suas necessidades. Robert Harvey
Outra postagem relevante do StackOverflow:stackoverflow.com/questions/835790/how-to-do-sql-like-in-linq Robert Harvey
Eu editei a questão com um link para uma postagem no blog descrevendo o mesmo problema que estamos tendo. brien
14

Atualização: Na EF 6.2 existe um operador similar

<code>Where(i => DbFunctions.Like(searchstring ,like expression)
</code>
28

esse link deve ajudar. Vamos paraesta resposta se você já estiver usando o EF 6.2.x. Paraesta resposta se você estiver usando o EF Core 2.x

Versão curta:

SqlFunctions.PatIndex método - retorna a posição inicial da primeira ocorrência de um padrão em uma expressão especificada, ou zeros se o padrão não for encontrado, em todos os tipos de dados de texto e caractere válidos

Namespace: System.Data.Objects.SqlClient Assembly: System.Data.Entity (em System.Data.Entity.dll)

Um pouco de explicação também aparece nestediscussão no fórum.

como é a resposta aceita a que links para um fórum do MSDN que liga de volta a esta questão para oresposta abaixo? Eonasdan
A resposta abaixo é boa para padrões simples, mas se eu quiser dizer "WHERE Name LIKE 'abc [0-9]%'" ou algum outro padrão mais complexo, simplesmente usar Contains () não é suficiente. HotN
Dup deste mais velhoresponda a esta pergunta. (Não de sua primeira parte, mas de sua solução alternativa.) Frédéric
A resposta foi usar o método SqlFunctions.PatIndex. O tópico do fórum vinculado era fornecer um pouco mais de informações "de fundo". Yann Duran
144

mas no LINQ to SQL você normalmente expressa uma cláusula LIKE usando String.Contains:

<code>where entity.Name.Contains("xyz")
</code>

traduz para

<code>WHERE Name LIKE '%xyz%'
</code>

(UsarStartsWith eEndsWith para outro comportamento.)

Eu não tenho certeza se isso é útil, porque eu não entendo o que você quer dizer quando diz que está tentandoimplemento GOSTAR. Se eu não entendi completamente, me avise e eu vou deletar essa resposta :)

@landsteven: Isso não responde à minha pergunta. Certamente, se o valor do registro começa com a entrada do usuário, tambémcontém a entrada do usuário. Então, "ABC Dealer X" contém "ABC Dealer", assim como "X ABC Dealer". Você pode dar um exemplo onde Contains retornaria false, mas StartsWith ou EndsWith retornariam true? Jon Skeet
@landsteven: Quando você esperariaStartsWith ouEndsWith para retornartrue, masContains retornar falso? Jon Skeet
Eu apenas tentei isso no EF 3.5 no SQL Server, e funciona pelo menos nessa combinação. Mark Seemann
@ John Skeet: Quando meu usuário insere "ABC Dealer" como a string para consultar meu banco de dados para retornar resultados para um AutoCompleteBox.ItemsSource. Os resultados precisam incluir uma lista de todos os registros relativos encontrados na tabela de origem, mesmo quando o registro real que eles estão procurando pode iniciar OU conter OU terminar com a entrada fornecida. landsteven
0

blah foobar foo? bar? foo * bar? e outros padrões complexos. "Eu realmente não tentei isso (ainda não precisei), mas você já tentou usar System.Text.RegularExpressions.RegEx?

Uma expressão regular seria executada no servidor da Web, não no banco de dados. Basicamente, isso envolveria retornar a tabela inteira do SQL Server e, em seguida, filtrá-la. Isso funcionaria para alguns casos pequenos, mas precisamos procurar grandes volumes de dados (ish). Boa sugestão, porém, esse também foi meu primeiro pensamento. brien
9

Há simLIKE operador é adicionado emEntity Framework Core 2.0:

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

Comparando à... where e.Title.Contains("developer") ... é realmente traduzido paraSQL LIKE ao invés deCHARINDEX nós vemos porContains método.

0

Adicionar

<code>    <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>
</code>

para o seu EDMX nesta tag:

edmx: Edmx / edmx: Tempo de execução / edmx: ConceptualModels / Schema

Lembre-se também do namespace no<schema namespace="" /> atributo

Em seguida, adicione uma classe de extensão no namespace acima:

<code>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");
    }
}
</code>

Este método de extensão será agora mapeado para a função EDMX.

Mais informações aqui:http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

32

Por enquanto, resolvi a filtragem Wildcard / Regex do lado do cliente com base emhttp://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - é simples e funciona como esperado.

Eu encontrei outra discussão sobre este tópico:http://forums.asp.net/t/1654093.aspx/2/10
Este post parece promissor se você usar o Entity Framework> = 4.0:

Use SqlFunctions.PatIndex:

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

Como isso:

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

Nota: esta solução é apenas para SQL Server, porque usa a função PATINDEX não padrão.

Trabalhou um tratamento, obrigado por encontrar isso! Tom Carver
@BlackICE isso é esperado. Quando você pesquisa no texto interno (% CD% BLUE%), o servidor não poderá usar índices. Sempre que possível, pesquisar texto desde o início (CD% BLUE%) é mais eficiente. surfen
Enquanto PatIndex "funciona", ele vai voltar a te morder, PatIndex na cláusula where não usa os índices na coluna que você gostaria de filtrar. BlackICE
@surfen patindex é pior que isso, ele não usará o índice mesmo sem% na frente, procurando por (BLUE CD%) com patindex não usará o índice da coluna. BlackICE
2

eu escrevi dois métodos de extensão para suportar o caractere% para pesquisa com curinga. (LinqKit é obrigatório)

<code>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;
    }
}
</code>

uso

<code>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();    
</code>

no ef6 e deve traduzir para

<code>....
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 '~'
</code>

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Oi% ', @ p__linq_2 ='% Ativo '

obrigado pelo seu comentário Ronel, há algo que eu possa ajudar? qual é a mensagem de erro? Steven Chong
Eu tenho erro no PredicateBuilder Ronel Gonzales

Perguntas relacionadas