entity frameworkDateTime和UTC

是否有可能有entity framework(我正在使用Code First方法与CTP5当前)在数据库中存储所有DateTime值为UTC?

或者有可能是在映射中指定它的方法,例如在last_login列中的一个:

modelBuilder.Entity<User>().Property(x => x.Id).HasColumnName("id"); modelBuilder.Entity<User>().Property(x => x.IsAdmin).HasColumnName("admin"); modelBuilder.Entity<User>().Property(x => x.IsEnabled).HasColumnName("enabled"); modelBuilder.Entity<User>().Property(x => x.PasswordHash).HasColumnName("password_hash"); modelBuilder.Entity<User>().Property(x => x.LastLogin).HasColumnName("last_login"); 

以下是您可以考虑的一种方法:

首先,定义以下属性:

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

现在把这个属性钩到你的EF上下文:

 public class MyContext : DbContext { public DbSet<Foo> Foos { get; set; } public MyContext() { ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += (sender, e) => DateTimeKindAttribute.Apply(e.Entity); } } 

现在在任何DateTimeDateTime? 属性,你可以应用这个属性:

 public class Foo { public int Id { get; set; } [DateTimeKind(DateTimeKind.Utc)] public DateTime Bar { get; set; } } 

有了这个,只要Entity Framework从数据库加载一个实体,它就会设置你指定的DateTimeKind ,比如UTC。

请注意,这在保存时不起任何作用。 在尝试保存之前,您仍然必须将该值正确转换为UTC。 但它确实允许您在检索时设置types,以便将其序列化为UTC,或者使用TimeZoneInfo将其转换为其他时区。

我真的很喜欢马特·约翰逊的方法,但在我的模型中,我所有的DateTime成员都是UTC,而且我不想用属性来装饰它们。 所以我推广马特的方法来允许事件处理程序应用一个默认的Kind值,除非一个成员是明确装饰的属性。

ApplicationDbContext类的构造函数包含以下代码:

 /// <summary> Constructor: Initializes a new ApplicationDbContext instance. </summary> public ApplicationDbContext() : base(MyApp.ConnectionString, throwIfV1Schema: false) { // Set the Kind property on DateTime variables retrieved from the database ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += (sender, e) => DateTimeKindAttribute.Apply(e.Entity, DateTimeKind.Utc); } 

DateTimeKindAttribute看起来像这样:

 /// <summary> Sets the DateTime.Kind value on DateTime and DateTime? members retrieved by Entity Framework. Sets Kind to DateTimeKind.Utc by default. </summary> [AttributeUsage(AttributeTargets.Property)] public class DateTimeKindAttribute : Attribute { /// <summary> The DateTime.Kind value to set into the returned value. </summary> public readonly DateTimeKind Kind; /// <summary> Specifies the DateTime.Kind value to set on the returned DateTime value. </summary> /// <param name="kind"> The DateTime.Kind value to set on the returned DateTime value. </param> public DateTimeKindAttribute(DateTimeKind kind) { Kind = kind; } /// <summary> Event handler to connect to the ObjectContext.ObjectMaterialized event. </summary> /// <param name="entity"> The entity (POCO class) being materialized. </param> /// <param name="defaultKind"> [Optional] The Kind property to set on all DateTime objects by default. </param> public static void Apply(object entity, DateTimeKind? defaultKind = null) { if (entity == null) return; // Get the PropertyInfos for all of the DateTime and DateTime? properties on the entity var properties = entity.GetType().GetProperties() .Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?)); // For each DateTime or DateTime? property on the entity... foreach (var propInfo in properties) { // Initialization var kind = defaultKind; // Get the kind value from the [DateTimekind] attribute if it's present var kindAttr = propInfo.GetCustomAttribute<DateTimeKindAttribute>(); if (kindAttr != null) kind = kindAttr.Kind; // Set the Kind property if (kind != null) { var dt = (propInfo.PropertyType == typeof(DateTime?)) ? (DateTime?)propInfo.GetValue(entity) : (DateTime)propInfo.GetValue(entity); if (dt != null) propInfo.SetValue(entity, DateTime.SpecifyKind(dt.Value, kind.Value)); } } } } 

如果在设置值时仔细地正确传递UTCdate,并且所有您关心的是在从数据库中检索实体时确保DateTimeKind设置正确,请在此处查看我的答案: https : //stackoverflow.com/一个/二十七万九千五百九十零分之九百三十八万六千三百六十四

我正在研究这个问题,其中大部分答案都不是很好。 从我所看到的情况来看,没有办法告诉EF6数据库中的date是UTC格式。 如果是这样的话,确保你的模型的DateTime属性是UTC的最简单的方法是validation并在setter中进行转换。

这里有一些C#像伪码描述algorithm

 public DateTime MyUtcDateTime { get { return _myUtcDateTime; } set { if(value.Kind == DateTimeKind.Utc) _myUtcDateTime = value; else if (value.Kind == DateTimeKind.Local) _myUtcDateTime = value.ToUniversalTime(); else _myUtcDateTime = DateTime.SpecifyKind(value, DateTimeKind.Utc); } } 

前两个分支是显而易见的。 最后一个秘密酱。

当EF6从数据库加载的数据创build模型时,DateTimes是DateTimeKind.Unspecified 。 如果你知道你的date在数据库中都是UTC的话,那么最后一个分支对你来说会很好用。

DateTime.Now总是DateTimeKind.Local ,所以上面的algorithm对代码中生成的date工作正常。 大多数时候。

但是,您必须谨慎,因为DateTimeKind.Unspecified可以通过其他方式潜入您的代码。 例如,您可能会从JSON数据中反序列化您的模型,而您的反序列化器的风格默认为这种types。 这是由你来防止本地化的date标记DateTimeKind.Unknown从任何人,而不是从EF到达该setter。

我相信我find了一个解决scheme,不需要任何自定义的UTC检查或DateTime操作。

基本上你需要改变你的EF实体使用DateTimeOffset(NOT DateTime)数据types。 这将在数​​据库中存储date值的时区(在我的情况下是SQL Server 2015)。

当EF Core从数据库请求数据时,它也会收到时区信息。 当你传递这个数据到一个web应用程序(Angular2在我的情况下),date会自动转换为浏览器的本地时区,这正是我所期望的。

当它传回我的服务器时,它也自动转换为UTC,也如预期。

没有办法在entity framework中指定DataTimeKind。 您可能决定在将date时间值转换为utc之前,将其存储到数据库中,并始终假定数据库中的数据为UTC。 但是在查询过程中使用的date时间对象将始终为“未指定”。 您也可以使用DateTimeOffset对象而不是DateTime。

对于那些需要像我一样使用.net framework 4来实现@MattJohnson解决scheme的人来说,reflection语法/方法的限制,需要修改一下,如下所示:

  foreach (var property in properties) { DateTimeKindAttribute attr = (DateTimeKindAttribute) Attribute.GetCustomAttribute(property, typeof(DateTimeKindAttribute)); if (attr == null) continue; var dt = property.PropertyType == typeof(DateTime?) ? (DateTime?)property.GetValue(entity,null) : (DateTime)property.GetValue(entity, null); if (dt == null) continue; //If the value is not null set the appropriate DateTimeKind; property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind) ,null); } 

另一种方法是创build与datetime属性的接口,在部分实体类上实现它们。 然后使用SavingChanges事件来检查对象是否是接口types,将这些date时间值设置为任何你想要的。 实际上,如果这些是在datetypes上创build/修改的,则可以使用该事件来填充它们。

在我的情况下,我只有一个与UTCdate时间表。 以下是我所做的:

 public partial class MyEntity { protected override void OnPropertyChanged(string property) { base.OnPropertyChanged(property); // ensure that values coming from database are set as UTC // watch out for property name changes! switch (property) { case "TransferDeadlineUTC": if (TransferDeadlineUTC.Kind == DateTimeKind.Unspecified) TransferDeadlineUTC = DateTime.SpecifyKind(TransferDeadlineUTC, DateTimeKind.Utc); break; case "ProcessingDeadlineUTC": if (ProcessingDeadlineUTC.Kind == DateTimeKind.Unspecified) ProcessingDeadlineUTC = DateTime.SpecifyKind(ProcessingDeadlineUTC, DateTimeKind.Utc); default: break; } } }