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

时间:2009-12-10 08:20:50

标签: c# .net linq linq-to-sql linq-to-objects

出于某种原因,我使用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代码并获得成功结果)。我甚至有一个链接Anders himself表明这应该有效。到底是什么?

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

3 个答案:

答案 0 :(得分:5)

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

任何意味着在数据库端执行的内容的一部分需要进行SQL翻译。 不能包含任意.NET代码 - 只有LINQ-to-SQL能够翻译的代码。否则,组合会被破坏 - 如果你之后添加一个连接或一个排序等,那么在SQL中进行某些处理会很困难,而在客户端进行一些,将两者混合在一起。您可以在SQL中执行一些操作,然后在客户端执行然后。请注意,您可以使用AsEnumerable替代ToList,以便在进程中执行其余查询。

答案 1 :(得分:3)

在Konamiman的建议中我checked the log查看正在执行的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将其标记为转义字符,因此不会在搜索中使用它。 This MSDN thread does a decent job of explaining why;它似乎是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()方法的假设是不正确的。)

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

答案 2 :(得分:2)

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

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

使用you can take a look at the generated SQL属性记住DataContext.Log。这可以帮助您了解它是如何工作的。