Voici une approche que vous pourriez envisager:
Tout d'abord, définissez cet attribut suivant:
[AttributeUsage(AttributeTargets.Property)]
public class DateTimeKindAttribute : Attribute
{
private readonly DateTimeKind _kind;
public DateTimeKindAttribute(DateTimeKind kind)
{
_kind = kind;
}
public DateTimeKind Kind
{
get { return _kind; }
}
public static void Apply(object entity)
{
if (entity == null)
return;
var properties = entity.GetType().GetProperties()
.Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?));
foreach (var property in properties)
{
var attr = property.GetCustomAttribute<DateTimeKindAttribute>();
if (attr == null)
continue;
var dt = property.PropertyType == typeof(DateTime?)
? (DateTime?) property.GetValue(entity)
: (DateTime) property.GetValue(entity);
if (dt == null)
continue;
property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind));
}
}
}
Maintenant, connectez cet attribut à votre contexte EF:
public class MyContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public MyContext()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
(sender, e) => DateTimeKindAttribute.Apply(e.Entity);
}
}
Maintenant, sur n'importe quelle propriété DateTime
ou DateTime?
, vous pouvez appliquer cet attribut:
public class Foo
{
public int Id { get; set; }
[DateTimeKind(DateTimeKind.Utc)]
public DateTime Bar { get; set; }
}
Avec cela en place, chaque fois qu'Entity Framework charge une entité à partir de la base de données, il définit le DateTimeKind
que vous spécifiez, tel que UTC.
Notez que cela ne fait rien lors de l'enregistrement. Vous devrez toujours avoir la valeur correctement convertie en UTC avant d'essayer de l'enregistrer. Mais il vous permet de définir le genre lors de la récupération, ce qui lui permet d'être sérialisé au format UTC ou de le convertir dans d'autres fuseaux horaires avec TimeZoneInfo
.
Matt Johnson-Pint
la source
'System.Array' does not contain a definition for 'Where'
J'aime vraiment l'approche de Matt Johnson, mais dans mon modèle, TOUS mes membres DateTime sont UTC et je ne veux pas avoir à les décorer tous avec un attribut. J'ai donc généralisé l'approche de Matt pour permettre au gestionnaire d'événements d'appliquer une valeur Kind par défaut à moins qu'un membre ne soit explicitement décoré avec l'attribut.
Le constructeur de la classe ApplicationDbContext inclut ce code:
DateTimeKindAttribute
ressemble à ça:la source
DateTIme
attribut sans méthode de définition (publique). Modification suggérée. Voir aussi stackoverflow.com/a/3762475/2279059La réponse acceptée ne fonctionne pas pour l'objet projeté ou anonyme. Les performances peuvent également être un problème.
Pour y parvenir, nous devons utiliser un
DbCommandInterceptor
, un objet fourni par EntityFramework.Créer un intercepteur:
interceptionContext.Result
est DbDataReader, que nous remplaçons par le nôtreEnregistrez l'intercepteur dans votre
DbConfiguration
Enfin, enregistrez la configuration sur votre
DbContext
C'est tout. À votre santé.
Pour plus de simplicité, voici l'intégralité de l'implémentation de DbReader:
la source
Dispose
etGetData
?Je crois avoir trouvé une solution qui ne nécessite aucune vérification UTC personnalisée ou manipulation de DateTime.
Fondamentalement, vous devez modifier vos entités EF pour utiliser le type de données DateTimeOffset (PAS DateTime). Cela stockera le fuseau horaire avec la valeur de date dans la base de données (SQL Server 2015 dans mon cas).
Lorsque EF Core demande les données à la base de données, il recevra également les informations de fuseau horaire. Lorsque vous transmettez ces données à une application Web (Angular2 dans mon cas), la date est automatiquement convertie dans le fuseau horaire local du navigateur, ce que j'attends.
Et quand il est renvoyé à mon serveur, il est à nouveau converti en UTC automatiquement, également comme prévu.
la source
Il n'existe aucun moyen de spécifier le DataTimeKind dans Entity Framework. Vous pouvez décider de convertir les valeurs de date et d'heure en utc avant de les stocker en db et de toujours supposer que les données récupérées de db sont UTC. Mais les objets DateTime matérialisés lors de la requête seront toujours "non spécifiés". Vous pouvez également évaluer à l'aide de l'objet DateTimeOffset au lieu de DateTime.
la source
Je fais des recherches là-dessus en ce moment, et la plupart de ces réponses ne sont pas vraiment excellentes. D'après ce que je peux voir, il n'y a aucun moyen de dire à EF6 que les dates sortant de la base de données sont au format UTC. Si tel est le cas, le moyen le plus simple de s'assurer que les propriétés DateTime de votre modèle sont en UTC serait de vérifier et de convertir dans le setter.
Voici un pseudocode de type c # qui décrit l'algorithme
Les deux premières branches sont évidentes. Le dernier détient la sauce secrète.
Lorsque EF6 crée un modèle à partir de données chargées à partir de la base de données, les DateTimes sont
DateTimeKind.Unspecified
. Si vous savez que vos dates sont toutes UTC dans la base de données, alors la dernière branche fonctionnera très bien pour vous.DateTime.Now
est toujoursDateTimeKind.Local
, donc l'algorithme ci-dessus fonctionne bien pour les dates générées dans le code. Le plus souvent.Vous devez cependant être prudent, car il existe d'autres moyens de
DateTimeKind.Unspecified
se faufiler dans votre code. Par exemple, vous pouvez désérialiser vos modèles à partir de données JSON, et votre saveur de désérialiseur est par défaut de ce type. C'est à vous de vous prémunir contre les dates localisées marquéesDateTimeKind.Unspecified
pour arriver à ce passeur par quelqu'un d'autre que EF.la source
DateTimeKind.Utc
fois vos résultats générés. Exemple:from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };
définit tous Kind surDateTimeKind.Unspecified
.Pour EF Core , il y a une excellente discussion sur ce sujet sur GitHub: https://github.com/dotnet/efcore/issues/4711
Une solution (crédit à Christopher Haws ) qui se traduira par le traitement de toutes les dates lors de leur stockage / récupération de la base de données comme UTC consiste à ajouter ce qui suit à la
OnModelCreating
méthode de votreDbContext
classe:En outre, vérifiez ce lien si vous voulez exclure certaines propriétés de certaines entités d'être traitées comme UTC.
la source
IsQueryType
semble avoir été remplacé parIsKeyLess
: github.com/dotnet/efcore/commit/…Si vous prenez soin de passer correctement les dates UTC lorsque vous définissez les valeurs et que tout ce qui vous importe est de vous assurer que DateTimeKind est correctement défini lorsque les entités sont extraites de la base de données, voir ma réponse ici: https://stackoverflow.com/ a / 9386364/279590
la source
Une autre année, une autre solution! C'est pour EF Core.
J'ai beaucoup de
DATETIME2(7)
colonnes qui correspondent àDateTime
et stockent toujours UTC. Je ne veux pas stocker de décalage car si mon code est correct, le décalage sera toujours nul.Pendant ce temps, j'ai d'autres colonnes qui stockent des valeurs de base de date-heure de décalage inconnu (fournies par les utilisateurs), donc elles sont simplement stockées / affichées "telles quelles", et non comparées à quoi que ce soit.
Par conséquent, j'ai besoin d'une solution que je puisse appliquer à des colonnes spécifiques.
Définissez une méthode d'extension
UsesUtc
:Cela peut ensuite être utilisé sur les propriétés dans la configuration du modèle:
Il a le petit avantage par rapport aux attributs que vous ne pouvez l'appliquer qu'aux propriétés du type correct.
Notez qu'il suppose que les valeurs de la base de données sont en UTC, mais qu'elles ont juste le mauvais
Kind
. Par conséquent, il contrôle les valeurs que vous essayez de stocker dans la base de données, lançant une exception descriptive si elles ne sont pas UTC.la source
Pour ceux qui ont besoin de réaliser la solution @MattJohnson avec .net framework 4 comme moi, avec une limitation de la syntaxe / méthode de réflexion, cela nécessite une petite modification comme indiqué ci-dessous:
la source
La solution de Matt Johnson-Pint fonctionne, mais si tous vos DateTimes sont censés être UTC, la création d'un attribut serait trop détournée. Voici comment je l'ai simplifié:
la source
Une autre approche serait de créer une interface avec les propriétés datetime, de les implémenter sur les classes d'entités partielles. Et puis utilisez l'événement SavingChanges pour vérifier si l'objet est du type d'interface, définissez ces valeurs datetime sur ce que vous voulez. En fait, si ceux-ci sont créés / modifiés à des dates, vous pouvez utiliser cet événement pour les remplir.
la source
Dans mon cas, je n'avais qu'une seule table avec des dates-heures UTC. Voici ce que j'ai fait:
la source