使用 ServiceStack / OrmLite 在 SQL 服务器中保留 Nodatime Instant

Persisting Nodatime Instant in SQL Server with ServiceStack / OrmLite

我在带有 ServiceStack 的 DTO 中使用 NodaTime Instant date/time 存储。我已将 DTO 中的 SQL 类型指定为 datetimeoffset,并且 ServiceStack 使用该类型正确创建了 table。但是,保存后,我得到一个 InvalidCastException.

简单示例:

public class ItemWithInstant
{
    public int Id { get; set; }
    public string Name { get; set; }
    [CustomField("DateTimeOffset")
    public Instant DateCreated { get; set; }
}

服务中:

public object Post(CreateItemWithInstant request)
{
    var dto = request.ConvertTo<ItemWithInstant>();
    Db.Save(dto); // ERROR here
    return dto;
}

具体错误是 InvalidCastException,详细信息为 无法将参数值从 Instant 转换为字符串。

不知道为什么当数据库类型为 DateTimeOffset 时它会转换为字符串。我是否需要告诉 ServiceStack 如何将值转换为适用于 SQL 类型的值?

更新

感谢@mythz 的回答,我创建了一个自定义转换器。我最后还去了 DATETIME2 以获得 SQL 数据类型(不要认为这有什么区别):

public class SqlServerInstantToDatetimeConverter : OrmLiteConverter
{
    public override string ColumnDefinition { get { return "DATETIME2"; } }

    public override DbType DbType { get { return DbType.DateTimeOffset; } }

    public override object ToDbValue(Type fieldType, object value)
    {
        var instantValue = (Instant) value;
        return instantValue.ToDateTimeUtc();
    }

    public override object FromDbValue(Type fieldType, object value)
    {
        var datetimeValue = DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
        return Instant.FromDateTimeUtc(datetimeValue);
    }
}

然后我在我的 AppHost.cs 文件中注册了它:

Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(Settings.Default.LocalSqlConnectionString, SqlServerDialect.Provider));
SqlServerDialect.Provider.RegisterConverter<Instant>(new SqlServerInstantConverter());

不要忘记 FromDbType 覆盖。我最初忘记了它并且该字段没有被输出。

另一个警告——由于 ServiceStack 想要本地化所有日期,我不得不使用此答案中的信息将所有日期强制为 DateTimeKind.Local

[CustomField] 只是告诉 OrmLite 在创建 table 时使用什么列定义,即它没有提供任何关于 OrmLite 应该如何处理未知类型的指示。

您可以通过注册 Custom Type Converter 来支持新的字段类型,该 Custom Type Converter 现在在最新的 v4.0.44 版本中可用。