Obtenga DateTime como UTC con Dapper

.net c# dapper orm utc

Pregunta

Estoy usando Dapper para mapear mis entidades a SQL Server CE. Si Kind=Utc un DateTime con Kind=Utc , cuando lo leo recibo un DateTime con Kind=Unspecified , lo que lleva a todo tipo de problemas.

Ejemplo:

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);

Este código da el siguiente resultado:

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

Sé que debería usar DateTimeOffset , pero desafortunadamente SQL CE no tiene soporte para este tipo.

¿Hay alguna solución? ¿Puedo decirle a Dapper que asuma que todas las fechas tienen DateTimeKind.Utc ? Y de manera más general, ¿cuáles son mis opciones para personalizar el mapeo?


EDITAR: Mi solución actual es parchar las fechas después de que Dapper haya materializado el resultado, pero huele ...

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;
}

Respuesta aceptada

Agregando esta respuesta para cualquier otra persona que venga buscando una solución simple. Esto es posible ahora con la adición de SqlMapper.TypeHandler en Dapper.

Agregue esta clase para convertir el valor de la base de datos a una fecha y hora con el tipo especificado como 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);
    }
}

Luego, en mi archivo Global.asax de mi API web, agrego el controlador de tipos a Dapper.

SqlMapper.AddTypeHandler(new DateTimeHandler());

Si necesita asegurarse de que siempre está insertando fechas como UTC, entonces en el método SetValue puede usar:

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

Respuesta popular

Miró el código Dapper. A menos que el mío estuviera desactualizado, para tipos de valor como datetime (que está mapeado a DbType.DateTime), dapper solo hace un lanzamiento simple del objeto IDataReader.

Pseudo: retorno de rendimiento (DateTime) IDataReader.GetValue (0);

Ese es el caso específico de Datetime de un montón de código genérico y lambdas.

AFAIK, SQL datetime nunca almacena el desplazamiento / zona horaria por lo que el tipo siempre dirá "No especificado" en cualquier fecha y hora que almacene y recupere.

Entonces, para hacerlo limpiamente , puedes tocar el interior de dapper:

lo cual es un problema ya que tendrías que tocar un gran método de generación de IL (DataRow Deserializer) y ponerlo en caso de DateTime.

O

simplemente ponga un setter en los accesorios de DateTime donde UTC es un problema (que es un poco contra POCO pero es relativamente cuerdo):

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué