在LINQ和Entities中,这不是区分大小写的比较:
Thingies.First(t => t.Name == "ThingamaBob");
如何使用LINQ to Entities实现区分大小写的比较?
在LINQ和Entities中,这不是区分大小写的比较:
Thingies.First(t => t.Name == "ThingamaBob");
如何使用LINQ to Entities实现区分大小写的比较?
Answers:
这是因为您使用的是LINQ To Entities,最终将Lambda表达式转换为SQL语句。这意味着区分大小写取决于您的SQL Server,默认情况下,SQL Server具有SQL_Latin1_General_CP1_CI_AS排序规则,并且不区分大小写。
使用ObjectQuery.ToTraceString查看实际上已提交给SQL Server的生成的SQL查询揭示了一个奥秘:
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始终将其视为相同。
因此,最好的解决方案是将Thingies表中“ 名称”列的排序规则更改为COLLATE Latin1_General_CS_AS,这是区分大小写的,方法是在SQL Server上运行以下命令:
ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS
有关SQL Server 排序规则的更多信息,请查看SQL SERVER排序规则区分大小写的SQL查询搜索。
您可以在客户端应用的唯一解决方案是使用LINQ to Objects进行另一个比较,这似乎并不十分优雅:
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
您可以为EF6 +代码优先添加[CaseSensitive]批注
添加此类
[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);
}
然后做
添加迁移案例敏感
更新数据库
根据文章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");
}
但
您可以使用新的EF6功能创建名为“ CaseSensitive”的自定义注释,并可以如下装饰属性:
[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;
我喜欢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
基本上,首先检查是否有符合要求的用户,然后检查密码是否相同。有点冗长,但是当涉及到很多标准时,我觉得它更容易阅读。
使用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-第一),但stackoverflow.com/questions/841226/...