繁体   English   中英

LINQ to Entities使用日期时间的方式有所不同

[英]LINQ to Entities using datetime differently

抱歉,这个称呼可能很差; 我有一个问题比其他任何事情都要出于好奇。

我已经测试了用两种不同方式编写的同一LINQ to Entities语句,一种使用Datetime.Now,一种使用日期变量:

var timeNow = DateTime.Now;
var pendingMailshots = db.MailshotHistoryDatas.Where(m =>
                m.SendDate < timeNow).ToList();

var pendingMailshots = db.MailshotHistoryDatas.Where(m =>
                m.SendDate < DateTime.Now).ToList();

我发现他们发现的数据存在差异,经过一番挖掘和剖析,我发现了这一点:

exec sp_executesql N'SELECT 
[Extent1].[MailshotGuid] AS [MailshotGuid], 
[Extent1].[MailshotLineId] AS [MailshotLineId], 
[Extent1].[SendDate] AS [SendDate], 
[Extent1].[MessageType] AS [MessageType], 
[Extent1].[SendStatus] AS [SendStatus], 
[Extent1].[Recipients] AS [Recipients], 
[Extent1].[SendAttempts] AS [SendAttempts], 
[Extent1].[DateSent] AS [DateSent]
FROM  [dbo].[MailshotLineDatas] AS [Extent1]
INNER JOIN [dbo].[MailshotDatas] AS [Extent2] ON [Extent1].[MailshotGuid] = [Extent2].[MailshotGuid]
WHERE ([Extent1].[SendDate] < @p__linq__0),N'@p__linq__0 datetime2(7),@p__linq__0='2018-01-04 15:11:26.5618636'


SELECT 
[Extent1].[MailshotGuid] AS [MailshotGuid], 
[Extent1].[MailshotLineId] AS [MailshotLineId], 
[Extent1].[SendDate] AS [SendDate], 
[Extent1].[MessageType] AS [MessageType], 
[Extent1].[SendStatus] AS [SendStatus], 
[Extent1].[Recipients] AS [Recipients], 
[Extent1].[SendAttempts] AS [SendAttempts], 
[Extent1].[DateSent] AS [DateSent]
FROM  [dbo].[MailshotLineDatas] AS [Extent1]
INNER JOIN [dbo].[MailshotDatas] AS [Extent2] ON [Extent1].[MailshotGuid] = [Extent2].[MailshotGuid]
WHERE([Extent2].[StartDate] < (SysDateTime())))

使用datetime变量将查询作为存储过程并使用Datetime运行,现在将代码转换为TSQL。

谁能解释为什么? 另外,您认为更好的做法是什么?

在此先感谢亚当

如果您在过滤器表达式中传递DateTime.Now ,则提供程序知道如何处理它,并将其替换为对SYSDATETIME的调用,否则它别无选择,只能假设您要查找特定的日期。 这就是Expressions在C#中的工作方式( 请参阅此处

至于使用哪一个,那真的取决于您的用例。 调用SYSDATETIME将使用服务器上的时间,而后者将使用进行调用的计算机上的时间。 在实践中,这些可能仍然相同,或者至少仅稍有不同(考虑到网络延迟,时间漂移等)。

您正在定义一个表达式,稍后将执行它。

第一个LINQ表达式包含一个DateTime变量,该变量用某个值(DateTime.Now)初始化。 但是,在执行表达式时,该变量将不再具有当前日期的值。 因此,在实际执行表达式时使用参数。

在后一种情况下,您可以指定一个查询,其中filter子句必须使用当前日期/时间。 由于推迟执行该表达式,因此引擎不知道何时实际执行该表达式,因此它使用特定于数据库的函数来获取当前日期和时间。

对于这种情况,没有“什么是更好的做法”这样的东西。 这完全取决于您的用例。 此行为仅特定于LINQ查询的延迟执行:您实质上是在构建一个仅在调用ToList()/ ToArray / etc后才执行的表达式。

当您将代码重新编写为类似以下内容时:

var timeNow = DateTime.Now;
var query = db.MailshotHistoryDatas.Where(m =>
                m.SendDate < timeNow);

var pendingMailshots = query.ToList();

在上面的示例中,查询将仅在调用query.ToList();的代码行处执行query.ToList();
由于可以构建表达式并在程序中稍后执行它,因此LINQ必须使用带有参数的查询,以确保将正确的日期和时间传递给该查询,从而确保已定义了变量。

好吧,在第一种情况下,它知道当前的系统时间,因为您传递了DateTime.Now,因此LINQ试图在SQL中寻找等效项,即SysDateTime()。

在第二种情况下,您要传递值,SQL中没有等效项,这意味着LINQ将必须使其成为SP并传递值。

我强烈建议使用

var pendingMailshots = db.MailshotHistoryDatas.Where(m =>
            m.SendDate < DateTime.Now).ToList();

因为这样可以确保它与SQL执行时一样最新。 如果您不是真正在寻找系统精度,而是在寻找变量传递的值,请使用

var timeNow = DateTime.Now;
var pendingMailshots = db.MailshotHistoryDatas.Where(m =>
                m.SendDate < timeNow).ToList();

发生这种情况的原因是如何解析表达式树以将您的LINQ语句转换为SQL。 在使用变量存储DateTime的示例中,遍历的表达式是ParameterExpression,通过向查询添加参数将其转换为SQL。 在直接在LINQ查询中使用DateTime.Now的示例中,Expression的类型为MemberExpression,而Entity Framework的Expression Tree遍历器具有一个特殊的情况,即表示DateTime.Now的MemberExpression会将其转换为SysDateTime()的SQL。 。

如果您的意图始终是在查询中使用当前数据库时间,则可以在LINQ表达式中直接使用DateTime.Now。 如果要使用查询中在Application Server上捕获的特定时间,我将使用该变量。

该MSDN博客对此进行了全面的解释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM