2

这似乎是一个不一致的地方,但我可能只是错过了一些明显的东西。基本查询是:

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。我错过了什么?

4

2 回答 2

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 子句中将 Tuesday 替换为 2,则可以对此进行测试,它实际上会起作用。这将在枚举后起作用,因为结果将成功映射到可用的 .net 对象中,其中 DateTime.DayOfWeek 转换为“星期二”将正常工作。

于 2013-02-27T20:53:21.610 回答
1

您的计数作用于一段IQueryable<Event>时间e是一个枚举实例。因此,某些操作可能无法工作,因为它们无法翻译成 SQL [编辑:或将翻译成无意义的 SQL]。

为确保您的Where条款有效,请AsEnumerable()在它之前添加一个。这会将 转换IQueryable<Event>为 anIEnumerable<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( doc here ) 类来提示 linq 提供程序它应该在 SQL 中做什么。

编辑

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

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

在 foreach 循环中,TimeStamp 是延迟加载的。在计数查询中,提供者尝试构建一个 SQL 查询,在此构建过程中它可能无法使用ToString(已知这是有问题的)并评估e.TimeStamp.DayOfWeek.ToString()为不同于"Tuesday".

AsEnumerable()强制提供程序停止生成 SQL,因此再次e.TimeStamp延迟加载。

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

编辑 2

基于@Sinaesthetic 的回答,返回 0 的原因是 Query 尝试比较"4"返回"Tuesday"false 的结果,因此正确的结果是false.

可以通过执行来测试

select ((CONVERT(NVarChar,CONVERT(Int,(DATEPART(dw, getdate()) + (@@DATEFIRST) + 6) % 7))))

针对数据库。

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

它还表明,使用AsEnumerable停止 SQL 生成过程会对查询的语义评估产生影响

于 2013-02-27T15:11:50.977 回答