LINQ to Entities 区分大小写比较

Posted

技术标签:

【中文标题】LINQ to Entities 区分大小写比较【英文标题】:LINQ to Entities case sensitive comparison 【发布时间】:2011-04-20 01:46:54 【问题描述】:

这不是 LINQ to Entities 中区分大小写的比较:

Thingies.First(t => t.Name == "ThingamaBob");

如何使用 LINQ to Entities 实现区分大小写的比较?

【问题讨论】:

@Ronnie:你确定吗?你是说不区分大小写比较吗? 绝对确定。不,我不是那个意思。 不,在我运行 EF 4.0 w/SQL Server 2008 R2 的计算机上,以上内容不区分大小写。我知道很多地方都说 EF 默认区分大小写,但这不是我所经历的。 那不依赖于底层数据库吗? @codymanix:这是个好问题! Linq to EF 是否翻译数据库查询的 lambda 表达式?我不知道答案。 【参考方案1】:

@Morteza Manavi 给出的答案解决了这个问题。尽管如此,对于 客户端解决方案,以下是一种优雅的方式(添加双重检查)。

var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
    .FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;

【讨论】:

这不是那么优雅,因为如果返回的单个记录不是完全区分大小写的匹配怎么办?我们回到数据库吗?可能存在我们未检索到的区分大小写的匹配项。【参考方案2】:

那是因为您使用的是 LINQ To Entities,它最终将您的 Lambda 表达式转换为 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 Collat​​es。

服务器端解决方案:

因此,最好的解决方案是将 Thingies 表中 Name 列的排序规则更改为 COLLATE Latin1_General_CS_AS 在 SQL Server 上运行此命令区分大小写:

ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS

有关SQL Server Collat​​es的更多信息,请查看SQL SERVER Collate Case Sensitive SQL Query Search

客户端解决方案:

您可以在客户端应用的唯一解决方案是使用 LINQ to Objects 进行另一个看起来不太优雅的比较:

Thingies.Where(t => t.Name == "ThingamaBob")
        .AsEnumerable()
        .First(t => t.Name == "ThingamaBob");

【讨论】:

我正在使用实体框架生成数据库模式,因此使用我的调用代码的解决方案将是最好的。我想我会在结果回来后进行检查。谢谢。 没问题。是的,这是正确的,我已经用客户端解决方案更新了我的答案,但是它不是很优雅,我仍然建议使用数据存储解决方案。 @eglasius 这并不完全正确:它不会获取所有数据,它只获取不区分大小写匹配的数据,然后在客户端再次过滤大小写敏感的数据。当然,如果您碰巧有数千个匹配不区分大小写的条目,但其中只有一个是正确的区分大小写的,那么开销会很大。但我不认为现实会呈现这样的场景...... :) @MassoodKhaari 您发布的该解决方案会使它不区分大小写,因为您在比较的两边都小写了。 OP 需要区分大小写的比较。 "因此,最好的解决方案是将 Thingies 表中 Name 列的排序规则更改为 COLLATE Latin1_General_CS_AS" - 我认为这不是最好的。大多数时候我需要不区分大小写的 LIKE 过滤器(.Contains()),但有时它应该区分大小写。我会试试你的“客户端解决方案”——我认为它对我的用例来说更优雅(很高兴理解它的作用,但你不能拥有它:))。【参考方案3】:

默认情况下,SQL Server 中的WHERE 条件不区分大小写。通过将列的默认排序规则 (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; 

blog post 解释了如何做到这一点。

【讨论】:

在那篇文章中有一个错误【参考方案4】:

使用字符串.Equals

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);

另外,您不必担心 null 并且只取回您想要的信息。

使用 StringComparision.CurrentCultureIgnoreCase 区分大小写。

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);

【讨论】:

Equals() 无法转换为 SQL... 此外,如果您尝试使用实例方法,则会忽略 StringComparison。 你试过这个解决方案了吗?我最终尝试了这个,以便与 EF 一起正常工作。【参考方案5】:

StringComparison.IgnoreCase 都不适合我。但这确实:

context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));

【讨论】:

这对提出的问题没有帮助,即How can I achieve case sensitive comparison【参考方案6】:

您可以为 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/ 并修复了一些错误

【讨论】:

【参考方案7】:

不确定 EF4,但 EF5 支持:

Thingies
    .First(t => t.Name.Equals(
        "ThingamaBob",
        System.StringComparison.InvariantCultureIgnoreCase)

【讨论】:

好奇生成了什么sql。 我用 EF5 检查了这个,它只是在 SQL 中生成了一个 WHERE ... = ...。同样,这取决于 SQL 服务器端的排序规则设置。 即使在数据库中有区分大小写的排序规则,我也无法得到这个或任何其他 StringComparison 枚举来产生影响。我已经看到足够多的人建议这种事情应该可以认为问题出在 EDMX 文件中的某个地方(db-first),尽管***.com/questions/841226/…【参考方案8】:

我喜欢 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

基本上,首先检查是否有符合要求的用户,然后检查密码是否相同。 有点啰嗦,但我觉得可能涉及一大​​堆标准时更容易阅读。

【讨论】:

这个答案意味着您将密码作为纯文本存储在数据库中,这是一个巨大的安全漏洞。 @JasonCoyne 他正在比较的密码可能已经被散列了

以上是关于LINQ to Entities 区分大小写比较的主要内容,如果未能解决你的问题,请参考以下文章

在 LINQ to Entities 中正确比较没有时间的 DatetimeOffset

在带有 LINQ to XML 的 VB.NET 中,where 子句在属性值和字符串之间设置不区分大小写的比较

VB.Net Linq to Entities Null 比较 - “啥都没有”或“= 啥都没有”?

LINQ to DataSet 不区分大小写分组方式

如何使用 LINQ to Entities 获取字节数组长度?

为啥 LINQ-to-Entities 将此查询放在子选择中?