EF Core 仅查询 DateTimeOffset 的 DateTime 无法翻译;

Posted

技术标签:

【中文标题】EF Core 仅查询 DateTimeOffset 的 DateTime 无法翻译;【英文标题】:EF Core query only DateTime of DateTimeOffset cannot be translated; 【发布时间】:2021-12-13 00:36:29 【问题描述】:

我遇到了一个问题,我想仅基于 DateTimeOffset SQL Server 列的 DateTime 进行查询,我想知道是否可以使用 EF Core。

如果我在世界各地都有约会,并且我的业务逻辑将它们准确地记录在他们的本地 DateTimeOffset 中,我希望能够获得特定日期的约会,而不管时区如何,但是,我得到一个例外,即当我尝试以下操作时无法翻译查询:

public class Appointment

   public int Id get;set;
   public DateTimeOffset BeginTime get;set;


DateTime queryDay = new DateTime(2021, 1, 1);

var results = dbContext.Appointments.Where(a => a.BeginTime.DateTime >= queryDate && a.BeginTime.DateTime < queryDay.AddDays(1)).ToList();

有没有办法用 EF Core 做到这一点?我的意思是,在示例中,我只是想获得一个特定的日期,但实际上,我希望能够在任何日期时间使用任何时间值等来做到这一点。

换句话说,我不希望按通用时间范围进行过滤,而是按不考虑偏移量的时间范围进行过滤。

即使我创建了一个返回 BeginTime.DateTime 的 [NotMapped] 属性,它似乎也不起作用。

编辑:

确切的错误:

System.InvalidOperationException:LINQ 表达式 'DbSet() .Where(t => True && t.BeginTime.DateTime >= __fakeStartDate_1 && t.BeginTime.DateTime https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。

【问题讨论】:

确切的错误是什么? 您是否尝试过预先计算 queryDay.AddDays(1) 并将其放入另一个变量(queryDayPlusOne)?这应该解决它,恕我直言。 为什么不对 queryDay 变量使用 DateTimeOffset?您可能必须创建类型为 DateTimeOffset 的 queryDayFrom 和 queryDayTo 变量。 @ErmiyaEskandary 添加了异常 @TomTom 没有解决它。这不是问题。我应该删除 .AddDays 以避免混淆 【参考方案1】:

SqlServer 提供程序支持所谓的“双重转换”构造(首先到 object,然后到其他类型,例如 (DateTime)(object)),它欺骗 C# 编译器接受通常会失败的转换(如果在LINQ to Objects 上下文)和 EF Core 转换器,强制后者发出从 datetimeoffsetdatetime2 using CASToperator 的转换。

例如

var query = dbContext.Appointments
    .Where(a => ((DateTime)(object)a.BeginTime) >= queryDate 
        && ((DateTime)(object)a.BeginTime) < queryDate.AddDays(1));

成功转换为

DECLARE @__queryDate_0 datetime2 = '2021-01-01T00:00:00.0000000';
DECLARE @__AddDays_1 datetime2 = '2021-01-02T00:00:00.0000000';

SELECT [a].[Id], [a].[BeginTime]
FROM [Appointments] AS [a]
WHERE (CAST([a].[BeginTime] AS datetime2) >= @__queryDate_0) AND (CAST([a].[BeginTime] AS datetime2) < @__AddDays_1)

【讨论】:

如果列中存在索引,则列转换将排除查找,从而导致全扫描。 哇。非常感谢。它几乎是完美的,我可能会使用它,但是,如果像 @DanGuzman 说它会忽略索引,这将是一个性能问题,因为我在列上有索引。 先生,您能帮我解决这个问题吗:***.com/questions/69745866/… 当然索引会被忽略。但这与 OP 问题无关“EF Core 仅查询 DateTimeOffset 的 DateTime 无法翻译”。您想要 DateTimeOffset.DateTime 属性的 SQL 翻译,并提供了答案(好像 EF Core 自然支持)。您定义它的方式,当前问题对我来说已关闭。如果您想要一种在数据库中寻找此类列的有效方法,那是完全不同的问题,因此需要创建新的 SO 问题。 @DanGuzman 您的评论是正确的,但与当前问题无关。【参考方案2】:

有一种我不太喜欢的方法,但它不能避免列上的索引,它应该可以工作。应该是这样的:

public IEnumerable<Appointments> GetAppointments(DateTime start, DateTime end)

  var bufferStart = start.AddDays(-1);
  var bufferEnd = end.AddDays(1);

  return dbContext.Appointments
   .Where(a => a.BeginTime >= bufferStart && a.BeginTime < bufferEnd)
   .AsEnumerable()
   .Where(a => a.BeginTime.DateTime >= start && a.BeginTime.DateTime < end);


显然,我不喜欢这个有很多原因,所以仍在寻找更好的答案。如果有人有的话将不胜感激。

【讨论】:

以上是关于EF Core 仅查询 DateTimeOffset 的 DateTime 无法翻译;的主要内容,如果未能解决你的问题,请参考以下文章

.net ef core 检查是不是插入成功

.net ef core 检查是不是插入成功

如何在 EF Core 中仅包含相关表的最新记录

添加迁移时,IdentityServer DB 上的 EF Core 迁移适用于 MULTIPLE 表而不是仅 AspNetUsers 表

如何优化查询 - Ef core

EF Core 5.0 中的多对多关系是不是可以配置为仅保留一个导航属性(在一侧)?