如果NodaTime使用Duration且Postgres使用INTERVAL / Period,如何在EF Core中进行时间戳算法?

问题描述

在我的.NET Core 3.1项目中,我目前正在将与时间相关的代码移至NodaTime。

我在翻译某些查询时遇到麻烦,因为NodaTime中两个Instant的差异产生了持续时间,而在Postgres中,两个TIMESTAMP的差异产生了一个INTERVAL。

Npgsql将一个Instant映射到TIMESTAMP,但是一个INTERVAL是一个时期。

假设我有一个名为“ Visit”的实体,该实体具有一个ID,并且两次:“到达”和“离开”。 (这不是我的实际用例,我有几个存在该问题的实体,因此这是一个最小的示例)

public class Visit
{
    public int Id { get; set; }
    public Instant Arrived { get; set; }
    public Instant Left { get; set; }
}

现在,我想查找访问者停留的时间少于给定时间的访问:

public async Task<List<Visit>> FindVisitsShorterThan(DbSet<Visit> dbSet,Duration duration)
{
    var visits =
        from visit in dbSet
        where (visit.Left - visit.Arrived) < duration
        select visit;
    return await visits.ToListAsync();
}

如果这样做,Npgsql会抱怨System.InvalidCastException: Can't write CLR type NodaTime.Duration with handler type TimestampHandler之类的-因为不支持持续时间。

另一方面,如果我将“持续时间”转换为“期间”,则NodaTime会抱怨,因为从它的角度来看,这种差异是“持续时间”,并且不能与

我可以做几件事,但是似乎都不是理想的事情

  • 更改实体,并将“ Instant Left”替换为“ Period Stay”,这样我就不必在查询中进行该计算。但这仅适用于一个特定查询-如果我需要“左”查询另一个查询怎么办?
  • 仅使用LocalDateTime代替Instant。区别是一个周期,因此可以在NodaTime和Postgres中使用。但是那样一来,我就改变了实体的语义-由于某种原因,我希望那些时间成为UTC时间戳。 LocalDateTime本质上意味着“ TZ未知或存储在其他地方”,这根本不是真的。
  • 恢复为BCL类型,然后重新开始与时区进行战斗。
  • 使用Instants将我的实体拆分为域类型,使用LocalTime将我的实体拆分为特定于Npgsql的DTO类型,并在它们之间进行一些映射。可能是最好的解决方案,但似乎没有必要。

有更好的方法吗?

解决方法

这确实是当前Npgsql EF对NodaTime类型的支持的不幸限制。

好消息是,我刚刚实现了上述内容以及其他一些NodaTime算术翻译-see this tracking issue。这将包含在下周即将发布的EF Core 5.0中-只需等待几天,一切都会很好。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...