To nie jest porównanie z rozróżnianiem wielkości liter w LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Jak mogę uzyskać porównanie z rozróżnianiem wielkości liter w LINQ to Entities?
To nie jest porównanie z rozróżnianiem wielkości liter w LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Jak mogę uzyskać porównanie z rozróżnianiem wielkości liter w LINQ to Entities?
Odpowiedzi:
Dzieje się tak, ponieważ używasz LINQ To Entities, które ostatecznie konwertuje twoje wyrażenia Lambda na instrukcje SQL. Oznacza to, że rozróżnianie wielkości liter zależy od serwera SQL Server, który domyślnie ma sortowanie SQL_Latin1_General_CP1_CI_AS i NIE rozróżnia wielkości liter.
Użycie ObjectQuery.ToTraceString do zobaczenia wygenerowanego zapytania SQL, które zostało faktycznie przesłane do SQL Server, ujawnia tajemnicę:
string sqlQuery = ((ObjectQuery)context.Thingies
.Where(t => t.Name == "ThingamaBob")).ToTraceString();
Podczas tworzenia LINQ podmiotom kwerendy LINQ do podmiotów wykorzystuje parser LINQ, aby rozpocząć przetwarzanie zapytań i zamienia go na drzewie wyrażenie LINQ. Drzewo wyrażeń LINQ jest następnie przesyłane do interfejsu API usług obiektów , które konwertuje drzewo wyrażeń na drzewo poleceń. Następnie jest wysyłany do dostawcy sklepu (np. SqlClient), który konwertuje drzewo poleceń na tekst polecenia natywnej bazy danych. Zapytanie jest wykonywane w magazynie danych, a wyniki są materializowane w obiektach jednostek przez usługi obiektowe. Nie wprowadzono żadnej logiki, aby uwzględnić rozróżnianie wielkości liter. Więc bez względu na to, jaki przypadek umieścisz w swoim predykacie, będzie on zawsze traktowany jako taki sam przez serwer SQL Server, chyba że zmienisz sortowanie SQL Server dla tej kolumny.
Dlatego najlepszym rozwiązaniem byłaby zmiana sortowania kolumny Name w tabeli Thingies na COLLATE Latin1_General_CS_AS, w której rozróżniana jest wielkość liter, uruchamiając to na serwerze SQL:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
Aby uzyskać więcej informacji na temat SQL Server zestawia podjąć aa spojrzenie na SQL Server Sortowanie Case Sensitive SQL Query Szukaj
Jedynym rozwiązaniem, które możesz zastosować po stronie klienta, jest użycie LINQ to Objects do wykonania kolejnego porównania, które nie wydaje się zbyt eleganckie:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
Możesz dodać adnotację [CaseSensitive] dla EF6 + Code-first
Dodaj te zajęcia
[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());
}
}
Zmodyfikuj swój DbContext, dodaj
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
"CaseSensitive",
(property, attributes) => attributes.Single().IsEnabled));
base.OnModelCreating(modelBuilder);
}
Więc zrób
Add-Migration CaseSensitive
Zaktualizować bazę danych
na podstawie artykułu https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ z poprawką błędów
WHERE
warunki w SQL Server domyślnie nie uwzględniają wielkości liter. Uwzględnij wielkość liter, zmieniając domyślne sortowanie kolumny ( SQL_Latin1_General_CP1_CI_AS
) na SQL_Latin1_General_CP1_CS_AS
.
Kruchym sposobem na to jest użycie kodu. Dodaj nowy plik migracji, a następnie dodaj to wewnątrz Up
metody:
public override void Up()
{
Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}
Ale
Możesz utworzyć niestandardową adnotację o nazwie „Uwzględnianie wielkości liter” za pomocą nowych funkcji EF6 i ozdobić swoje właściwości w następujący sposób:
[CaseSensitive]
public string Name { get; set; }
Ten wpis na blogu wyjaśnia, jak to zrobić.
Odpowiedź udzielona przez @Morteza Manavi rozwiązuje problem. Mimo to, dla rozwiązania po stronie klienta , elegancki sposób byłby następujący (dodanie podwójnego sprawdzenia).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Podobała mi się odpowiedź Morteza i normalnie wolałbym to naprawić po stronie serwera. Po stronie klienta zwykle używam:
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
Zasadniczo najpierw sprawdź, czy jest użytkownik z wymaganymi kryteriami, a następnie sprawdź, czy hasło jest takie samo. Trochę rozwlekły, ale wydaje mi się, że jest łatwiejszy do odczytania, gdy w grę wchodzi cała masa kryteriów.
Żaden z nich nie StringComparison.IgnoreCase
działał dla mnie. Ale to zrobiło:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
How can I achieve case sensitive comparison
Użyj string.Equals
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);
Nie musisz też martwić się o wartość null i odzyskać tylko potrzebne informacje.
Użyj StringComparision.CurrentCultureIgnoreCase dla bez uwzględniania wielkości liter.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
Nie jestem pewien co do EF4, ale EF5 obsługuje to:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
wyliczenia, aby coś zmienić. Widziałem wystarczająco dużo osób sugerujących, że tego rodzaju rzeczy powinny działać, aby pomyśleć, że problem jest gdzieś w pliku EDMX (db-first), chociaż stackoverflow.com/questions/841226/ ...