c# - 为什么 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())
匹配任何东西(如预期的那样),但在后者中,什么都不匹配..
为什么?
解决方案
我相信这将是 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() 的需求。)
推荐阅读
- javascript - 如何使用 ajax 使用数据库中的数据填充带有选择选项的输入?
- html - Amp-carousel 停止自动播放功能非常随机 - AMPHTML Ads
- angular6 - 材料仪表板选项卡中的分页不起作用
- c# - 如何解析带有嵌套 XML 文本的 XML
- android - 将自定义字体设置为对话框
- java - 如何在 InfluxDB 中使用 Java 创建和设置连续查询?
- javascript - 如何在javascript中正确存储和显示不同时区的工作时间?
- java - 将从 sqlite 加载的图像插入滑块
- validation - 如何自定义 react-final-form 记录和字段验证?
- spring-boot - 外部化属性,以便部署不需要代码编译