Comparación de cadenas sin distinción entre mayúsculas y minúsculas en LINQ-to-SQL

137

He leído que no es aconsejable usar ToUpper y ToLower para realizar comparaciones de cadenas que no distinguen entre mayúsculas y minúsculas, pero no veo otra alternativa cuando se trata de LINQ-to-SQL. LINQ-to-SQL ignora los argumentos ignoreCase y CompareOptions de String.Compare (si está utilizando una base de datos que distingue entre mayúsculas y minúsculas, obtendrá una comparación entre mayúsculas y minúsculas, incluso si solicita una comparación que no distinga entre mayúsculas y minúsculas). ¿Es ToLower o ToUpper la mejor opción aquí? ¿Es uno mejor que el otro? Pensé que leí en alguna parte que ToUpper era mejor, pero no sé si eso se aplica aquí. (Estoy haciendo muchas revisiones de código y todos están usando ToLower).

Dim s = From row In context.Table Where String.Compare(row.Name, "test", StringComparison.InvariantCultureIgnoreCase) = 0

Esto se traduce en una consulta SQL que simplemente compara row.Name con "test" y no devolverá "Test" y "TEST" en una base de datos sensible a mayúsculas y minúsculas.

BlueMonkMN
fuente
1
¡Gracias! Esto realmente me salvó el culo hoy. Nota: también funciona con otras extensiones LINQ como LINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase)y LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase). Wahoo!
Greg Bray
Es curioso, acabo de leer que ToUpper fue mejor en las comparaciones de esta fuente: msdn.microsoft.com/en-us/library/dd465121
malckier

Respuestas:

110

Como usted dice, hay algunas diferencias importantes entre ToUpper y ToLower, y solo una es confiable y confiable cuando intenta hacer verificaciones de igualdad que no distinguen entre mayúsculas y minúsculas.

Idealmente, la mejor manera de hacer una verificación de igualdad sin distinción entre mayúsculas y minúsculas sería :

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

¡NOTA, SIN EMBARGO, que esto no funciona en este caso! Por lo tanto estamos atrapados con ToUppero ToLower.

Tenga en cuenta el ordinal IgnoreCase para que sea la seguridad de fallos. Pero exactamente el tipo de verificación sensible a mayúsculas y minúsculas que use depende de sus propósitos. Pero, en general, use Iguales para verificaciones de igualdad y Comparar cuando esté ordenando, y luego elija la Comparación de cadenas correcta para el trabajo.

Michael Kaplan (una autoridad reconocida en cultura y manejo de personajes como este) tiene publicaciones relevantes en ToUpper vs. ToLower:

Él dice "String.ToUpper - Use ToUpper en lugar de ToLower, y especifique InvariantCulture para recoger las reglas de la carcasa del sistema operativo "

Andrew Arnott
fuente
1
Parece que esto no se aplica a SQL Server: print upper ('Große Straße') devuelve GROßE STRAßE
BlueMonkMN
1
Además, el código de muestra que proporcionó tiene el mismo problema que el código que proporcioné en cuanto a mayúsculas y minúsculas cuando se ejecuta a través de LINQ-to-SQL en una base de datos MS SQL 2005.
BlueMonkMN
2
Estoy de acuerdo. Lo siento, no estaba claro. El código de muestra que proporcioné no funciona con Linq2Sql como señaló en su pregunta original. Simplemente estaba reafirmando que la forma en que comenzaste era una excelente manera de hacerlo, si solo funcionaba en este escenario. Y sí, otra caja de jabón de Mike Kaplan es que el manejo de caracteres de SQL Server está por todas partes. Si necesita no distingue entre mayúsculas y minúsculas y no puede obtenerlo de otra manera, le sugerí (sin claridad) que almacene los datos en mayúsculas y luego lo consulte en mayúsculas.
Andrew Arnott
3
Bueno, si tiene una base de datos sensible a mayúsculas y minúsculas y almacena en mayúsculas y busca en mayúsculas, no obtendrá coincidencias. Si agrega tanto los datos como la consulta en su búsqueda, entonces está convirtiendo todo el texto que está buscando para cada consulta, lo que no es eficaz.
Andrew Arnott
1
@BlueMonkMN, ¿estás seguro de que pegaste los fragmentos correctos? Es difícil creer que el servidor MSSQL prefiera el rojo más que el negro.
greenoldman
75

