如何指定从 EntityFramework 检索到的 DateTime 对象应该是 DateTimeKind.UTC
How to specify that DateTime objects retrieved from EntityFramework should be DateTimeKind.UTC
我有一个 C# 程序,其中所有 DateTime
个对象都是 DateTimeKind.UTC
。将对象保存到数据库时,它会按预期存储 UTC。然而,当检索它们时,它们是DateTimeKind.Unspecified
。在 C# 中创建 DateTime
对象时,有没有办法告诉 Entity Framework(代码优先)始终使用 DateTimeKind.UTC
?
不,没有。实际上是 DateTimeKind.Unspecified
.
但是,如果您担心支持多个时区,则应考虑使用 DateTimeOffset。它就像一个常规的 DateTime,只是它不代表 "perspective" 时间,它代表一个绝对视图,其中 3PM (UTC - 3) 等于 4PM (UTC - 2)。 DateTimeOffset 包含日期时间和时区,EntityFramework 和 SQL 服务器都支持它。
您可以让您的数据上下文在运行时修复所有相关值。以下内容使用实体类型的属性缓存来执行此操作,以避免每次都必须检查类型:
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
.
中寻找属性
在此处查看 michael.aird 答案:
它在加载期间标记日期 UTC 种类,并在对象物化上有一个事件。
我的解决方案,首先使用代码:
以这种方式声明 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;
属性 的设置方法在使用 EF 从数据库读取时调用,或者在 MVC 控制器的编辑方法中分配时调用。
警告,如果在 Web 表单中,如果您在本地时区编辑日期,则必须在发送到服务器之前将日期转换为 UTC。
我有一个 C# 程序,其中所有 DateTime
个对象都是 DateTimeKind.UTC
。将对象保存到数据库时,它会按预期存储 UTC。然而,当检索它们时,它们是DateTimeKind.Unspecified
。在 C# 中创建 DateTime
对象时,有没有办法告诉 Entity Framework(代码优先)始终使用 DateTimeKind.UTC
?
不,没有。实际上是 DateTimeKind.Unspecified
.
但是,如果您担心支持多个时区,则应考虑使用 DateTimeOffset。它就像一个常规的 DateTime,只是它不代表 "perspective" 时间,它代表一个绝对视图,其中 3PM (UTC - 3) 等于 4PM (UTC - 2)。 DateTimeOffset 包含日期时间和时区,EntityFramework 和 SQL 服务器都支持它。
您可以让您的数据上下文在运行时修复所有相关值。以下内容使用实体类型的属性缓存来执行此操作,以避免每次都必须检查类型:
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
.
在此处查看 michael.aird 答案: 它在加载期间标记日期 UTC 种类,并在对象物化上有一个事件。
我的解决方案,首先使用代码: 以这种方式声明 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;
属性 的设置方法在使用 EF 从数据库读取时调用,或者在 MVC 控制器的编辑方法中分配时调用。
警告,如果在 Web 表单中,如果您在本地时区编辑日期,则必须在发送到服务器之前将日期转换为 UTC。