LINQ contém maiúsculas e minúsculas

174

Este código faz distinção entre maiúsculas e minúsculas, como torná-lo sem distinção entre maiúsculas e minúsculas?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}
Jeaf Gilbert
fonte
As respostas de Sjoerd estão corretas, mas ... Desejo obter resultados de pesquisa de nomes com um turco © (por exemplo) ao escrever um ie vice-versa. Nesse caso, ToLower parece ser o caminho correto a seguir. Por favor me corrija se eu estiver errado. Sobre turco ©: en.wikipedia.org/wiki/Dotted_and_dotless_I
He Nrik
@HeNrik - Conforme discutido no link Teste da Turquia no comentário de JYelton sob resposta aceita, quando executado com a cultura turca, esses dois i serão diferentes - para que você não encontre nomes com o outro i. Você deseja ToLowerInvariant. Veja a discussão sob várias respostas aqui .
Home
essa é uma pergunta antiga, mas vale a pena notar que, na versão atual, o EF core 2.0 ToLower () funciona da seguinte maneira: person.Where (p => p.Name.ToLower (). Contains (myParam.Name.ToLower ()) )); Eu estou usando isso em uma consulta Linq contra um banco de dados Postgres. Não tenho insensibilidade a maiúsculas e minúsculas no agrupamento de colunas no banco de dados e verifiquei que sem ToLower () a correspondência é claramente sensível a maiúsculas e minúsculas.
Shelbypereira 26/09/19

Respostas:

72

Supondo que estamos trabalhando com strings aqui, aqui está outra solução "elegante" usando IndexOf().

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
        .Where(fi => fi.DESCRIPTION
                       .IndexOf(description, StringComparison.OrdinalIgnoreCase) != -1);
}
Jeff Mercado
fonte
7
Agradável. Para meus próprios fins, porém, isso não funciona para o LINQ to Entities. Solução agradável para o LINQ to Objects.
Damian Powell
242
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())
Nealv
fonte
49
Como Jon Skeet comentou sobre uma questão relacionada , esse método não será aprovado no Teste da Turquia .
JYelton
5
Não, mas os bancos de dados funcionam com conjuntos de caracteres e agrupamento. Se você está tentando enviar trabalho para o banco de dados, precisa fazer algumas suposições sobre conjunto de caracteres e agrupamento, certo?
Christopher Stevenson
66
Contém deve estar usando o IEqualityComparer<string>atributo para manipular como a comparação funcionará. Use ToLower e ToUpper para verificar a igualdade é uma má idéia. Tente: .Contains(description, StringComparer.CurrentCultureIgnoreCase)por exemplo
Dorival
19
O comentário de @Dorival não funciona, pois fornece esta mensagem de erro:Error 1 'string' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains<TSource>(System.Linq.ParallelQuery<TSource>, TSource, System.Collections.Generic.IEqualityComparer<TSource>)' has some invalid arguments
eMi
6
Containswith StringComparernão recebe string como parâmetro, portanto, haverá erro de construção. IndexOfem Queryable, provavelmente, não pode ser traduzido em SQL. Pessoalmente, achei essa resposta totalmente válida quando falamos sobre LINQ to database.
Thariq Nugrohotomo 10/04/2015
122

Se a consulta LINQ for executada no contexto do banco de dados, uma chamada para Contains()será mapeada para o LIKEoperador:

.Where(a => a.Field.Contains("hello")) torna-se Field LIKE '%hello%'. O LIKEoperador não diferencia maiúsculas de minúsculas por padrão, mas isso pode ser alterado alterando o agrupamento da coluna .

Se a consulta LINQ for executada no contexto .NET, você poderá usar IndexOf () , mas esse método não é suportado no LINQ to SQL.

O LINQ to SQL não oferece suporte a métodos que usam um CultureInfo como parâmetro, provavelmente porque não pode garantir que o servidor SQL manipule culturas da mesma forma que o .NET. Isso não é totalmente verdade, porque ele faz suporte StartsWith(string, StringComparison).

No entanto, ele não parece oferecer suporte a um método que é avaliado LIKEem LINQ to SQL e em uma comparação sem distinção entre maiúsculas e minúsculas no .NET, tornando impossível executar Contains () sem diferenciação de maiúsculas e minúsculas de maneira consistente.

