Это сравнение в LINQ to Entities не учитывает регистр:
Thingies.First(t => t.Name == "ThingamaBob");
Как добиться сравнения с учетом регистра с LINQ to Entities?
Это сравнение в LINQ to Entities не учитывает регистр:
Thingies.First(t => t.Name == "ThingamaBob");
Как добиться сравнения с учетом регистра с LINQ to Entities?
Ответы:
Это потому, что вы используете LINQ To Entities, который в конечном итоге преобразует ваши лямбда-выражения в операторы SQL. Это означает, что чувствительность к регистру находится во власти вашего SQL Server, который по умолчанию имеет сопоставление SQL_Latin1_General_CP1_CI_AS и НЕ чувствителен к регистру.
Использование ObjectQuery.ToTraceString для просмотра сгенерированного запроса SQL, который был фактически отправлен в SQL Server, раскрывает тайну:
string sqlQuery = ((ObjectQuery)context.Thingies
.Where(t => t.Name == "ThingamaBob")).ToTraceString();
Когда вы создаете запрос LINQ to Entities , LINQ to Entities использует синтаксический анализатор LINQ, чтобы начать обработку запроса и преобразовать его в дерево выражений LINQ. Затем дерево выражений LINQ передается в API объектных служб , который преобразует дерево выражений в дерево команд. Затем он отправляется поставщику хранилища (например, SqlClient), который преобразует дерево команд в текст команды собственной базы данных. Запрос выполняется в хранилище данных, и результаты материализуются в объекты сущности с помощью служб объектов., Никакой логики для учета чувствительности к регистру не использовалось. Таким образом, независимо от того, какой регистр вы укажете в своем предикате, он всегда будет обрабатываться вашим SQL Server как один, если вы не измените параметры сортировки SQL Server для этого столбца.
Поэтому лучшим решением было бы изменить параметры сортировки столбца Name в таблице Thingies на COLLATE Latin1_General_CS_AS, который чувствителен к регистру, запустив это на вашем SQL Server:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
Для получения дополнительной информации о SQL Server Collates , взгляните на SQL SERVER Collate с учетом регистра SQL Query Search
Единственное решение, которое вы можете применить на стороне клиента, - это использовать LINQ to Objects, чтобы провести еще одно сравнение, которое не выглядит очень элегантным:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
Вы можете добавить аннотацию [CaseSensitive] для EF6 + Code-first
Добавить эти классы
[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());
}
}
Измените свой DbContext, добавьте
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
"CaseSensitive",
(property, attributes) => attributes.Single().IsEnabled));
base.OnModelCreating(modelBuilder);
}
Тогда сделай
Надстройка миграции CaseSensitive
Обновление базы данных
на основе статьи https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ с исправлением некоторых ошибок
WHERE
Условия в SQL Server по умолчанию нечувствительны к регистру. Сделайте регистр чувствительным, изменив параметры сортировки столбца по умолчанию ( SQL_Latin1_General_CP1_CI_AS
) на SQL_Latin1_General_CP1_CS_AS
.
Хрупкий способ сделать это - использовать код. Добавьте новый файл миграции, а затем добавьте его внутри Up
метода:
public override void Up()
{
Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}
Но
Вы можете создать настраиваемую аннотацию под названием CaseSensitive, используя новые функции EF6, и украсить свои свойства следующим образом:
[CaseSensitive]
public string Name { get; set; }
В этом сообщении блога объясняется, как это сделать.
Ответ @Morteza Manavi решает проблему. Тем не менее, для решения на стороне клиента элегантным способом будет следующий (добавление двойной проверки).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Мне понравился ответ Мортеза, и обычно я предпочел бы исправить на стороне сервера. На стороне клиента я обычно использую:
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
По сути, сначала проверяется, есть ли пользователь с требуемыми критериями, а затем проверяется, тот же ли пароль. Немного многословно, но я чувствую, что его легче читать, когда может быть задействован целый ряд критериев.
Ни один из них не StringComparison.IgnoreCase
работал у меня. Но это произошло:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
How can I achieve case sensitive comparison
Используйте string.Equals
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);
Кроме того, вам не нужно беспокоиться о null и получить только ту информацию, которую вы хотите.
Используйте StringComparision.CurrentCultureIgnoreCase для нечувствительности к регистру.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
Не уверен насчет EF4, но EF5 это поддерживает:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
перечисление иметь значение. Я видел достаточно людей, предлагающих такие вещи, чтобы думать, что проблема где-то в файле EDMX (db-first), хотя stackoverflow.com/questions/841226/…