我有一个方法,运行以下代码行。它应该获取在过去24小时内创建的所有消息,并且未来不会创建(因为这不可能)。
messages.AddRange(_dbcontext.Messages
.Where(message => message.CreationTime >= DateTime.UtcNow.AddHours(-24) && message.CreationTime <= DateTime.UtcNow)
.ToList());
当应用程序运行并通过上面的行时,这是运行的SQL查询:
SELECT [message].[Id], [message].[CreationTime], [message].[Value]
FROM [Messages] AS [message]
WHERE([message].[CreationTime] <= GETUTCDATE())
这基本上检索所有消息,而不是过去24小时内创建的消息。
我想知道为什么Where()的这一部分被忽略或者没有被转换为SQL查询,以及我可以做些什么来使它工作。
(message => message.CreationTime >= DateTime.UtcNow.AddHours(-24)
答案 0 :(得分:6)
这是先前2.0版EF核心版本(DateTime
,AddHours
等不支持的AddDays
方法)中SQL查询转换的问题,导致所谓的client evaluation 。您的查询应该产生正确的结果,但是效率很低,因为在检索到与其他条件匹配的所有记录之后,where
过滤器的第二部分将在内存中进行评估。
在EF Core 2.0中进行了改进,生成的SQL看起来像这样(如预期的那样):
SELECT [message].[Id], [message].[CreationTime], [message].[Value]
FROM [Messages] AS [message]
WHERE ([message].[CreationTime] >= DATEADD(hour, -24E0, GETUTCDATE())) AND ([message].[CreationTime] <= GETUTCDATE())
因此要么升级到EF Core 2.0,要么使用通常的EF解决方法,将DateTime.UtcNow.AddHours(-24)
置于查询之外的变量中并在其中使用。
答案 1 :(得分:2)
由于EF无法将DateTime
操作转换为正确的SQL查询,我建议如下:
DateTime dayBefore = DateTime.UtcNow.AddHours(-24);
messages.AddRange(_dbcontext.Messages
.Where(message => message.CreationTime >= dayBefore && message.CreationTime <= DateTime.UtcNow)
.ToList());
我发现这个解决方案使用EF date and time canonical functions显然有效,但我还没有测试过:
messages.AddRange(_dbcontext.Messages
.Where(message => message.CreationTime >= EntityFunctions.AddHours(DateTime.UtcNow, -24) && message.CreationTime <= DateTime.UtcNow)
.ToList());
希望这有帮助。
修改强>
EntityFunctions
已过时,已被DbFunctions取代,后面给出了以下示例。
messages.AddRange(_dbcontext.Messages
.Where(message => message.CreationTime >= DbFunctions.AddHours(DateTime.UtcNow, -24) && message.CreationTime <= DateTime.UtcNow)
.ToList());