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

时间:2015-04-07 16:18:24

标签: c# datetime entity-framework-6 utc

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

4 个答案:

答案 0 :(得分:16)

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

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

答案 1 :(得分:7)

您可以让您的datacontext修复所有相关值。以下是实体类型的属性缓存,以避免每次都检查类型:

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中的属性。

答案 2 :(得分: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的扩展程序,如果是ToUniversalTime(),则可致电DateTimeKind.Local 这里是扩展的代码:

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;

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

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

答案 3 :(得分:1)

在这里查看michael.aird答案:https://stackoverflow.com/a/9386364/279590 它在加载期间标记UTC种类,并在ObjectMaterialized上标记事件。