Utilicé System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") en mi consulta.

Esto realiza una comparación entre mayúsculas y minúsculas.

Andrew Davey
fuente
3
¡decir ah! He estado usando linq 2 sql durante varios años, pero no había visto SqlMethods hasta ahora, ¡gracias!
Carl Hörberg
3
¡Brillante! Sin embargo, podría usar más detalles. ¿Es este uno de los usos esperados de Like? ¿Hay posibles entradas que causarían un resultado falso positivo? ¿O un resultado falso negativo? La documentación sobre este método es deficiente, ¿dónde está la documentación que va a describir el funcionamiento del método de Como?
Tarea
2
Creo que solo se basa en cómo SQL Server compara las cadenas, que probablemente sea configurable en algún lugar.
Andrew Davey
11
System.Data.Linq.SqlClient.SqlMethods.Like (row.Name, "test") es lo mismo que row.Name.Contains ("test"). Como Andrew dice, esto depende de la recopilación del servidor SQL. Por lo tanto, Like (o contiene) no siempre realiza una comparación entre mayúsculas y minúsculas.
doekman
3
Tenga en cuenta que esto hace que el código sea demasiado parejo SqlClient.
Jaider
5

Intenté esto usando la expresión Lambda, y funcionó.

List<MyList>.Any (x => (String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) && (x.Type == qbType) );

vinahr
fuente
18
Esto se debe a que está utilizando un List<>, lo que significa que la comparación se realiza en memoria (código C #) en lugar de un IQueryable(o ObjectQuery) que realizaría la comparación en la base de datos .
drzaus
1
Lo que dijo @drzaus. Esta respuesta es simplemente incorrecta, considerando que el contexto es linq2sql y no linq regular.
rsenna
0

Si pasa una cadena que no distingue entre mayúsculas y minúsculas en LINQ-to-SQL, se pasará al SQL sin cambios y la comparación se realizará en la base de datos. Si desea hacer comparaciones de cadenas que no distinguen entre mayúsculas y minúsculas en la base de datos, todo lo que necesita hacer es crear una expresión lambda que haga la comparación y el proveedor LINQ-to-SQL traducirá esa expresión en una consulta SQL con su cadena intacta.

Por ejemplo, esta consulta LINQ:

from user in Users
where user.Email == "[email protected]"
select user

se traduce al siguiente SQL por el proveedor LINQ-to-SQL:

SELECT [t0].[Email]
FROM [User] AS [t0]
WHERE [t0].[Email] = @p0
-- note that "@p0" is defined as nvarchar(11)
-- and is passed my value of "[email protected]"

Como puede ver, el parámetro de cadena se comparará en SQL, lo que significa que las cosas deberían funcionar de la manera que esperaría.

Andrew Hare
fuente
No entiendo lo que dices. 1) Las cadenas en sí no pueden distinguir entre mayúsculas y minúsculas o mayúsculas y minúsculas en .NET, por lo que no puedo pasar una "cadena entre mayúsculas y minúsculas". 2) Una consulta LINQ básicamente ES una expresión lambda, y así es como paso mis dos cadenas, por lo que esto no tiene ningún sentido para mí.
BlueMonkMN
3
Quiero realizar una comparación CASO-INSENSIBLE en una base de datos CASO-SENSIBLE.
BlueMonkMN
¿Qué base de datos sensible a mayúsculas y minúsculas está utilizando?
Andrew Hare
Además, una consulta LINQ no es una expresión lambda. Una consulta LINQ se compone de varias partes (especialmente operadores de consulta y expresiones lambda).
Andrew Hare
Esta respuesta no tiene sentido como comenta BlueMonkMN.
Alf
0

Para realizar consultas de Linq a Sql sensibles a mayúsculas y minúsculas, declare que los campos 'string' distinguen entre mayúsculas y minúsculas especificando el tipo de datos del servidor utilizando uno de los siguientes;

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

