为什么这个Linq查询对于Count()返回0 ?
本文关键字:返回 Count Linq 查询 为什么 | 更新日期: 2023-09-27 18:01:28
这似乎不一致,但我可能只是错过了一些明显的东西。基本查询是:
var events = db.tbl_special_events.Where(x => x.TimeStamp >= startDate);
当我运行以下代码块时,出现了明显的不一致:
int c1 = 0;
foreach (var e in events)
{
if (e.TimeStamp.DayOfWeek.ToString() == "Tuesday") c1++;
}
int c2 = events.Where(e => e.TimeStamp.DayOfWeek.ToString() == "Tuesday").Count();
运行之后,c1是1832,但c2是0。我错过了什么?
我重新创建了这个测试,发现它可能与DateTime函数直接相关。
生成的查询:
exec sp_executesql N'SELECT [t0].[User_ID], [t0].[Email], [t0].[Password], [t0].[BrandID], [t0].[CustomerID], [t0].[Created], [t0].[EndDate], [t0].[AccountStatusID], [t0].[Verified], [t0].[ResetPasswordFailCount], [t0].[ResetPasswordLocked]
FROM [dbo].[User] AS [t0]
WHERE ((CONVERT(NVarChar,CONVERT(Int,(DATEPART(dw, [t0].[Created]) + (@@DATEFIRST) + 6) % 7))) = @p0) AND ([t0].[BrandID] = @p1)',N'@p0 nvarchar(4000),@p1 int',@p0=N'Tuesday',@p1=3
注意@p0=N' tuesday '
请记住IQueryable和IEnumerable的不同之处在于IEnumerable代表一个实际的。net对象,IQueryable将你的表达式转换成一个实际的用于查询数据库的SQL语句。因此,你在表达式中提供的任何值实际上都被发送到数据库。
返回0个结果,因为没有匹配。原因是,SQL中的日期转换返回2而不是'Tuesday'。如果你在LINQ WHERE子句中将Tuesday替换为2,你可以对此进行测试,它实际上会工作。这将在枚举它之后工作,因为结果将被成功地映射到一个可用的。net对象中,其中DateTime。DayOfWeek转换为"Tuesday"将正常工作。
您的计数作用于IQueryable<Event>
,而e
是枚举实例。因此,有些操作可能无法工作,因为它们无法转换为SQL [Edit:或将转换为无意义的SQL]。
要确保Where
子句有效,请在其前面添加AsEnumerable()
。这会将IQueryable<Event>
转换为IEnumerable<Event>
,并告诉linq提供程序它应该在此时停止生成SQL。
int c2 = events.AsEnumerable()
.Where(e => e.TimeStamp.DayOfWeek.ToString() == "Tuesday")
.Count();
不能转换的实际代码在SQL中导致问题的是e.TimeStamp.DayOfWeek.ToString()
。
或者,你可以使用System.Data.Objects.SqlClient.SqlFunctions
(这里的文档)类来提示linq提供程序在SQL中应该做什么。
编辑
正如@Servy指出的,这是一个Linq to SQL问题。然而,这个问题很常见,所以我留下了答案,不删除它。
再看一下OP,整个游戏可能还有另一个变量…延迟加载。
在foreach循环中,时间戳是延迟加载的。在计数查询中,提供程序尝试构造一个SQL查询,在此构造期间,它可能无法处理ToString
(已知这是有问题的),并将e.TimeStamp.DayOfWeek.ToString()
计算为与"Tuesday"
不同的值。
AsEnumerable()
强制提供者停止生成SQL,以便e.TimeStamp
再次惰性加载。
唯一确切知道发生了什么的方法,是在DB上使用跟踪工具(例如SQL Server Profiler)来实际看到在服务器上执行的查询。
编辑2
基于@Sinaesthetic的回答,返回0的原因是查询试图将"4"
与返回false的"Tuesday"
进行比较,因此正确的结果是false
。
可以通过执行
来测试select ((CONVERT(NVarChar,CONVERT(Int,(DATEPART(dw, getdate()) + (@@DATEFIRST) + 6) % 7))))
然而,这也表明由提供者来决定生成什么SQL。此外,提供者还可以决定它支持哪些语句,不支持哪些语句。
还表明使用AsEnumerable
停止SQL生成过程会影响查询的语义求值