LINQ contiene mayúsculas y minúsculas

174

Este código distingue entre mayúsculas y minúsculas, ¿cómo hacerlo?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}
Jeaf Gilbert
fuente
Las respuestas de Sjoerd son correctas, pero ... Quiero obtener resultados de búsqueda para nombres con una İ turca (por ejemplo) al escribir una i y viceversa. En este caso, ToLower parece ser el camino correcto. Por favor corrígeme si estoy equivocado. Sobre turco ©: en.wikipedia.org/wiki/Dotted_and_dotless_I
He Nrik
@HeNrik - Como se discutió en Turquía Enlace de prueba en el comentario de JYelton bajo respuesta aceptada, cuando se ejecuta con la cultura turca, esos dos i serán diferentes, por lo que no encontrará nombres con el otro i. Desea ToLowerInvariant. Vea la discusión bajo varias respuestas aquí .
ToolmakerSteve
esta es una pregunta antigua, pero vale la pena señalar que en la versión actual EF core 2.0 ToLower () funciona de la siguiente manera person.Where (p => p.Name.ToLower (). Contiene (myParam.Name.ToLower () )); Estoy usando esto en una consulta de Linq contra un DB de Postgres. No tengo insensibilidad a mayúsculas y minúsculas en la clasificación de columnas en el DB y verifiqué que sin ToLower () la coincidencia es claramente sensible a mayúsculas y minúsculas.
shelbypereira

Respuestas:

72

Asumiendo que estamos trabajando con cadenas aquí, aquí hay otra solución "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
fuente
77
Agradable. Sin embargo, para mis propios fines, esto no funciona para LINQ to Entities. Sin embargo, es una buena solución para LINQ to Objects.
Damian Powell
242
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())
Nealv
fuente
49
Como Jon Skeet comentó sobre una pregunta relacionada , este método no pasará la Prueba de Turquía .
JYelton
55
No, pero las bases de datos funcionan con conjuntos de caracteres y colación. Si está tratando de dejar el trabajo en la base de datos, debe hacer algunas suposiciones sobre el conjunto de caracteres y la clasificación, ¿verdad?
Christopher Stevenson
66
Contiene debe usar el IEqualityComparer<string>atributo para manejar cómo funcionará la comparación. Usar ToLower y ToUpper para verificar la igualdad es una mala idea. Prueba: .Contains(description, StringComparer.CurrentCultureIgnoreCase)por ejemplo
Dorival
19
El comentario de @Dorival no funciona, ya que da este mensaje de error: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
66
Containswith StringComparerno recibe una cadena como parámetro, por lo que será un error de compilación. IndexOfel Queryableprobablemente no se pueden traducir en SQL. Personalmente, esta respuesta me pareció totalmente válida mientras hablamos de LINQ to database.
Thariq Nugrohotomo
122

Si la consulta LINQ se ejecuta en el contexto de la base de datos, Contains()se asigna una llamada al LIKEoperador:

.Where(a => a.Field.Contains("hello")) se convierte Field LIKE '%hello%'. El LIKEoperador no distingue entre mayúsculas y minúsculas de forma predeterminada, pero eso se puede cambiar cambiando la clasificación de la columna .

Si la consulta LINQ se ejecuta en contexto .NET, puede usar IndexOf () , pero ese método no es compatible con LINQ to SQL.

LINQ to SQL no admite métodos que toman un CultureInfo como parámetro, probablemente porque no puede garantizar que el servidor SQL maneje cultivos de la misma manera que .NET. Esto no es completamente cierto, porque sí es compatible StartsWith(string, StringComparison).

Sin embargo, no parece admitir un método que se evalúe LIKEen LINQ to SQL y en una comparación de mayúsculas y minúsculas en .NET, por lo que es imposible hacer Contains () insensible a mayúsculas y minúsculas de manera consistente.

Sjoerd
fuente
Solo FYI EF 4.3 no es compatible con StartsWith. Obtengo: LINQ to Entities no reconoce el método 'Boolean StartsWith (System.String, System.StringComparison)'
nakhli
StartWith convierte a LIKE 'hello%'?
Bart Calixto
El enlace clicdata está muerto.
Adam Parkin
2
gran esfuerzo para profundizar en el comportamiento generado de SQL y db para la cláusula LIKE
Thariq Nugrohotomo
1
Entonces, ¿cuál es el uso de opciones queridos cuando EF, en un contexto que tengo que hacer caso insensitivede búsqueda, y en la otra lo necesito para ser case sensitive. ¿Debo tomar el golpe de rendimiento y usar 'toLower ()'?
Zapnologica
12

La respuesta aceptada aquí no menciona un hecho de que si tiene una cadena nula ToLower () arrojará una excepción. La forma más segura sería hacer:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())
Marko
fuente
No puede generar una excepción en una consulta traducida a SQL
Alex Zhukovskiy
@AlexZhukovskiy ¿Cómo es eso relevante para este problema? Si fi.DESCRIPTION es nulo o la descripción es nula, obtendrá una excepción de referencia nula de C #. No importa a qué se convierta la consulta LINQ en el lado de SQL. Aquí está la prueba: dotnetfiddle.net/5pZ1dY
Marko
Debido a que esta consulta fallará la traducción a SQL porque no admite el operador de fusión nula. Y probablemente esté consultando la base de datos en lugar de cargar todas las entradas para usar la fusión nula en el lado del cliente. Entonces, si lo usa, está bien en el lado del cliente pero falla en DB, de lo contrario, está bien con DB y no le importa nullref en el lado del cliente porque no sucederá porque C # no ejecuta esta consulta y no en realidad no leer objetos nulos.
Alex Zhukovskiy
Esta respuesta me ayudó a resolver un problema que estaba obteniendo en LINQ to Entities donde estaba haciendo .IndexOf y .Contains en un IEnumerable donde el valor de cadena proveniente de la base de datos era nulo. No recibí el error hasta que se enumeró el resultado y luego recibí un mensaje de error que decía "La referencia del objeto no está establecida en una instancia de un objeto". No pude entender por qué ocurría hasta que vi esta publicación. ¡Gracias!
randyh22
7

Usando C # 6.0 (que permite funciones corporales de expresión y propagación nula), para LINQ to Objects, se puede hacer en una sola línea como esta (también buscando null):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
Alexei
fuente
No funciona porque ContainsInsensitive no es un comando de tienda
Sven
@Sven: sí, solo funciona para LINQ to Objects. He arreglado mi respuesta. Gracias.
Alexei
4

IndexOf funciona mejor en este caso

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

Puedes usar string.Compare

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

si solo desea verificar los contenidos, use "Cualquiera"

  lst.Any(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0)
Código Primero
fuente
Esto no responde la pregunta. El OP pregunta acerca de 'Contiene' dentro de una cadena (es decir, una cadena contiene otra), no si una colección de cadenas contiene una sola cadena.
andrewf
1
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}
E Rolnicki
fuente
2
¿podemos usar métodos de extensión personalizados en consultas linq? Estás seguro ?
Vishal Sharma
0

Honestamente, esto no tiene por qué ser difícil. Puede parecer que al principio, pero no lo es. Aquí hay una consulta simple de linq en C # que hace exactamente lo solicitado.

En mi ejemplo, estoy trabajando contra una lista de personas que tienen una propiedad llamada FirstName.

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

Esto buscará en la base de datos en la búsqueda de minúsculas pero devolverá los resultados completos de las mayúsculas.

Frank Thomas
fuente
-2

Utilice el 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
fuente