o

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

Nota: La 'CS' en los tipos de clasificación anteriores significa 'mayúsculas y minúsculas'.

Esto se puede ingresar en el campo "Tipo de datos del servidor" cuando se visualiza una propiedad con Visual Studio DBML Designer.

Para más detalles ver http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html

John Hansen
fuente
Ese es el problema. Normalmente, el campo que uso distingue entre mayúsculas y minúsculas (la fórmula química CO [monóxido de carbono] es diferente de Co [cobalto]). Sin embargo, en una situación específica (búsqueda), quiero que coincida con Co y CO. Definir una propiedad adicional con un "tipo de datos de servidor" diferente no es legal (linq a sql solo permite una propiedad por columna sql). Así que aún no te vayas.
doekman
Además, si realiza pruebas unitarias, es probable que este enfoque no sea compatible con una simulación de datos. Es mejor usar el enfoque linq / lambda en la respuesta aceptada.
Derrick
0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)
Julio Silveira
fuente
1
¿Cuál es el texto SQL al que se traduce esto y qué permite que no distinga entre mayúsculas y minúsculas en un entorno SQL que de otro modo lo trataría como mayúsculas y minúsculas?
BlueMonkMN
0

El siguiente enfoque de 2 etapas funciona para mí (VS2010, ASP.NET MVC3, SQL Server 2008, Linq to SQL):

result = entRepos.FindAllEntities()
    .Where(e => e.EntitySearchText.Contains(item));

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}
Jim Davies
fuente
1
Este código tiene un error si el texto comienza con el texto de búsqueda (debe ser> = 0)
Flatliner DOA
@FlatlinerDOA en realidad debería ser != -1porque IndexOf "devuelve -1 si no se encuentra el carácter o la cadena"
drzaus
0

A veces, el valor almacenado en la base de datos puede contener espacios, por lo que ejecutar esto podría fallar

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

La solución a este problema es eliminar el espacio, luego convertir su caja y luego seleccionar así

 return db.UsersTBs.Where(x => x.title.ToString().ToLower().Replace(" ",string.Empty).Equals(customname.ToLower())).FirstOrDefault();

Nota en este caso

nombre personalizado es el valor que coincide con el valor de la base de datos

UsersTBs es clase

título es la columna de la base de datos

TAHA SULTAN TEMURI
fuente
-1

¡Recuerde que hay una diferencia entre si la consulta funciona y si funciona de manera eficiente ! Una declaración LINQ se convierte a T-SQL cuando el objetivo de la declaración es SQL Server, por lo que debe pensar en el T-SQL que se produciría.

El uso de String.

En otras palabras, el uso de una expresión aumentará su acceso a los datos y eliminará su capacidad de hacer uso de índices. Funcionará en mesas pequeñas y no notará la diferencia. En una mesa grande podría funcionar muy mal.

Ese es uno de los problemas que existe con LINQ; la gente ya no piensa en cómo se cumplirán las declaraciones que escriben.

En este caso, no hay una manera de hacer lo que quiere sin usar una expresión, ni siquiera en T-SQL. Por lo tanto, es posible que no pueda hacer esto de manera más eficiente. Incluso la respuesta T-SQL dada anteriormente (usando variables con intercalación) probablemente dará como resultado que se ignoren los índices, pero si es una tabla grande entonces vale la pena ejecutar la declaración y mirar el plan de ejecución para ver si se utilizó un índice .

Andrew H
fuente
2
Eso no es cierto (no hace que las filas se devuelvan al cliente). He usado String.Equals y la razón por la que no funciona es porque se convierte en una comparación de cadenas TSQL, cuyo comportamiento depende de la recopilación de la base de datos o el servidor. Por mi parte, considero cómo cada expresión de LINQ to SQL que escribo se convertiría en TSQL. El camino hacia lo que quiero es usar ToUpper para forzar al TSQL generado a usar UPPER. Entonces, toda la lógica de conversión y comparación todavía se realiza en TSQL para que no pierda mucho rendimiento.
BlueMonkMN