为什么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(...)当参数string.Empty被转换为小写,这些函数似乎有些古怪没有关于此的SO帖子)。

尝试使用不区分大小写的字符串。当LINQ to Entities尝试转换为SQL时,Contains(...)重载会被抛出异常,因此我必须手动指定column.Contains(argument.ToLower())LINQ to Entities的SQL查询可以按预期进行操作,并且可以进行不区分大小写的模拟单元测试。

问题:如果参数为string.Empty,则不匹配任何内容。罪魁祸首是何时将参数转换为小写。

这不是障碍(只需将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查询,而CHARINDEX比较则不以相同的方式处理SQL Server中的空字符串。区分大小写的行为将取决于数据库引擎,对于SQL Server,则取决于为数据库中的字符串选择的排序规则。不知道为什么不能使用像LOWER(Tracking_Number)这样的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()。)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章