使用OffsetDateTime与NHibernate

3
我正在尝试将NodaTime的OffsetDateTime类型映射到SQL Server,但我不确定如何解决NodaTime的OffsetDateTime和SQL Server的DateTimeOffset之间的阻抗匹配问题。
我遇到的主要问题是让LINQ支持正确工作,因为OffsetDateTime没有诸如< 的比较运算符。 代码>。这两个系统在处理相等性方面也有所不同。NodaTime考虑时间瞬间和偏移量,而SQL Server仅考虑时间瞬间。
SQL Server中认为2015-12-24 11:18 + 01:00和2015-12-24 10:18 + 00:00相等,但在NodaTime中它们不相等。
我考虑使用ICompositeUserType将UTC日期/时间和偏移量存储在单独的列中(类似于SQL Server 2008之前),但OffsetDateTime没有UTC / Instant属性。 因此,我无法看到如何将date.ToInstant()在LINQ查询中正确映射到ICompositeUserType的属性中。

首先,你想做什么?如果你把这两个值都转换成NodaTime instants,它们就会相等 - 它们确实指的是同一个瞬间。你真的希望它们表现为不同的值吗? - Panagiotis Kanavos
其次,NHibernat将您的类映射数据库。您不能使用数据库类型不支持的语义。LINQ或任何ORM都不会改变数据库的功能,它们只是使对象和表之间的映射更容易。任何其他用途都是一个严重的问题。 - Panagiotis Kanavos
我想在我的模型中使用NodaTime的OffsetDateTime类型,并将其映射到SQL Server的DateTimeOffset或2个列,一个存储UTC值的DateTime和一个偏移量。 - Chris Chilvers
1个回答

7

OffsetDateTime 应该映射到 SQL Server 的 datetimeoffset。NHibernate 通过 IUserType 支持自定义类型映射,如 本文所述,这对实现此功能至关重要。

虽然 OffsetDateTime 没有直接实现 IComparable,但可以使用 OffsetDateTime.Comparer.Instant 进行比较。这在 LINQ 查询中可能仍然很难使用,但这是一个值得探索的方法。

可能需要编写一个 NHibernate-NodaTime 集成包,以使此过程更简单。考虑到我之前已经为 RavenDBDapper 进行了此操作,我会考虑这个建议。 :)

抱歉,我没有更好的“执行此操作”的答案。


更新

我开始着手编写此内容,并成功构建了一个针对OffsetDateTimeIUserType,但它无法使用比较运算符 - 正如您所描述的那样。 我认为解决方案涉及扩展Linq提供程序,类似于此博客文章中所述的技术。 我还没有完整的工作示例,但我会在此处更新。

最终,您将无法这样编写:

session.Query<Foo>().Where(x => x.SomeODT > value)

因为OffsetDateTime没有比较运算符,所以它无法编译。
相反,LINQ提供程序应该扩展支持类似这样的功能:
session.Query<Foo>().Where(x => OffsetDateTime.Comparer.Instant.Compare(x.SomeODT, value) > 0)

或者更干净的写法:

session.Query<Foo>().Where(x => x.SomeODT.ToInstant() > value.ToInstant())

或者两者都可以。两者都能编译,但如果在LINQ提供程序中没有适当的支持,将会抛出异常。


第二次更新

有人比我更快。现在有一组NHibernate扩展支持Noda Time数据类型在这个项目中。 :)


@JonSkeet(如果您正在阅读此内容),请注意在OffsetDateTime中没有默认比较器会导致像这种情况下的问题。另外,请注意,在此处通过本地时间进行比较是不可能实现的,因为最终LINQ将被转换为SQL语句,并且SQL 始终通过其UTC等效项比较datetimeoffset类型。 - Matt Johnson-Pint
好的,我在阅读 :) 那么你认为 OffsetDateTime 是否应该有一个默认比较方式,就像 SQL 一样是按照时间来进行比较吗?请注意,它不会与等式相容,因为等式会同时比较时区和时间...那 SQL 是如何处理这个问题的呢? - Jon Skeet
我使用自定义的LinqToHqlGeneratorsRegistry和处理ToInstant方法,得到了一个可工作的该概念的实现。在我的实现中,我使用了一个复合用户类型,因为这是一个遗留的数据库,它有单独的UTC日期/时间和偏移列。 - Chris Chilvers
@JonSkeet MSSQL按照时间的瞬间(即忽略偏移量)对DateTimeOffset进行排序,并且如果两个DateTimeOffsets是相同的瞬间,则认为它们是相等的。拥有一个比较器可以使得使用Linq和MSSQL更简单,因为你可以使用query.OrderBy(x => x.CreateAt)。唯一我不确定的是多少代码假设当CompareTo == 0Equals == true - Chris Chilvers
@ChrisChilvers:正如Matt所示,我们已经有OffsetDateTime.Comparer.Instant了;你可以使用query.OrderBy(x => x.CreateAt, OffsetDateTime.Comparer.Instant)。如果你是指实现IComparable<T>...我需要再考虑一下。我更喜欢Equals仍然考虑偏移量,但我可以理解自然排序只能按瞬间进行。就像你说的,让相等和排序做不同的事情有点奇怪。 - Jon Skeet
我曾经考虑过这个问题,但是如果你使用了 query.OrderBy(x => x.CreateAt, OffsetDateTime.Comparer.Local),那么就会出现一个问题。我无法确定使用了哪个比较器,而且大多数的 Linq 提供程序都不支持这种形式的 OrderBy - Chris Chilvers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接