Obtenir DateTime comme UTC avec Dapper

.net c# dapper orm utc

Question

J'utilise Dapper pour mapper mes entités à SQL Server CE. Si je sauve un DateTime avec Kind=Utc , quand je le relis, je reçois un DateTime avec Kind=Unspecified , ce qui conduit à toutes sortes de problèmes.

Exemple:

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

Ce code donne la sortie suivante:

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

Je sais que je devrais utiliser un DateTimeOffset , mais malheureusement, SQL CE ne prend pas en charge ce type.

Y at-il un travail autour? Puis-je dire à Dapper que toutes les dates ont DateTimeKind.Utc ? Et plus généralement, quelles sont mes options pour personnaliser le mapping?


EDIT: La solution actuelle est de corriger les dates après que Dapper ait matérialisé le résultat, mais ça sent un peu ...

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

Réponse acceptée

Ajout de cette réponse à toute personne qui cherche une solution simple. Ceci est possible maintenant avec l'ajout de SqlMapper.TypeHandler dans Dapper.

Ajoutez cette classe pour convertir la valeur de la base de données en date-heure avec le type spécifié comme 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);
    }
}

Ensuite, dans mon fichier Global.asax de mon API Web, j'ajoute le gestionnaire de type à dapper.

SqlMapper.AddTypeHandler(new DateTimeHandler());

Si vous devez vous assurer que vous insérez toujours des dates au format UTC, vous pouvez utiliser la méthode SetValue:

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

Réponse populaire

Regardé dans le code Dapper. À moins que le mien ne soit obsolète, pour les types de valeur comme datetime (qui est mappé à DbType.DateTime), dapper ne fait qu'un simple cast à partir de l'objet IDataReader.

Pseudo: rendement de retour (DateTime) IDataReader.GetValue (0);

C'est le cas spécifique pour Datetime d'un tas de code générique et de lambdas.

De toute façon, SQL datetime ne stocke jamais le décalage / le fuseau horaire, de sorte que le type indiquera toujours "non spécifié" sur toutes les dates et heures que vous stockez et récupérez.

Donc, pour le faire proprement , vous pouvez toucher les internes dapper:

ce qui est pénible car vous devez toucher une grosse méthode de génération de IL (le DataRow Deserializer) et placer un cas pour DateTime.

OU

Il suffit de mettre un setter sur les accessoires DateTime où UTC est un problème (ce qui est un peu contre POCO mais est relativement sain):

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
}



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi