不能将 NodaTime 的 Instant.InZone 与 Npgsql 的 Entity Framework 核心提供程序的变量中的 DateTimeZone 一起使用

Cannot use NodaTime's Instant.InZone with the DateTimeZone in a variable with Npgsql's Entity Framework Core provider

所以我 运行 在这里遇到了一个相当奇怪的问题。我有一个 PostgreSQL 14 服务器,我正在使用 Npgsql Entity Framework 核心提供程序来访问它。如果我有一个 Entity Framework 核心实体,其中包含日期作为 NodaTime Instant 类型(在数据库中变为 timestamp with time zone)并尝试通过 .InZone(timeZone).LocalDateTime 投影,我 运行分为3种情况。

实际示例查询如下所示:

await context.Entities.Select(e => e.Date.InZone(<timeZone>).LocalDateTime).ToListAsync();

(尽管似乎不包括 .LocalDateTime 部分与以下情况无关,但 .InZone(<timeZone>).InUtc() 替换时除外。)

第一种情况,如果我代替 <timeZone> 显式调用 DateTimeZoneProviders.Tzdb.GetSystemDefault(),则上述查询成功,没有问题。 (尽管奇怪的是,当我查看 EFCore 生成的 SQL 时,我没有看到像 Npgsql 文档提到的 AT TIME ZONE,但是如果我用 .InZone(<timeZone>) 调用替换我确实看到了它=20=].)

第二种情况,如果我在 <timeZone> 的位置放入对包含 DateTimeZoneProviders.Tzdb.GetSystemDefault() 结果的静态变量的引用,那么我会得到一个带有以下消息的 InvalidOperationException :

The client projection contains a reference to a constant expression of 'NodaTime.TimeZones.CachedDateTimeZone' which is being passed as an argument to the method 'InZone'. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.

鉴于上述情况,我 运行 进入第三种情况,如果代替 <timeZone> 我使用包含 DateTimeZoneProviders.Tzdb.GetSystemDefault() 或静态变量 I 的结果的局部变量上面提到的,然后我得到一个 InvalidOperationException 和以下消息:

Expression '@__timeZone_0' in the SQL tree does not have a type mapping assigned.

我是不是做错了什么?我的预期目标是能够从数据库的 timestamp with time zone 中获取 Instant 并在客户端将其设置为 ZonedDateTimeLocalDateTime,从数据库中获取数据后,无需在客户端执行额外的步骤。现在我必须使用 .InUtc().LocalDateTime 从数据库中获取日期,然后在列表具体化后,我才能对返回值进行转换。

NodaTime 插件不支持从 .NET 传递参数化时区;您只能使用常量表达式,例如 DateTimeZoneProviders.Tzdb["Europe/Berlin"](支持翻译的 see these docs)。你可以用它在PG中将一个PGtimestamp with time zone转换成一个timestamp without time zone,然后将结果检索为一个timestamp without time zone,它可以读作一个NodaTime LocalDateTime.

但是,如果您要寻找的是 ZonedDateTime/LocalDateTime 与客户端机器(.NET 正在执行的地方)的时区,那么做 .InUtc().LocalDateTime 是这样做 - 你有什么理由试图避免它?

多一点上下文(以及完整的代码示例)可以更清楚地说明您要实现的目标。