为什么这个Linq查询为Count()返回0?

时间:2013-02-27 14:55:02

标签: c# linq linq-to-sql

这似乎是一种不一致,但我可能只是遗漏了一些明显的东西。基本查询是:

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.我缺少什么?

2 个答案:

答案 0 :(得分:2)

我重新创建了这个测试,发现它可能与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而不是'星期二'。如果你在LINQ WHERE子句中用2替换星期二,你可以测试它,它实际上是可行的。这将在枚举之后起作用,因为结果将成功映射到一个可用的.net对象,其中DateTime.DayOfWeek转换为“Tuesday”将正常工作。

答案 1 :(得分:1)

您的计数是IQueryable<Event>,而e是枚举的实例。因此,某些操作可能无法正常工作,因为它们无法转换为SQL [编辑:或将在无意义的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.SqlFunctionsdoc here)类来提示linq提供程序在SQL中应该执行的操作。

修改

正如@Servy指出这是一个Linq to SQL问题。但是,问题很常见,所以我留下答案,不要删除它。

再次看OP,整个游戏可能会有另一个变量......延迟加载。

在foreach循环中,TimeStamp是延迟加载的。在计数查询中,提供程序尝试构造SQL查询,在此构造期间,它可能无法使用ToString(已知存在问题)并将e.TimeStamp.DayOfWeek.ToString()评估为不同于{{ 1}}。

"Tuesday"强制提供程序停止生成SQL,以便AsEnumerable()再次延迟加载。

确切地说明正在发生的事情的唯一方法是在数据库(例如SQL Server Profiler)上使用跟踪工具来实际查看在服务器上执行的查询(或查询)。

修改2

@Sinaesthetic's answer的基础上,返回0的原因是查询尝试将e.TimeStamp"4"进行比较,返回false,因此正确的结果为"Tuesday"。< / p>

可以通过执行

进行测试
false

反对DB。

但是,这也表明由提供者决定它生成什么SQL。此外,由提供者决定它支持哪些语句以及哪些语句不支持。

它还表明 使用select ((CONVERT(NVarChar,CONVERT(Int,(DATEPART(dw, getdate()) + (@@DATEFIRST) + 6) % 7)))) 来停止SQL生成过程可能会对查询的语义评估产生影响