Questo non è un confronto con distinzione tra maiuscole e minuscole in LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Come posso ottenere un confronto con distinzione tra maiuscole e minuscole con LINQ to Entities?
Questo non è un confronto con distinzione tra maiuscole e minuscole in LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Come posso ottenere un confronto con distinzione tra maiuscole e minuscole con LINQ to Entities?
Risposte:
Questo perché stai usando LINQ To Entities che alla fine converte le tue espressioni Lambda in istruzioni SQL. Ciò significa che la distinzione tra maiuscole e minuscole è alla mercé del tuo SQL Server che per impostazione predefinita ha SQL_Latin1_General_CP1_CI_AS Collation e NON distingue tra maiuscole e minuscole.
L'utilizzo di ObjectQuery.ToTraceString per visualizzare la query SQL generata che è stata effettivamente inviata a SQL Server rivela il mistero:
string sqlQuery = ((ObjectQuery)context.Thingies
.Where(t => t.Name == "ThingamaBob")).ToTraceString();
Quando si crea una query LINQ to Entities , LINQ to Entities sfrutta il parser LINQ per iniziare a elaborare la query e la converte in un albero delle espressioni LINQ. L'albero delle espressioni LINQ viene quindi passato all'API Object Services , che converte l'albero delle espressioni in un albero dei comandi. Viene quindi inviato al provider del negozio (ad esempio SqlClient), che converte l'albero dei comandi nel testo del comando del database nativo. La query viene eseguita sull'archivio dati ei risultati vengono materializzati in oggetti entità da Object Services. Non è stata inserita alcuna logica per tenere conto della distinzione tra maiuscole e minuscole. Quindi, indipendentemente dal caso in cui si inserisce il predicato, verrà sempre considerato lo stesso dal proprio SQL Server a meno che non si modifichino i fascicoli di SQL Server per quella colonna.
Pertanto, la soluzione migliore sarebbe modificare le regole di confronto della colonna Nome nella tabella Thingies in COLLATE Latin1_General_CS_AS, che distingue tra maiuscole e minuscole eseguendolo su SQL Server:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
Per ulteriori informazioni sulle fascicelle di SQL Server , consultare Ricerca query SQL con distinzione tra maiuscole e minuscole in SQL SERVER
L'unica soluzione che puoi applicare sul lato client è usare LINQ to Objects per fare ancora un altro confronto che non sembra essere molto elegante:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
È possibile aggiungere l'annotazione [CaseSensitive] per EF6 + Code-first
Aggiungi queste classi
[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());
}
}
Modifica il tuo DbContext, aggiungi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
"CaseSensitive",
(property, attributes) => attributes.Single().IsEnabled));
base.OnModelCreating(modelBuilder);
}
Quindi fa
Add-Migration CaseSensitive
Aggiornare il database
basato sull'articolo https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ con alcune correzioni di bug
WHERE
le condizioni in SQL Server non fanno distinzione tra maiuscole e minuscole per impostazione predefinita. Rendilo sensibile al maiuscolo / minuscolo modificando le regole di confronto predefinite della colonna ( SQL_Latin1_General_CP1_CI_AS
) in SQL_Latin1_General_CP1_CS_AS
.
Il modo fragile per farlo è con il codice. Aggiungi un nuovo file di migrazione e quindi aggiungilo all'interno del Up
metodo:
public override void Up()
{
Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}
Ma
Puoi creare annotazioni personalizzate chiamate "CaseSensitive" utilizzando le nuove funzionalità EF6 e puoi decorare le tue proprietà in questo modo:
[CaseSensitive]
public string Name { get; set; }
Questo post sul blog spiega come farlo.
La risposta data da @Morteza Manavi risolve il problema. Tuttavia, per una soluzione lato client , un modo elegante sarebbe il seguente (aggiungendo un doppio controllo).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Mi è piaciuta la risposta di Morteza e normalmente preferirei risolvere il problema sul lato server. Per lato client normalmente utilizzo:
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
Fondamentalmente, prima controlla se c'è un utente con i criteri richiesti, quindi controlla se la password è la stessa. Un po 'prolisso, ma sento che è più facile da leggere quando ci possono essere un sacco di criteri coinvolti.
Nessuno dei due ha StringComparison.IgnoreCase
funzionato per me. Ma questo ha fatto:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
How can I achieve case sensitive comparison
Usa string.Equals
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);
Inoltre, non devi preoccuparti di null e ottenere solo le informazioni che desideri.
Usa StringComparision.CurrentCultureIgnoreCase per Case Insensitive.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
Non sono sicuro di EF4, ma EF5 supporta questo:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
enum facessero la differenza. Ho visto abbastanza persone che suggeriscono questo genere di cose dovrebbe funzionare a pensare che il problema è da qualche parte nel file EDMX (db-prima), anche se stackoverflow.com/questions/841226/...