如何指定从 EntityFramework 检索的 DateTime 对象应该是 DateTimeKind.UTC [重复]

Posted

技术标签:

【中文标题】如何指定从 EntityFramework 检索的 DateTime 对象应该是 DateTimeKind.UTC [重复]【英文标题】:How to specify that DateTime objects retrieved from EntityFramework should be DateTimeKind.UTC [duplicate] 【发布时间】:2015-06-12 07:52:48 【问题描述】:

我有 C# 程序,其中所有 DateTime 对象都是 DateTimeKind.UTC。将对象保存到数据库时,它按预期存储 UTC。但是,在检索它们时,它们是DateTimeKind.Unspecified。在 C# 中创建 DateTime 对象时,有没有办法告诉实体框架(代码优先)始终使用 DateTimeKind.UTC

【问题讨论】:

请显示您将数据从 db 获取到 C# 的代码行 【参考方案1】:

不,没有。它实际上是DateTimeKind.Unspecified

但是,如果您担心支持多个时区,则应考虑使用DateTimeOffset。它就像一个常规的 DateTime,只是它不代表时间的“透视”,它代表一个绝对视图,其中 3PM (UTC - 3) 等于 4PM (UTC - 2)。 DateTimeOffset 包含 DateTime 和时区,EntityFramework 和 SQL Server 都支持。

【讨论】:

