Я использую NodaTime Instant
для хранения даты/времени в своих DTO с ServiceStack. Я указал тип SQL в DTO как datetimeoffset
, и ServiceStack правильно создает таблицу с этим типом. Однако при сохранении я получаю 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 в String.
Не знаю, почему он преобразуется в строку, когда тип базы данных 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