2016-10-10 3 views
1

Есть ли способ, которым я могу дать указание Entity Framework всегда хранить DateTimeOffset в качестве значения UTC. Технически для этого нет оснований, но я предпочитаю, чтобы все данные в моей базе данных были согласованы.Entity Framework всегда сохраняет DateTimeOffset как UTC

В настоящее время он хранит DateTimeOffset как полученное от клиента, которое может быть чем угодно в зависимости от локали пользователей.

Я думал, возможно, перехватывать все метод SaveChanges, циклически изменяется, ищет типов DateTimeOffset и делает преобразование в явном виде, но это, кажется, как много работы, которая может быть смягчено, если EntityFramework имеет что-то встраивается.

Любое понимание или предложения были бы весьма признательны.

EDIT: Разница между моим сообщением и другими, имеющими отношение к этой теме, заключается в том, что мое свойство C# имеет тип DateTimeOffset, а поле моей базы данных также DateTimeOffset (0). Нет никаких технических причин для преобразования, кроме последовательности. Если я сохраняю значение как «17:00 +5: 00» или «10:00 -2: 00», это не имеет значения, это оба одинаковых момента времени. Тем не менее, я хотел бы, чтобы все мои даты хранились как «12:00 +0: 00», так что их легче читать в SSMS или других инструментах DB.

РЕДАКТИРОВАТЬ 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(); 
} 

EDIT 3 я, наконец, удалось решить эту проблему, см ответ ниже

+0

Возможно, вы хотите сохранить все как UTC + 0?Как вы сможете конвертировать его обратно, когда вам нужно отобразить формат локали пользователей? Учитывайте также изменения в часовом поясе, такие как BST – musefan

+0

@musefan Именно по этой причине сохранение в UTC имеет смысл. Да, вам нужен локальный формат/часовой пояс, но он будет работать в глобальном масштабе, а не только в часовом поясе, где было введено время datetime. – Maarten

+0

Возможный дубликат [Can Entity Framework автоматически конвертирует поля datetime в UTC при сохранении?] (Http://stackoverflow.com/questions/39418323/canentity-framework-automatically-convert-datetime-fields-to-utc-on -save) – Igor

ответ

0

Что-то вроде этого?

public System.DateTime TimeUTC 
{ 
    get; 
    set; 
} 

[NotMapped] 
public System.DateTime LocalTime 
{ 
    get 
    { 
     return this.TimeUTC.ToLocalTime(); 
    } 

    set 
    { 
     this.TimeUTC = value.ToUniversalTime(); 
    } 
} 
+0

Спасибо Адаму, хотя он не решает мою просьбу с точки зрения центрального набора и забывает функцию в EF, я начинаю думать, что этот подход, вероятно, лучший способ справиться с этим экземпляром. Хотя я использую DateTimeOffset, а не DateTime, подход остается тем же. – Talon

+0

ОП задает вопрос о 'DateTimeOffset', а не' DateTime'. Нет причин для конвертирования из/в UTC –

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

EDIT:

По совету 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]; 
    } 
} 

EDIT 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

+1

Убедитесь, что вы кешируете все эти вызовы 'GetType(). GetProperties()': они очень медленны (вы можете сделать любое кэширующее решение, которое коррелирует 'Type' и' PropertyName 'с фактическим' PropertyInfo') – Jcl

+0

@Jcl Я сейчас рассмотрю, что все эти проблемы только для читаемости заставляют меня задуматься, но во имя опыта я буду упорствовать. – Talon

+0

честно, если бы я должен был это сделать (* я бы не хотел *), я бы ** также ** сохранял часовой пояс, в который пользователь вводил дату-время (и я имею в виду фактический часовой пояс, который был бы лучше если вам не нужна производительность, чем просто сохранение смещения): я просто пытался помочь вам с вашим вопросом; ваши дизайнерские решения - это совсем другая история :-) Удачи! – Jcl

Смежные вопросы