使用 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 版本中可用。
我在带有 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 版本中可用。