为什么LINQ to Entities string.Contains(string.Empty)除string.Contains(string.Empty.ToLower())之外都不匹配?

时间:2019-05-15 17:14:59

标签: c# entity-framework linq-to-sql

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

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

尝试使用不区分大小写的字符串。当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放在一起实体分歧。在前一种情况下,string.Contains(string.Empty.ToLower())匹配任何内容(按预期方式),而在后一种情况下,则不匹配任何内容。

为什么?

1 个答案:

答案 0 :(得分:2)

我认为这将是EF的SQL Server提供程序的一个怪癖,因为当您对条件和要比较的字段执行.ToLower()时,它会将请求识别为明显不区分大小写,并替换了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()。)