Ce n'est pas une comparaison sensible à la casse dans LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Comment puis-je réaliser une comparaison sensible à la casse avec LINQ to Entities?
Ce n'est pas une comparaison sensible à la casse dans LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Comment puis-je réaliser une comparaison sensible à la casse avec LINQ to Entities?
Réponses:
En effet, vous utilisez LINQ To Entities, qui convertit finalement vos expressions Lambda en instructions SQL. Cela signifie que le respect de la casse est à la merci de votre serveur SQL qui par défaut a SQL_Latin1_General_CP1_CI_AS et qui n'est PAS sensible à la casse.
L'utilisation d' ObjectQuery.ToTraceString pour voir la requête SQL générée qui a été réellement soumise à SQL Server révèle le mystère:
string sqlQuery = ((ObjectQuery)context.Thingies
.Where(t => t.Name == "ThingamaBob")).ToTraceString();
Lorsque vous créez une requête LINQ to Entities , LINQ to Entities exploite l'analyseur LINQ pour commencer le traitement de la requête et la convertit en une arborescence d'expression LINQ. L'arborescence d'expression LINQ est ensuite transmise à l' API Object Services , qui convertit l'arborescence d'expression en une arborescence de commandes. Il est ensuite envoyé au fournisseur du magasin (par exemple SqlClient), qui convertit l'arborescence de commandes en texte de commande de la base de données native. La requête est exécutée sur le magasin de données et les résultats sont matérialisés en objets d'entité par Object Services. Aucune logique n'a été mise entre les deux pour tenir compte du respect de la casse. Ainsi, quel que soit le cas que vous mettez dans votre prédicat, il sera toujours traité de la même manière par votre serveur SQL, sauf si vous modifiez vos assemblages SQL Server pour cette colonne.
Par conséquent, la meilleure solution serait de modifier le classement de la colonne Nom dans la table Thingies en COLLATE Latin1_General_CS_AS, ce qui est sensible à la casse en exécutant ceci sur votre serveur SQL:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
Pour plus d'informations sur les assemblages SQL Server , jetez un œil à la recherche de requêtes SQL sensible à la casse de l' assemblage SQL SERVER
La seule solution que vous pouvez appliquer côté client est d'utiliser LINQ to Objects pour faire encore une autre comparaison qui ne semble pas très élégante:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
Vous pouvez ajouter l'annotation [CaseSensitive] pour EF6 + Code-first
Ajouter ces classes
[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());
}
}
Modifiez votre DbContext, ajoutez
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
"CaseSensitive",
(property, attributes) => attributes.Single().IsEnabled));
base.OnModelCreating(modelBuilder);
}
Alors fais
Add-Migration CaseSensitive
Update-Database
basé sur l'article https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ avec une correction de bogue
WHERE
les conditions dans SQL Server sont insensibles à la casse par défaut. Faites-le sensible à la casse en remplaçant les classements par défaut de la colonne ( SQL_Latin1_General_CP1_CI_AS
) par SQL_Latin1_General_CP1_CS_AS
.
La manière fragile de faire ceci est avec le code. Ajoutez un nouveau fichier de migration, puis ajoutez-le dans la Up
méthode:
public override void Up()
{
Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}
Mais
Vous pouvez créer une annotation personnalisée appelée "CaseSensitive" à l'aide des nouvelles fonctionnalités EF6 et vous pouvez décorer vos propriétés comme ceci:
[CaseSensitive]
public string Name { get; set; }
Cet article de blog explique comment faire cela.
La réponse donnée par @Morteza Manavi résout le problème. Pourtant, pour une solution côté client , une manière élégante serait la suivante (ajouter une double vérification).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
J'ai aimé la réponse de Morteza et je préférerais normalement la corriger côté serveur. Pour le côté client, j'utilise normalement:
Dim bLogin As Boolean = False
Dim oUser As User = (From c In db.Users Where c.Username = UserName AndAlso c.Password = Password Select c).SingleOrDefault()
If oUser IsNot Nothing Then
If oUser.Password = Password Then
bLogin = True
End If
End If
En gros, vérifiez d'abord s'il y a un utilisateur avec les critères requis, puis vérifiez si le mot de passe est le même. Un peu long, mais je pense que c'est plus facile à lire quand il peut y avoir tout un tas de critères impliqués.
Aucun des deux n'a StringComparison.IgnoreCase
fonctionné pour moi. Mais cela a fait:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
How can I achieve case sensitive comparison
Utilisez une chaîne.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);
De plus, vous n'avez pas à vous soucier de null et ne récupérez que les informations que vous souhaitez.
Utilisez StringComparision.CurrentCultureIgnoreCase pour ne pas respecter la casse.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
Je ne suis pas sûr de EF4, mais EF5 prend en charge ceci:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
enums fasse une différence. J'ai vu suffisamment de gens suggérer que ce genre de chose devrait fonctionner pour penser que le problème se trouve quelque part dans le fichier EDMX (db-first), bien que stackoverflow.com/questions/841226