Esta não é uma comparação que diferencia maiúsculas de minúsculas no LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Como posso fazer uma comparação que diferencia maiúsculas de minúsculas com o LINQ to Entities?
Esta não é uma comparação que diferencia maiúsculas de minúsculas no LINQ to Entities:
Thingies.First(t => t.Name == "ThingamaBob");
Como posso fazer uma comparação que diferencia maiúsculas de minúsculas com o LINQ to Entities?
Respostas:
Isso porque você está usando LINQ To Entities, que, em última análise, converte suas expressões Lambda em instruções SQL. Isso significa que a diferenciação de maiúsculas e minúsculas está à mercê de seu SQL Server, que por padrão tem SQL_Latin1_General_CP1_CI_AS Collation e NÃO diferencia maiúsculas de minúsculas.
Usar ObjectQuery.ToTraceString para ver a consulta SQL gerada que foi realmente enviada ao SQL Server revela o mistério:
string sqlQuery = ((ObjectQuery)context.Thingies
.Where(t => t.Name == "ThingamaBob")).ToTraceString();
Quando você cria uma consulta LINQ to Entities , LINQ to Entities aproveita o analisador LINQ para começar a processar a consulta e convertê-la em uma árvore de expressão LINQ. A árvore de expressão LINQ é então passada para a API de serviços de objeto , que converte a árvore de expressão em uma árvore de comando. Em seguida, ele é enviado ao provedor de armazenamento (por exemplo, SqlClient), que converte a árvore de comando no texto de comando do banco de dados nativo. A consulta é executada no armazenamento de dados e os resultados são materializados em objetos de entidade por serviços de objeto. Nenhuma lógica foi colocada no meio para levar em consideração a diferenciação de maiúsculas e minúsculas. Portanto, não importa que caso você coloque em seu predicado, ele sempre será tratado como o mesmo pelo seu SQL Server, a menos que você altere seus agrupamentos do SQL Server para essa coluna.
Portanto, a melhor solução seria alterar o agrupamento da coluna Nome na tabela Thingies para COLLATE Latin1_General_CS_AS, que diferencia maiúsculas de minúsculas executando-o em seu SQL Server:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
Para obter mais informações sobre o SQL Server Collates , dê uma olhada em SQL SERVER Collate Pesquisa de consulta SQL sensível a maiúsculas e minúsculas
A única solução que você pode aplicar no lado do cliente é usar LINQ to Objects para fazer outra comparação que não parece ser muito elegante:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
Você pode adicionar a anotação [CaseSensitive] para EF6 + Code-first
Adicionar esta classe
[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());
}
}
Modifique seu DbContext, adicione
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
"CaseSensitive",
(property, attributes) => attributes.Single().IsEnabled));
base.OnModelCreating(modelBuilder);
}
Então faça
Add-Migration CaseSensitive
Atualizar o banco de dados
com base no artigo https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ com alguma correção de bug
WHERE
as condições no SQL Server não diferenciam maiúsculas de minúsculas por padrão. Faça distinção entre maiúsculas e minúsculas alterando os agrupamentos padrão da coluna ( SQL_Latin1_General_CP1_CI_AS
) para SQL_Latin1_General_CP1_CS_AS
.
A maneira frágil de fazer isso é com código. Adicione um novo arquivo de migração e, em seguida, adicione-o dentro do Up
método:
public override void Up()
{
Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}
Mas
Você pode criar uma anotação personalizada chamada "CaseSensitive" usando os novos recursos do EF6 e pode decorar suas propriedades assim:
[CaseSensitive]
public string Name { get; set; }
Esta postagem do blog explica como fazer isso.
A resposta dada por @Morteza Manavi resolve o problema. Ainda assim, para uma solução do lado do cliente , uma maneira elegante seria a seguinte (adicionando uma dupla verificação).
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Gostei da resposta de Morteza e normalmente prefiro consertar no servidor. Para o lado do cliente, eu normalmente uso:
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
Basicamente, primeiro verifique se existe um usuário com os critérios exigidos, depois verifique se a senha é a mesma. Um pouco prolixo, mas acho que é mais fácil de ler quando pode haver um monte de critérios envolvidos.
Nenhum dos StringComparison.IgnoreCase
funcionou para mim. Mas isso fez:
context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
How can I achieve case sensitive comparison
Use string.Equals
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);
Além disso, você não precisa se preocupar com null e obter de volta apenas as informações que deseja.
Use StringComparision.CurrentCultureIgnoreCase para não diferenciar maiúsculas de minúsculas.
Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
Não tenho certeza sobre o EF4, mas o EF5 oferece suporte para:
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
que diferencia maiúsculas de minúsculas no banco de dados, não consegui fazer com que esta ou qualquer uma das outras enums fizesse diferença. Já vi pessoas suficientes sugerindo que esse tipo de coisa deve funcionar para pensar que o problema está em algum lugar no arquivo EDMX (db-first), embora stackoverflow.com/questions/841226/…