String.StartsWith不使用波形符(“〜”)字符LINQ to SQL?

出于某种原因,我使用String.StartsWith()调用IEnumerable.Where()似乎给出了不同的结果,具体取决于它是在LINQ-to-SQL还是标准LINQ(-to-objects)中使用。 如果我在ToList()上添加一个对同一个调用的调用,我会得到不同的结果:

var withToList = MyDataContext.MyEntities.ToList().Where(entity => entity.Name.StartsWith("~Test: My Test String")); // withToList.Count() returns 5, which is what I expect. var direct = MyDataContext.MyEntities.Where(entity => entity.Name.StartsWith("~Test: My Test String")); // direct.Count() returns 0 

我的理解是,与LINQ中的其他一些运算符/方法不同,Where()方法不要求谓词是SQL可翻译的; 它在客户端执行,因此可以是任意.NET代码。 (我肯定会在其中抛出其他非SQL代码并获得成功结果)。 我甚至得到了一个链接,其中安德斯自己暗示这应该有效。 有没有搞错?

编辑:我发现了问题; 它与我的搜索字符串中存在波浪号有关。 我更新了标题以反映这一点。

此查询在SQL中执行 – 因此您可能会看到一些奇怪的内容,具体取决于SQL如何处理“LIKE”。 我建议你找出它正在运行的查询,并尝试在SQL Management Studio中自己运行它。 在这种情况下,它似乎并不常见 – 它可能是LINQ to SQL中的一个错误; 它可能无法正确逃避。 (是否表示SQL LIKE子句中的任何特殊内容?)

任何想要成为数据库端执行内容的东西需要进行SQL翻译。 它不能包含任意.NET代码 – 只有LINQ-to-SQL能够翻译的代码。 否则,组合会被破坏 – 如果你之后添加一个连接或一个排序等,那么在SQL中进行一些处理会变得非常困难,而在客户端进行一些处理会混合两者。 你可以在SQL中做一些, 然后在客户端做一些。 请注意,您可以使用AsEnumerable作为ToList的替代方法,以使其余查询在进程中执行。

在Konamiman的建议下,我检查了日志以查看正在执行的SQL。 这是我所得到的(一个广泛的例子)。

第一个调用执行:

 SELECT [t0].[ID], [t0].[Name] FROM [dbo].[MyEntity] AS [t0] 

这是有道理的,因为过滤发生在客户端,所以我们需要查询所有行。

第二个调用执行:

 SELECT COUNT(*) AS [value] FROM [dbo].[MyEntity] AS [t0] WHERE [t0].[Name] LIKE @p0 ESCAPE '~' 

所以,Jon Skeet走在了正确的轨道上; 我遇到了问题,因为我碰巧在我的数据/查询条件中有波形符号。 这会导致LINQ-to-SQL将其标记为转义字符,因此不会在搜索中使用它。 这个MSDN线程在解释原因方面做得不错 ; 它似乎是LINQ-to-SQL代码中的一个错误。

建议的解决方法是使用LINQ的SQLMethods.Like()方法将转义字符从“〜”更改为,如下所示:

 var direct = MyDataContext.MyEntities.Where(entity => SQLMethods.Like(entity.Name, "~Test: My Test String%", "!"); // direct.Count() now returns 5 as expected 

这很有效,但不幸的是,这是一个仅限 LINQ-to-SQL的解决方案。 如果您在LINQ-to-Object版本上尝试它,则会收到以下错误:

 System.NotSupportedException : Method 'Boolean Like(System.String, System.String, Char)' cannot be used on the client; it is only for translation to SQL. 

当Jon Skeet建议使用AsEnumerable()时会发生同样的事情。

我尝试在一个方法中包装我的StartsWith调用并使用带有StringComparisonOption的StartsWith,但是这些在LINQ-to-SQL方面不起作用,因为它们不是SQL可翻译的。 (因此我之前关于Where()方法的假设是不正确的)。

所以:看起来(直到修复了这个错误)你不能拥有一个搜索function,它既可以使用波形符,也可以与LINQ的基本风格无关。 (如果有方法,请务必发布)。

在第一种情况下, Where谓词实际上不需要可以转换为SQL,因为您首先将整个表转换为内存( ToList )然后进行过滤。

在第二种情况下, Where谓词需要可以转换为SQL WHERE子句,因为过滤是在数据库中完成的。 TheString.StartsWith方法被转换为SQL LIKE语句。

请记住, 您可以使用DataContext.Log属性查看生成的SQL 。 这应该可以帮助您了解它的工作原理。