Подтвердить что ты не робот

Получить DateTime как UTC с Dapper

Я использую Dapper для сопоставления своих объектов с SQL Server CE. Если я сохраняю DateTime с Kind=Utc, когда я его прочитаю, я получаю DateTime с Kind=Unspecified, что приводит к возникновению всех проблем.

Пример:

var f = new Foo { Id = 42, ModificationDate = DateTime.UtcNow };
Console.WriteLine("{0} ({1})", f.ModificationDate, f.ModificationDate.Kind);
connection.Execute("insert into Foo(Id, ModificationDate) values(@Id, @ModificationDate)", f);
var f2 = connection.Query<Foo>("select * from Foo where Id = @Id", f).Single();
Console.WriteLine("{0} ({1})", f2.ModificationDate, f2.ModificationDate.Kind);

Этот код дает следующий результат:

20/09/2012 10:04:16 (Utc)
20/09/2012 10:04:16 (Unspecified)

Я знаю, что должен использовать DateTimeOffset, но, к сожалению, SQL CE не поддерживает этот тип.

Есть ли обходной путь? Могу ли я сказать Dapper, что все даты имеют DateTimeKind.Utc? И в общем, каковы мои настройки для настройки отображения?


EDIT: Моим текущим обходным путем является исправление дат после того, как Dapper материализовал результат, но он немного пахнет...

var results = _connection.Query<Foo>(sql, param).Select(PatchDate);

...

static Foo PatchDate(Foo f)
{
    if (f.ModificationDate.Kind == DateTimeKind.Unspecified)
        f.ModificationDate = DateTime.SpecifyKind(f.ModificationDate, DateTimeKind.Utc);
    return f;
}
4b9b3361

Ответ 1

Посмотрел код Dapper. Если мой учет не был устаревшим, для типов значений, таких как datetime (который отображается в DbType.DateTime), dapper просто выполняет простое преобразование из объекта IDataReader.

Pseudo: return return (DateTime) IDataReader.GetValue(0);

Это конкретный случай для Datetime из кучи общего кода и lambdas.

AFAIK, SQL datetime никогда не хранит смещение/часовой пояс, поэтому вид всегда будет указывать "Unspecified" в любом хранилище и выборке времени.

Итак, чтобы сделать это чисто, вы можете коснуться внутренних функций dapper:

который является болью, так как вам придется прикоснуться к большому методу генерации IL (Deserializer DataRow) и поставить случай if для DateTime.

ИЛИ

просто установите сеттер в репозитории DateTime, где UTC является проблемой (что не похоже на POCO, но относительно разумно):

class Foo
{
    private DateTime _modificationDate;
    public DateTime ModificationDate
    {
        get { return _modificationDate; }
        set { _modificationDate = DateTime.SpecifyKind(value, DateTimeKind.Utc); }
    }
    //Ifs optional? since it always going to be a UTC date, and any DB call will return unspecified anyways
}

Ответ 2

Добавление этого ответа для всех, кто приходит в поисках простого исправления. Это возможно теперь с добавлением SqlMapper.TypeHandler в Dapper.

Добавьте этот класс, чтобы преобразовать значение из db в datetime с типом, указанным как UTC.

public class DateTimeHandler : SqlMapper.TypeHandler<DateTime>
{
    public override void SetValue(IDbDataParameter parameter, DateTime value)
    {
        parameter.Value = value;
    }

    public override DateTime Parse(object value)
    {
        return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
    }
}

Затем в моем файле Global.asax моего веб-API я добавлю обработчик типа dapper.

SqlMapper.AddTypeHandler(new DateTimeHandler());

Если вам нужно убедиться, что вы всегда вставляете даты в формате UTC, то в методе SetValue вы можете использовать:

parameter.Value = DateTime.SpecifyKind(value, DateTimeKind.Utc);

Ответ 3

Если вы используете Dapper из источника (не nuget), вы можете настроить код, чтобы всегда принудительно устанавливать DateTimeKind из UTC. Более настраиваемый параметр может заключаться в создании нового атрибута для значений свойства DateTime, которые позволяют указать тип времени даты как подсказку для dapper. Dapper может искать свойства DateTime с этим атрибутом, и когда найденный может использовать его для указания типа DateTime во время сопоставления ORM. Это может быть приятной особенностью для core dapper, поскольку вы не единственная проблема с этой проблемой:)