Sjoerd
fonte
Apenas FYI EF 4.3 não suporta StartsWith. Eu recebo: LINQ to Entities não reconhece o método 'Boolean StartsWith (System.String, System.StringComparison)'
nakhli
StartWith se converte em LIKE 'hello%'?
Bart Calixto
O link clicdata está morto.
Adam Parkin
2
grande esforço para cavar o comportamento SQL e db gerado por cláusula LIKE
Thariq Nugrohotomo
1
Então, quais são as opções ao usar o EF? Em um contexto, preciso fazer uma insensitivepesquisa de caso e, no outro, preciso case sensitive. Eu só tenho que aguentar o desempenho e usar 'toLower ()'?
Zapnologica
12

A resposta aceita aqui não menciona o fato de que, se você tiver uma sequência nula, ToLower () lançará uma exceção. A maneira mais segura seria fazer:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())
Marko
fonte
Você não pode gen uma exceção em uma consulta traduzido para SQL
Alex Zhukovskiy
@AlexZhukovskiy Como isso é relevante para esse problema? Se fi.DESCRIPTION for nulo ou a descrição for nula, você está recebendo uma exceção de referência nula em C #. Não importa o que a consulta LINQ converte no lado SQL. Aqui está a prova: dotnetfiddle.net/5pZ1dY
Marko
Porque esta consulta falhará na conversão para SQL porque não suporta o operador de coalescência nula. E você provavelmente está consultando o banco de dados em vez de carregar todas as entradas para usar coalescência nula no lado do cliente. Portanto, se você usá-lo - tudo está bem no lado do cliente, mas falha no DB, caso contrário, você está bem com o DB e não se importa com nullref no lado do cliente, porque isso não acontecerá porque o C # não executa essa consulta e não realmente lê objetos nulos.
Alex Zhukovskiy
Essa resposta me ajudou a resolver um problema que eu estava obtendo no LINQ to Entities onde eu estava executando .IndexOf e .Contains em um IEnumerable onde o valor da string proveniente do banco de dados era nulo. Eu não estava recebendo o erro até que o resultado fosse enumerado e recebi uma mensagem de erro "Referência de objeto não definida para uma instância de um objeto". Eu não conseguia descobrir por que estava ocorrendo até ver este post. Obrigado!
randyh22
7

Usando o C # 6.0 (que permite funções corporais de expressão e propagação nula), para LINQ to Objects, isso pode ser feito em uma única linha como esta (também verificando se é nulo):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
Alexei
fonte
Não está funcionando porque ContainsInsensitive não é um comando loja
Sven
@ Sven - sim, funciona apenas para LINQ to Objects. Eu consertei minha resposta. Obrigado.
Alexei
4

IndexOf funciona melhor neste caso

return this
   .ObjectContext
   .FACILITY_ITEM
   .Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase)>=0);
Menelaos Vergis
fonte
3

Você pode usar string.Compare

    lst.Where(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0);

se você quiser apenas verificar o conteúdo, use "Qualquer"

  lst.Any(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0)
Code First
fonte
Isso não responde à pergunta. O OP está perguntando sobre 'Contém' dentro de uma string (ou seja, uma string contém outra), não se uma coleção de strings contém uma única string.
andrewf 10/04
1
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}
E Rolnicki
fonte
2
podemos usar métodos de extensão personalizados em consultas linq? você tem certeza ?
Vishal Sharma
0

Honestamente, isso não precisa ser difícil. Pode parecer que no início, mas não é. Aqui está uma consulta linq simples em C # que faz exatamente como solicitado.

No meu exemplo, estou trabalhando com uma lista de pessoas que têm uma propriedade chamada FirstName.

var results = ClientsRepository().Where(c => c.FirstName.ToLower().Contains(searchText.ToLower())).ToList();

Isso pesquisará o banco de dados na pesquisa em minúsculas, mas retornará resultados completos.

Frank Thomas
fonte
-2

Use o método String.Equals

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
           .Where(fi => fi.DESCRIPTION
           .Equals(description, StringComparison.OrdinalIgnoreCase));
}
Francesco Moroni
fonte