Comparación entre mayúsculas y minúsculas de LINQ to Entities
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?
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");
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
WHERE
Las 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 Up
mé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.
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;
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_AS
intercalación, que distingue entre mayúsculas y minúsculas.