为什么 LINQ to Entities string.Contains(string.Empty) 匹配任何内容,但 string.Contains(string.Empty.ToLower()) 不匹配任何内容?
Why does LINQ to Entities string.Contains(string.Empty) match anything but string.Contains(string.Empty.ToLower()) match nothing?
我正在为库存 table 编写存储库服务查询。仅供参考:我们正在使用 C# 7.0、EF 6,并且我们正在使用 Moq 来测试我们的查询。
我了解到,将默认区分大小写的string.Contains(...)
放入LINQ查询中,然后转换为SQL,结果是insensitive(找到其他 SO 帖子来帮助解决这个问题,我们会处理它),而且我还发现 string.Contains(...)
函数在参数为 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())
匹配任何内容(正如预期的那样),但在后者中什么都不匹配..
为什么?
我认为这将是 EF 的 SQL 服务器提供商的一个怪癖,因为当您对条件和正在比较的字段执行 .ToLower()
时,它会将请求明确识别为 case-insensitive 并用 CHARINDEX 比较替换 LIKE 查询,CHARINDEX 比较不以相同方式处理 SQL 服务器中的空字符串。区分大小写的行为将取决于数据库引擎,在 SQL 服务器的情况下,为数据库中的字符串选择的排序规则。不确定为什么不能使用 LOWER(Tracking_Number) LIKE LOWER('%%')。
就我个人而言,在编写 EF Linq 表达式时,我的查询代码将始终检查字符串上的 IsNullOrEmpty 并且不会附加 .Where()
未提供实际条件的条件。这样,WHERE 子句仅适用于提供的条件。
即如果我相信数据库不会被整理 case-sensitive:
if(!string.IsNullOrEmpty(substring))
query = query.Where(entity => entity.Tracking_Number.Contains(substring));
如果我担心数据库会被整理case-sensitive:
if(!string.IsNullOrEmpty(substring))
query = query.Where( entity => entity.Tracking_Number.ToLower().Contains(substring.ToLower()));
即便如此,我更愿意设置一个标准,如果数据库只为这个应用程序服务,Tracking_Number 总是存储为 lower-case 值。实体属性将强制任何设置值为 lower-cased。 (删除查询中对 .Tracking_Number.ToLower() 的需要。)
我正在为库存 table 编写存储库服务查询。仅供参考:我们正在使用 C# 7.0、EF 6,并且我们正在使用 Moq 来测试我们的查询。
我了解到,将默认区分大小写的string.Contains(...)
放入LINQ查询中,然后转换为SQL,结果是insensitive(找到其他 SO 帖子来帮助解决这个问题,我们会处理它),而且我还发现 string.Contains(...)
函数在参数为 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())
匹配任何内容(正如预期的那样),但在后者中什么都不匹配..
为什么?
我认为这将是 EF 的 SQL 服务器提供商的一个怪癖,因为当您对条件和正在比较的字段执行 .ToLower()
时,它会将请求明确识别为 case-insensitive 并用 CHARINDEX 比较替换 LIKE 查询,CHARINDEX 比较不以相同方式处理 SQL 服务器中的空字符串。区分大小写的行为将取决于数据库引擎,在 SQL 服务器的情况下,为数据库中的字符串选择的排序规则。不确定为什么不能使用 LOWER(Tracking_Number) LIKE LOWER('%%')。
就我个人而言,在编写 EF Linq 表达式时,我的查询代码将始终检查字符串上的 IsNullOrEmpty 并且不会附加 .Where()
未提供实际条件的条件。这样,WHERE 子句仅适用于提供的条件。
即如果我相信数据库不会被整理 case-sensitive:
if(!string.IsNullOrEmpty(substring))
query = query.Where(entity => entity.Tracking_Number.Contains(substring));
如果我担心数据库会被整理case-sensitive:
if(!string.IsNullOrEmpty(substring))
query = query.Where( entity => entity.Tracking_Number.ToLower().Contains(substring.ToLower()));
即便如此,我更愿意设置一个标准,如果数据库只为这个应用程序服务,Tracking_Number 总是存储为 lower-case 值。实体属性将强制任何设置值为 lower-cased。 (删除查询中对 .Tracking_Number.ToLower() 的需要。)