转换数据库的 NHibernate 日期格式问题
NHibernate Date Format Issue for Converted Database
我们有一个应用程序使用 NHibernate 连接到 Oracle 数据库。我的任务是将 Oracle 数据库转换为 SQL 服务器数据库,以便在数据库之间切换就像更改 NHibernate 驱动程序一样简单。在我的 Oracle 数据库中,我有一个定义为 TIMESTAMP(6) WITH TIME ZONE 的列。此列已转换为 SQL 服务器 datetimeoffset(6) 数据类型。我的 NHibernate hbm.xml 文件中的映射如下所示:
<property name="checkoutDate" type="DateTime">
<column name="CHECKOUT_DATE" sql-type="TIMESTAMP(6) WITH TIME ZONE" not-null="false" />
</property>
通过此映射,我可以在 table 中插入一个日期,当我在 SSMS 中 运行 查询时,它会像这样显示:2018-05-24 10:48:17。 000000 +00:00。但是,当我尝试查询此 table 时,我得到两个异常:
FormatException: Input string '5/24/2018 10:48:17 +00:00' was not in the correct format.
和
InvalidCastException: Unable to cast object of type 'System.DateTimeOffset' to type 'System.IConvertible'.
有谁知道让 NHibernate 在不更改映射中的 sql 类型的情况下识别 datetimeoffset 列格式的方法吗?或者是否有一种 sql 类型可用于映射,同时适用于 Oracle 和 SQL 服务器列类型?
DateTimeType
NHibernate 类型期望从数据读取器接收 .Net DateTime
,但是 SqlClient 在读取 SQL 服务器 DateTimeOffset
。这样的 SQL 类型应该映射到 .Net DateTimeOffset
,使用 DateTimeOffset
NHibernate 类型。
(是的,您收到的错误消息有点误导,it is emitted 通过捕获 Convert.ToDateTime(rs[index])
的失败,其中 rs
是 DbDataReader
。在您的情况下,使用 SQL-Server,rs[index]
会产生一个 DateTimeOffset
,但转换失败。该消息确实不应提及 string
。)
由于这是与 Oracle 一起工作的,我想这意味着您的 Oracle 客户端在读取 timestamp with time zone
类型时会产生 DateTime
。这是非常不幸的,因为这意味着丢失偏移量,而且这也意味着 Oracle 不支持 DateTimeOffset
.Net 类型。 (好吧,Entity Framework Oracle 适配器似乎支持它,所以我想它仍然可以通过达到 Oracle 特定的 DbDataReader
实现来实现。但是仅仅使用 DbDataReader
公开的内容是不可行的.)
所以问题是 Oracle 和 Sql-Server 客户端之间关于时间 zone/offset 的类型不匹配,第一个产生 .Net DateTime
(顺便说一下,由于 Oracle 客户端不传输它,您的应用程序中的偏移量有何用途?),第二个产生 .Net DateTimeOffset
.
如果像您的示例一样,您将所有内容都存储在 UTC 中,也许您应该考虑在 SQL-服务器端将您的列声明为简单的 datetime2(6)
。
如果您需要在 Kind
正确设置为 Utc
的情况下检索它,则另外将其映射为 NHibernate 类型 UtcDateTime
.
否则,如, you need to use a custom user type (a class implementing NHibernate.UserTypes.IUserType
). His link is about a user type for handling the .Net DateTimeOffset
with Oracle. But in your case you would want instead to write a user type class with a NullSafeGet
method checking what the data-reader does yield and converting it to DateTime
if needed. Then in your mapping use it by supplying its assembly qualified name as the property type. (See a simple user type implementation here。)
我们有一个应用程序使用 NHibernate 连接到 Oracle 数据库。我的任务是将 Oracle 数据库转换为 SQL 服务器数据库,以便在数据库之间切换就像更改 NHibernate 驱动程序一样简单。在我的 Oracle 数据库中,我有一个定义为 TIMESTAMP(6) WITH TIME ZONE 的列。此列已转换为 SQL 服务器 datetimeoffset(6) 数据类型。我的 NHibernate hbm.xml 文件中的映射如下所示:
<property name="checkoutDate" type="DateTime">
<column name="CHECKOUT_DATE" sql-type="TIMESTAMP(6) WITH TIME ZONE" not-null="false" />
</property>
通过此映射,我可以在 table 中插入一个日期,当我在 SSMS 中 运行 查询时,它会像这样显示:2018-05-24 10:48:17。 000000 +00:00。但是,当我尝试查询此 table 时,我得到两个异常:
FormatException: Input string '5/24/2018 10:48:17 +00:00' was not in the correct format.
和
InvalidCastException: Unable to cast object of type 'System.DateTimeOffset' to type 'System.IConvertible'.
有谁知道让 NHibernate 在不更改映射中的 sql 类型的情况下识别 datetimeoffset 列格式的方法吗?或者是否有一种 sql 类型可用于映射,同时适用于 Oracle 和 SQL 服务器列类型?
DateTimeType
NHibernate 类型期望从数据读取器接收 .Net DateTime
,但是 SqlClient 在读取 SQL 服务器 DateTimeOffset
。这样的 SQL 类型应该映射到 .Net DateTimeOffset
,使用 DateTimeOffset
NHibernate 类型。
(是的,您收到的错误消息有点误导,it is emitted 通过捕获 Convert.ToDateTime(rs[index])
的失败,其中 rs
是 DbDataReader
。在您的情况下,使用 SQL-Server,rs[index]
会产生一个 DateTimeOffset
,但转换失败。该消息确实不应提及 string
。)
由于这是与 Oracle 一起工作的,我想这意味着您的 Oracle 客户端在读取 timestamp with time zone
类型时会产生 DateTime
。这是非常不幸的,因为这意味着丢失偏移量,而且这也意味着 Oracle 不支持 DateTimeOffset
.Net 类型。 (好吧,Entity Framework Oracle 适配器似乎支持它,所以我想它仍然可以通过达到 Oracle 特定的 DbDataReader
实现来实现。但是仅仅使用 DbDataReader
公开的内容是不可行的.)
所以问题是 Oracle 和 Sql-Server 客户端之间关于时间 zone/offset 的类型不匹配,第一个产生 .Net DateTime
(顺便说一下,由于 Oracle 客户端不传输它,您的应用程序中的偏移量有何用途?),第二个产生 .Net DateTimeOffset
.
如果像您的示例一样,您将所有内容都存储在 UTC 中,也许您应该考虑在 SQL-服务器端将您的列声明为简单的 datetime2(6)
。
如果您需要在 Kind
正确设置为 Utc
的情况下检索它,则另外将其映射为 NHibernate 类型 UtcDateTime
.
否则,如NHibernate.UserTypes.IUserType
). His link is about a user type for handling the .Net DateTimeOffset
with Oracle. But in your case you would want instead to write a user type class with a NullSafeGet
method checking what the data-reader does yield and converting it to DateTime
if needed. Then in your mapping use it by supplying its assembly qualified name as the property type. (See a simple user type implementation here。)