web-dev-qa-db-ja.com

EntityFrameworkから取得したDateTimeオブジェクトがDateTimeKind.UTCであることを指定する方法

すべてのDateTimeオブジェクトがDateTimeKind.UTCであるC#プログラムがあります。データベースにオブジェクトを保存すると、UTCが期待どおりに保存されます。ただし、それらを取得するときは、DateTimeKind.Unspecifiedです。 C#でDateTimeオブジェクトを作成するときにEntity Framework(Code First)に常にDateTimeKind.UTCを使用するように指示する方法はありますか?

27
William

いいえ、ありません。そして、実際にはDateTimeKind.Unspecified

ただし、複数のタイムゾーンのサポートについて懸念がある場合は、 DateTimeOffset の使用を検討する必要があります。通常のDateTimeに似ていますが、時間の「視点」を表すのではなく、3PM(UTC-3)が4PM(UTC-2)に等しい絶対ビューを表します。 DateTimeOffsetにはDateTimeとタイムゾーンの両方が含まれており、EntityFrameworkとSQL Serverの両方でサポートされています。

16
André Pena

データコンテキストに関連するすべての値を修正してもらうことができます。次の例では、エンティティタイプのプロパティのキャッシュを使用して、毎回タイプを調べる必要を回避しています。

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の属性。

7
Jon Hanna

最初にコードを使用する私のソリューション:この方法で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;
_

プロパティのsetメソッドは、EFでデータベースから読み取るとき、またはMVCコントローラーの編集メソッドで割り当てられるときに呼び出されます。

警告、Webフォームの場合、ローカルタイムゾーンで日付を編集する場合、サーバーに送信する前に日付をUTCに変換する必要があります。

3
FRL

Michael.airdの回答をご覧ください: https://stackoverflow.com/a/9386364/279590 読み込み中にUTCの種類をスタンプします、ObjectMaterializedのイベントを使用します。

1