但是这个呢...social.msdn.microsoft.com/Forums/en-US/… ? 他们正在创建另一个非持久性属性,用于将 DateTime 转换为 UTC 或 Local。不完全是 OP 想要的。 DateTimeOffset 包含时区:它包含一个 UTC 偏移量,用于创建值的时刻。它包含有关实际值的更多信息,但几乎没有比裸露的 UTC DateTime 值有用。如果您想要一个相对于时区的值,则必须将该时区存储在某处并转换该值,就像您对 DateTime 所做的那样。 我似乎无法在实体框架中获取 datetimeoffset 的 datetime 组件。没有办法将其转换为 datetime 以获得值的“本地”部分。在 SQL 中,它只是“cast(value as datetime)”,但在 EF 中似乎没有这样的等价物。 内存中的 SQLLite 不支持 DateTimeOffset 查询 :-( docs.microsoft.com/en-us/ef/core/providers/sqlite/…【参考方案2】:

您可以让您的数据上下文修复所有相关值。以下使用实体类型的属性缓存来执行此操作,以避免每次都检查类型:

public class YourContext : DbContext

  private static readonly List<PropertyInfo> EmptyPropsList = new List<PropertyInfo>();
  private static readonly Hashtable PropsCache = new Hashtable(); // Spec promises safe for single-reader, multiple writer.
                                                                  // Spec for Dictionary makes no such promise, and while
                                                                  // it should be okay in this case, play it safe.
  private static List<PropertyInfo> GetDateProperties(Type type)
  
    List<PropertyInfo> list = new List<PropertyInfo>();
    foreach(PropertyInfo prop in type.GetProperties())
    
      Type valType = prop.PropertyType;
      if(valType == typeof(DateTime) || valType == typeof(DateTime?))
        list.Add(prop);
    
    if(list.Count == 0)
      return EmptyPropsList; // Don't waste memory on lots of empty lists.
    list.TrimExcess();
    return list;
  
  private static void FixDates(object sender, ObjectMaterializedEventArgs evArg)
  
    object entity = evArg.Entity;
    if(entity != null)
    
      Type eType = entity.GetType();
      List<PropertyInfo> rules = (List<PropertyInfo>)PropsCache[eType];
      if(rules == null)
        lock(PropsCache)
          PropsCache[eType] = rules = GetPropertyRules(eType); // Don't bother double-checking. Over-write is safe.
      foreach(var rule in rules)
      
        var info = rule.PropertyInfo;
        object curVal = info.GetValue(entity);
        if(curVal != null)
          info.SetValue(entity, DateTime.SpecifyKind((DateTime)curVal, rule.Kind));
      
    
  
  public YourContext()
  
    ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += FixDates;
    /* rest of constructor logic here */
  
  /* rest of context class here */

这也可以与属性结合使用,从而允许设置每个属性应具有的DateTimeKind,方法是存储一组关于每个属性的规则,而不仅仅是PropertyInfo,并在GetDateProperties.

【讨论】:

看起来不错! GetPropertyRules GetDateProperties 虽然不匹配 拥有private static readonly List&lt;PropertyInfo&gt; EmptyPropsList = new List&lt;PropertyInfo&gt;(0);不是更好吗? @Artyom 讽刺的是,在我写这篇文章的时候,我认为显式 0 更好,而上面只是错误地错过了它,但从那以后我比较了代码路径并了解到有对打算保留为空的列表不使用容量参数有一点优势。 在实体框架 7 中有类似的功能吗? 不适用于投影或匿名对象。【参考方案3】:

我的解决方案,首先使用代码: 以这种方式声明 DateTime 属性:

private DateTime _DateTimeProperty;
public DateTime DateTimeProperty

    get
    
        return _DateTimeProperty;
    
    set
    
        _DateTimeProperty = value.ToKindUtc();
    

还可以将属性创建为:

private DateTime? _DateTimeProperty;
public DateTime? DateTimeProperty

    get
    
        return _DateTimeProperty;
    
    set
    
        _DateTimeProperty = value.ToKindUtc();
    

ToKindUtc() 是将 DateTimeKind.Unspecified 更改为 DateTimeKind.Utc 的扩展,如果 kind 是 DateTimeKind.Local,请致电 ToUniversalTime() 这里是扩展的代码:

public static class DateTimeExtensions

    public static DateTime ToKindUtc(this DateTime value)
    
        return KindUtc(value);
    
    public static DateTime? ToKindUtc(this DateTime? value)
    
        return KindUtc(value);
    
    public static DateTime ToKindLocal(this DateTime value)
    
        return KindLocal(value);
    
    public static DateTime? ToKindLocal(this DateTime? value)
    
        return KindLocal(value);
    
    public static DateTime SpecifyKind(this DateTime value, DateTimeKind kind)
    
        if (value.Kind != kind)
        
            return DateTime.SpecifyKind(value, kind);
        
        return value;
    
    public static DateTime? SpecifyKind(this DateTime? value, DateTimeKind kind)
    
        if (value.HasValue)
        
            return DateTime.SpecifyKind(value.Value, kind);
        
        return value;
    
    public static DateTime KindUtc(DateTime value)
    
        if (value.Kind == DateTimeKind.Unspecified)
        
            return DateTime.SpecifyKind(value, DateTimeKind.Utc);
        
        else if (value.Kind == DateTimeKind.Local)
        
            return value.ToUniversalTime();
        
        return value;
    
    public static DateTime? KindUtc(DateTime? value)
    
        if (value.HasValue)
        
            return KindUtc(value.Value);
        
        return value;
    
    public static DateTime KindLocal(DateTime value)
    
        if (value.Kind == DateTimeKind.Unspecified)
        
            return DateTime.SpecifyKind(value, DateTimeKind.Local);
        
        else if (value.Kind == DateTimeKind.Utc)
        
            return value.ToLocalTime();
        
        return value;
    
    public static DateTime? KindLocal(DateTime? value)
    
        if (value.HasValue)
        
            return KindLocal(value.Value);
        
        return value;
    

记得包含在模型的文件中。

using TheNameSpaceWhereClassIsDeclared;

属性的 set 方法在使用 EF 从数据库读取时调用,或者在 MVC 控制器的编辑方法中分配时调用。

警告,如果在网络表单中,如果您在本地时区编辑日期,您必须在发送到服务器之前将日期转换为 UTC。

【讨论】:

【参考方案4】:

在这里查看 michael.aird 的答案:https://***.com/a/9386364/279590 它在加载期间标记日期 UTC 类型,并在 ObjectMaterialized 上添加一个事件。

【讨论】:

以上是关于如何指定从 EntityFramework 检索的 DateTime 对象应该是 DateTimeKind.UTC [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Linux如何在多个文本文件中检索指定内容?

Entity Framework Core 从表中检索单个对象

如何从 NuGet 安装 EntityFramework 5.0(和其他旧版本)?

entityframework 已经有一个与此命令关联的打开的 DataReader 必须先关闭

如何通过串口发送请求从目录中检索数据?

指定的 EntityFramework 架构无效