Сравнение с регистром LINQ to Entities
Это не чувствительное к регистру сравнение в LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Как я могу получить чувствительное к регистру сравнение с LINQ to Entities?
Ответы
Ответ 1
Это потому, что вы используете 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 затем передается в Object Services API, который преобразует дерево выражений в дерево команд. Затем он отправляется поставщику магазина (например, SqlClient), который преобразует дерево команд в текст команды основной базы данных. Запрос выполняется в хранилище данных, и результаты материализуются в объекты объектов через службы объектов. Никакая логика не была вставлена между ними, чтобы учитывать чувствительность к регистру. Поэтому, независимо от того, в каком случае вы помещаете в свой предикат, он всегда будет рассматривать то же самое на вашем SQL Server, если вы не измените ваши SQL Server Collates для этого столбца.
Решение на стороне сервера:
Следовательно, лучшим решением было бы изменить сортировку столбца 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. Скомпоновать регистр с SQL-запросом..
Клиентское решение:
Единственное решение, которое вы можете применить на стороне клиента, - это использовать LINQ to Objects для еще одного сравнения, которое кажется не очень элегантным:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
Ответ 2
Вы можете добавить аннотацию [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);
}
Тогда do
Add-Migration CaseSensitive
Update-Database
на основе статьи https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ с исправлением ошибок
Ответ 3
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; }
В этом сообщении в блоге объясняется, как это сделать.
Ответ 4
Ответ, данный @Morteza Manavi, решает проблему. Тем не менее, для решения на стороне клиента элегантным способом было бы следующее (добавление двойной проверки).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Ответ 5
Мне понравился Morteza ответ, и он обычно предпочитает исправлять на стороне сервера.
Для клиентской стороны я обычно использую:
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
В принципе, сначала проверяя, есть ли у пользователя требуемые критерии, а затем проверьте, совпадает ли пароль.
Немного длинный, но я чувствую, что его легче читать, когда может быть целая куча критериев.
Ответ 6
Ни один из StringComparison.IgnoreCase
не работал у меня. Но это произошло:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
Ответ 7
Использовать 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);
Ответ 8
Не уверен в EF4, но EF5 поддерживает это:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)