问题
Is there a way that I can instruct Entity Framework to always store DateTimeOffset as the UTC value. Technically there is no reason for this but I prefer it if all the data in my database is consistent.
Currently it is storing the DateTimeOffset as received from the client which could be anything depending on the users locale.
I was thinking of perhaps intercepting all the SaveChanges method, looping through the changes, looking for DateTimeOffset types and doing the conversion explicitly, but this seems like a lot of work which could be mitigated if EntityFramework has something built in.
Any insight or suggestions would be greatly appreciated.
EDIT: The difference between my post and others dealing with this topic is that my C# property is of type DateTimeOffset and my database field is also DateTimeOffset(0). There is not technical reason to convert other than consistency. If I store a value as "17:00 +5:00" or "10:00 -2:00" it doesn't matter, those are both identical moments in time. However, I would like to have all my dates stored as "12:00 +0:00" so that it is easier to read in SSMS or other DB tools.
EDIT 2: Seeing as there doesn't seem to be "simple" way to do this I am looking to now tackle the problem in the DbContext.SaveChanges() function. The problem is that no matter what I do, the value won't change? Here's the code so far:
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 I finally managed to solve this, see answer below
回答1:
Something like this?
public System.DateTime TimeUTC
{
get;
set;
}
[NotMapped]
public System.DateTime LocalTime
{
get
{
return this.TimeUTC.ToLocalTime();
}
set
{
this.TimeUTC = value.ToUniversalTime();
}
}
回答2:
Thanks to the help I have received. I finally managed to get to the answer thanks to 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:
At the advice of Jcl, I wrote a small helper to cache the reflection:
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: To clean up the code a bit I created a generic function which you can use to intercept and modify any Data Type:
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)));
}
}
}
The usage of this is (Inside your 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();
}
Disclaimer This does solve my problem but is not recommended. I don't even know if I am going to use this as something about it just feels wrong now (Thanks Jcl ;). So please do use this with caution. It may probably be better that you follow the answer by Adam Benson
来源:https://stackoverflow.com/questions/39960923/entity-framework-always-save-datetimeoffset-as-utc