实体框架始终将DateTimeOffset保存为UTC

时间:2016-10-10 14:44:24

标签: c# .net entity-framework dbcontext datetimeoffset

是否有一种方法可以指示Entity Framework始终将DateTimeOffset存储为UTC值。从技术上讲,没有理由这样做但我更喜欢它,如果我的数据库中的所有数据都是一致的。

目前它正在存储从客户端收到的DateTimeOffset,它可以是任何东西,具体取决于用户的区域设置。

我在考虑拦截所有的SaveChanges方法,循环更改,查找DateTimeOffset类型并明确地进行转换,但是如果EntityFramework内置了一些内容,这似乎可以减轻很多工作。

非常感谢任何见解或建议。

编辑:我的帖子与处理此主题的其他人之间的区别在于我的C#属性是DateTimeOffset类型,我的数据库字段也是DateTimeOffset(0)。除了一致性之外,没有技术理由进行转换。如果我将值存储为" 17:00 +5:00"或" 10:00 -2:00"没关系,这些都是相同的时刻。但是,我希望将所有日期存储为" 12:00 +0:00"这样在SSMS或其他数据库工具中更容易阅读。

编辑2:看起来似乎没有"简单"我希望现在解决DbContext.SaveChanges()函数中的问题。问题在于,无论我做什么,价值都不会改变?这是迄今为止的代码:

public new void SaveChanges()
{
    foreach (var entry in this.ChangeTracker.Entries())
    {
        foreach (var propertyInfo in entry.Entity.GetType().GetProperties().Where(p => p.CanWrite && p.PropertyType == typeof(DateTimeOffset)))
        {
            var value = entry.CurrentValues.GetValue<DateTimeOffset>(propertyInfo.Name);

            var universalTime = value.ToUniversalTime();
            entry.CurrentValues[propertyInfo.Name] = universalTime;
            // entry.Property(propertyInfo.Name).CurrentValue = universalTime;

            // Adding a watch to "entry.CurrentValues[propertyInfo.Name]" reveals that it has not changed??
        }

        // TODO: DateTimeOffset?
    }

    base.SaveChanges();
}

编辑3 我终于设法解决了这个问题,请参阅下面的答案

2 个答案:

答案 0 :(得分:0)

这样的东西?

Activator

答案 1 :(得分:0)

感谢我收到的帮助。由于Jcl

,我终于找到了答案
public new void SaveChanges()
{
    foreach (var entry in this.ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Modified))
        {
        foreach (var propertyInfo in entry.Entity.GetType().GetTypeProperties().Where(p => p.PropertyType == typeof(DateTimeOffset)))
        {
            propertyInfo.SetValue(entry.Entity, entry.CurrentValues.GetValue<DateTimeOffset>(propertyInfo.Name).ToUniversalTime());
        }
        foreach (var propertyInfo in entry.Entity.GetType().GetTypeProperties().Where(p => p.PropertyType == typeof(DateTimeOffset?)))
        {
            var dateTimeOffset = entry.CurrentValues.GetValue<DateTimeOffset?>(propertyInfo.Name);
            if (dateTimeOffset != null) propertyInfo.SetValue(entry.Entity, dateTimeOffset.Value.ToUniversalTime());
        }
    }

     base.SaveChanges();
}

修改

根据Jcl的建议,我写了一个小助手来缓存反射:

public static class TypeReflectionExtension
{
    public static Dictionary<Type, PropertyInfo[]> PropertyInfoCache;

    static TypeReflectionHelper()
    {
        PropertyInfoCache = new Dictionary<Type, PropertyInfo[]>();
    }

    public static PropertyInfo[] GetTypeProperties(this Type type)
    {
        if (!PropertyInfoCache.ContainsKey(type))
        {
            PropertyInfoCache[type] = type.GetProperties();
        }
        return PropertyInfoCache[type];
    }
}

编辑2: 为了清理代码,我创建了一个通用函数,您可以使用它来拦截和修改任何数据类型:

public static class DbEntityEntryExtension
{
    public static void ModifyTypes<T>(this DbEntityEntry dbEntityEntry, Func<T, T> method)
    {
        foreach (var propertyInfo in dbEntityEntry.Entity.GetType().GetTypeProperties().Where(p => p.PropertyType == typeof(T) && p.CanWrite))
        {
            propertyInfo.SetValue(dbEntityEntry.Entity, method(dbEntityEntry.CurrentValues.GetValue<T>(propertyInfo.Name)));
        }
    }
}

这个用法是(在你的DbContext里面):

public new void SaveChanges()
{
    foreach (var entry in this.ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Modified))
    {
        entry.ModifyTypes<DateTimeOffset>(ConvertToUtc);
        entry.ModifyTypes<DateTimeOffset?>(ConvertToUtc);
    }

    base.SaveChanges();
}

private static DateTimeOffset ConvertToUtc(DateTimeOffset dateTimeOffset)
{
    return dateTimeOffset.ToUniversalTime();
}

private static DateTimeOffset? ConvertToUtc(DateTimeOffset? dateTimeOffset)
{
    return dateTimeOffset?.ToUniversalTime();
}

<强>声明 这确实解决了我的问题,但不推荐。我甚至不知道我是否会使用它,因为现在感觉不对(感谢Jcl;)。所以请谨慎使用。您可以通过Adam Benson

按照答案更好