首页 > 解决方案 > 为什么 LINQ to Entities string.Contains(string.Empty) 除了 string.Contains(string.Empty.ToLower()) 什么都不匹配?

问题描述

我正在为库存表的存储库服务编写查询。仅供参考:我们正在使用 C# 7.0、EF 6,并且我们正在使用 Moq 来测试我们的查询。

我了解到,当string.Contains(...)默认情况下区分大小写的 被放入 LINQ 查询然后转换为 SQL 时,结果区分大小写(找到其他 SO 帖子来帮助解决这个问题,我们将处理它),我还发现当参数是并且转换为小写时,这些string.Contains(...)函数似乎有一个怪癖(发现没有关于此的SO帖子)。string.Empty

当 LINQ to Entities 尝试转换为 SQL 时,尝试使用不区分大小写的 string.Contains(...) 重载会遇到异常,因此我必须手动指定column.Contains(argument.ToLower())LINQ to Entities 的 SQL 查询的顺序按预期运行,并且通过模拟单元测试以确保不区分大小写。

问题:如果参数是 string.Empty,则不匹配。罪魁祸首是 then 参数转换为小写。

这不是障碍(只需将argument.ToLower()检查移出查询即可解决问题,无论如何它会更有效),但我仍然想知道发生了什么。

public List<InventoryModel> FindByTrackingNumberSubstring( string substring )
{
    // (bad) matches nothing when argument is string.Empty
    //var query = _modelTable.Where( entity => entity.Tracking_Number.ToLower().Contains( substring.ToLower() ) );

    // (good) matches everything when argument is string.Empty
    string lower = substring.ToLower();
    var query = _modelTable.Where( entity => entity.Tracking_Number.ToLower().Contains( lower ) );

    return query.ToList<InventoryModel>();
}

// SQL for queries 1 and 2, respectively (stripped out SELECT and FROM stuff for brevity)
WHERE ((CASE WHEN (( CAST(CHARINDEX(LOWER(@p__linq__0), LOWER([Extent1].[Tracking Number])) AS int)) > 0) THEN cast(1 as bit) WHEN ( NOT (( CAST(CHARINDEX(LOWER(@p__linq__0), LOWER([Extent1].[Tracking Number])) AS int)) > 0)) THEN cast(0 as bit) END) = 1)
WHERE ((CASE WHEN (LOWER([Extent1].[Tracking Number]) LIKE @p__linq__0 ESCAPE N'~') THEN cast(1 as bit) WHEN ( NOT (LOWER([Extent1].[Tracking Number]) LIKE @p__linq__0 ESCAPE N'~')) THEN cast(0 as bit) END) = 1)

我做了一些检查,发现在 LINQ to Entities 的 SQL 查询中,它string.Contains(string.Empty)匹配任何东西,我发现它string.Empty.ToLower() == string.Empty匹配任何东西,但是将这两者放在一起,C# 和 LINQ to Entities 出现分歧。在前者中,string.Contains(string.Empty.ToLower())匹配任何东西(如预期的那样),但在后者中,什么都不匹配..

为什么?

标签: c#entity-frameworklinq-to-sql

解决方案


我相信这将是 EF 的 SQL Server 提供程序的一个怪癖,因为当您.ToLower()对条件和正在比较的字段执行时,它会将请求识别为明确不区分大小写,并用不区分大小写的 CHARINDEX 比较替换 LIKE 查询以同样的方式处理 SQL Server 中的空字符串。区分大小写的行为取决于数据库引擎,如果是 SQL Server,则取决于为数据库中的字符串选择的排序规则。不知道为什么不能使用 LOWER(Tracking_Number) LIKE LOWER('%%')。

就个人而言,在编写 EF Linq 表达式时,我的查询代码将始终检查字符串上的 IsNullOrEmpty,而不是.Where()在未提供实际条件的情况下附加条件。这种方式 WHERE 子句仅适用于提供的标准。

即如果我相信数据库不会区分大小写:

if(!string.IsNullOrEmpty(substring))
    query = query.Where(entity => entity.Tracking_Number.Contains(substring));

如果我担心数据库可能区分大小写:

if(!string.IsNullOrEmpty(substring))
    query = query.Where( entity => entity.Tracking_Number.ToLower().Contains(substring.ToLower()));

即使在那里,如果数据库仅服务于该应用程序,我更愿意设置一个标准,即 Tracking_Number 始终存储为小写值。实体属性将强制任何设置值小写。(消除查询中对 .Tracking_Number.ToLower() 的需求。)


推荐阅读