Comparación entre mayúsculas y minúsculas de LINQ to Entities

Resuelto Ronnie Overby asked hace 13 años • 9 respuestas

Esta no es una comparación que distingue entre mayúsculas y minúsculas en LINQ to Entities:

Thingies.First(t => t.Name == "ThingamaBob");

¿Cómo puedo lograr una comparación que distinga entre mayúsculas y minúsculas con LINQ to Entities?

Ronnie Overby avatar Oct 02 '10 04:10 Ronnie Overby
Aceptado

Esto se debe a que está utilizando LINQ To Entities , que en última instancia convierte sus expresiones Lambda en declaraciones SQL. Eso significa que la distinción entre mayúsculas y minúsculas está a merced de su SQL Server, que de forma predeterminada tiene la intercalación SQL_Latin1_General_CP1_CI_AS y NO distingue entre mayúsculas y minúsculas.

El uso de ObjectQuery.ToTraceString para ver la consulta SQL generada que realmente se envió a SQL Server revela el misterio:

string sqlQuery = ((ObjectQuery)context.Thingies
        .Where(t => t.Name == "ThingamaBob")).ToTraceString();

Cuando crea una consulta de LINQ to Entities , LINQ to Entities aprovecha el analizador de LINQ para comenzar a procesar la consulta y la convierte en un árbol de expresiones LINQ. Luego, el árbol de expresión LINQ se pasa a la API de servicios de objetos , que convierte el árbol de expresión en un árbol de comandos. Luego se envía al proveedor de la tienda (por ejemplo, SqlClient), que convierte el árbol de comandos en el texto de comando nativo de la base de datos. La consulta se ejecuta en el almacén de datos y los resultados se materializan en objetos de entidad mediante servicios de objetos . No se ha puesto ninguna lógica intermedia para tener en cuenta la distinción entre mayúsculas y minúsculas. Entonces, no importa qué caso ponga en su predicado, su servidor SQL siempre lo tratará como el mismo a menos que cambie sus intercalaciones de SQL Server para esa columna.

Solución del lado del servidor:

Por lo tanto, la mejor solución sería cambiar la intercalación de la columna Nombre en la tabla Thingies a COLLATE Latin1_General_CS_AS , que distingue entre mayúsculas y minúsculas, ejecutando esto en su SQL Server:

ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS

Para obtener más información sobre las intercalaciones de SQL Server , consulte SQL SERVER Collate Búsqueda de consultas SQL que distingue entre mayúsculas y minúsculas.

Solución del lado del cliente:

La única solución que puede aplicar en el lado del cliente es usar LINQ to Objects para hacer otra comparación que no parece muy elegante:

Thingies.Where(t => t.Name == "ThingamaBob")
        .AsEnumerable()
        .First(t => t.Name == "ThingamaBob");
Morteza Manavi avatar Oct 01 '2010 22:10 Morteza Manavi

Puede agregar la anotación [CaseSensitive] para EF6+ Code-first

Añadir estas clases

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class CaseSensitiveAttribute : Attribute
{
    public CaseSensitiveAttribute()
    {
        IsEnabled = true;
    }
    public bool IsEnabled { get; set; }
}

public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        base.Generate(alterColumnOperation);
        AnnotationValues values;
        if (alterColumnOperation.Column.Annotations.TryGetValue("CaseSensitive", out values))
        {
            if (values.NewValue != null && values.NewValue.ToString() == "True")
            {
                using (var writer = Writer())
                {
                    //if (System.Diagnostics.Debugger.IsAttached == false) System.Diagnostics.Debugger.Launch();

                    // https://github.com/mono/entityframework/blob/master/src/EntityFramework.SqlServer/SqlServerMigrationSqlGenerator.cs
                    var columnSQL = BuildColumnType(alterColumnOperation.Column); //[nvarchar](100)
                    writer.WriteLine(
                        "ALTER TABLE {0} ALTER COLUMN {1} {2} COLLATE SQL_Latin1_General_CP1_CS_AS {3}",
                        alterColumnOperation.Table,
                        alterColumnOperation.Column.Name,
                        columnSQL,
                        alterColumnOperation.Column.IsNullable.HasValue == false || alterColumnOperation.Column.IsNullable.Value == true ? " NULL" : "NOT NULL" //todo not tested for DefaultValue
                        );
                    Statement(writer);
                }
            }
        }
    }
}

public class CustomApplicationDbConfiguration : DbConfiguration
{
    public CustomApplicationDbConfiguration()
    {
        SetMigrationSqlGenerator(
            SqlProviderServices.ProviderInvariantName,
            () => new CustomSqlServerMigrationSqlGenerator());
    }
}

Modifique su DbContext, agregue

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
                "CaseSensitive",
                (property, attributes) => attributes.Single().IsEnabled));
        base.OnModelCreating(modelBuilder);
    }

Entonces hazlo

Agregar migración distingue entre mayúsculas y minúsculas

Actualizar base de datos

basado en el artículo https://milinaudara.wordpress.com/2015/02/04/case-SENSITIVE-search-using-entity-framework-with-custom-annotation/ con alguna corrección de errores

RouR avatar Apr 25 '2015 14:04 RouR

WHERELas condiciones en SQL Server no distinguen entre mayúsculas y minúsculas de forma predeterminada. Haga que distinga entre mayúsculas y minúsculas cambiando las intercalaciones predeterminadas de la columna ( SQL_Latin1_General_CP1_CI_AS) a SQL_Latin1_General_CP1_CS_AS.

La forma frágil de hacer esto es con código. Agregue un nuevo archivo de migración y luego agregue esto dentro del Upmétodo:

public override void Up()
{
   Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}

Pero

Puede crear una anotación personalizada llamada "CaseSensitive" usando las nuevas funciones de EF6 y puede decorar sus propiedades de esta manera:

[CaseSensitive]
public string Name { get; set; }

Esta publicación de blog explica cómo hacerlo.

Milina Udara avatar Feb 08 '2015 06:02 Milina Udara

La respuesta dada por @Morteza Manavi soluciona el problema. Aún así, para una solución del lado del cliente , una forma elegante sería la siguiente (agregando una doble verificación).

var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
    .FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Swarup Rajbhandari avatar Nov 10 '2016 06:11 Swarup Rajbhandari

Sí, es posible y está bien documentado.

https://learn.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity

Opción 1 : es posible especificar la clasificación de una columna:

modelBuilder.Entity<Customer>().Property(c => c.Name)
    .UseCollation("SQL_Latin1_General_CP1_CS_AS");

Opción 2 - o de toda la base de datos:

modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");

Opción 3 : otra opción es especificar la intercalación para una única consulta:

var customers = context.Customers
    .Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John")
    .ToList();

Todas las opciones utilizan la SQL_Latin1_General_CP1_CS_ASintercalación, que distingue entre mayúsculas y minúsculas.

Martin Staufcik avatar Jan 31 '2024 16:01 Martin Staufcik