2015-04-07 6 views
24

sein sollten Ich habe ein C# -Programm, in dem alle DateTime-Objekte DateTimeKind.UTC sind. Wenn die Objekte in der Datenbank gespeichert werden, speichert sie UTC wie erwartet. Wenn sie jedoch erneut abgerufen werden, sind sie DateTimeKind.Unspecified. Gibt es eine Möglichkeit, Entity Framework (Code First) mitzuteilen, wenn DateTime Objekte in C# erstellt werden, um immer DateTimeKind.UTC zu verwenden?Wie Sie angeben, dass aus EntityFramework abgerufene DateTime-Objekte DateTimeKind.UTC

+0

Bitte zeigen Sie die Zeilen des Codes, wo Sie die Daten aus der db in C# –

Antwort

15

Nein, es gibt keine. Und es ist tatsächlich DateTimeKind.Unspecified. Wenn Sie jedoch Bedenken haben, mehrere Zeitzonen zu unterstützen, sollten Sie die Verwendung von DateTimeOffset in Erwägung ziehen. Es ist wie eine normale DateTime, außer dass es keine "Perspektive" der Zeit darstellt, sondern eine absolute Ansicht, in der 3PM (UTC - 3) 4PM (UTC - 2) entspricht. DateTimeOffset enthält sowohl die DateTime als auch die Zeitzone und wird sowohl von EntityFramework als auch von SQL Server unterstützt.

+0

bekommen, aber was ist mit diesem ... https: //social.msdn.microsoft.com/Forums/en-US/1a86afb2-e5c7 -46b6-9759-d815fad8da5e/entity-framework-convert-datetime-zwischen-arten? Forum = adodotnetelentityframework? –

+0

Sie erstellen eine andere nicht persistente Eigenschaft zum Konvertieren der DateTime in UTC oder Local. Nicht genau was OP will. –

+2

'DateTimeOffset' * enthält * keine Zeitzone: Es enthält einen * UTC-Offset * für den Zeitpunkt, an dem der Wert erstellt wurde.Es hat ein wenig mehr Informationen über den tatsächlichen Wert, aber es ist kaum nützlicher als ein nackter UTC 'DateTime' Wert. Wenn Sie einen Wert relativ zu einer Zeitzone wünschen, müssen Sie diese Zeitzone irgendwo speichern und den Wert wie für eine 'DateTime' konvertieren. – Suncat2000

7

Sie können Ihren Datenkontext veranlassen, alle relevanten Werte zu korrigieren. Die folgende tut dies mit einem Cache von Eigenschaften für Objekttypen, um zu vermeiden, den Typen jedes Mal zu prüfen:

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 */ 
} 

Dies auch mit Attributen kombiniert werden kann, um eine zu erlauben, die DateTimeKind jede Eigenschaft festlegen sollte, durch Speichern einer Reihe von Regeln für jede Eigenschaft, anstatt nur die PropertyInfo, und auf der Suche nach dem Attribut in GetDateProperties.

+0

Sieht gut aus! Es gibt 'GetPropertyRules'' GetDateProperties', die nicht übereinstimmen, – Artyom

+0

Und wäre es nicht besser, 'private static readonly zu haben List EmptyPropsList = new List (0);'? – Artyom

+0

@Artyom ironisch zu der Zeit, als ich das schrieb, hätte ich gedacht, dass die explizite 0 besser war und die obige nur aus Versehen verpasste, aber seitdem habe ich die Codepfade verglichen und gelernt, dass es einen kleinen Vorteil gibt, keine Kapazität zu verwenden Argument für eine Liste, die Sie leer halten möchten. –

2

Meine Lösung, Code zuerst: die Datetime-Eigenschaften auf diese Weise erklären:

private DateTime _DateTimeProperty; 
public DateTime DateTimeProperty 
{ 
    get 
    { 
     return _DateTimeProperty; 
    } 
    set 
    { 
     _DateTimeProperty = value.ToKindUtc(); 
    } 
} 

auch die Eigenschaft erstellen, wie:

private DateTime? _DateTimeProperty; 
public DateTime? DateTimeProperty 
{ 
    get 
    { 
     return _DateTimeProperty; 
    } 
    set 
    { 
     _DateTimeProperty = value.ToKindUtc(); 
    } 
} 

ToKindUtc() eine Verlängerung ist DateTimeKind.Unspecified zu DateTimeKind.Utc ändern oder rufen Sie ToUniversalTime(), wenn Art ist DateTimeKind.Local Hier der Code für die Erweiterungen:

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

Denken Sie daran, in die Modelldatei aufzunehmen.

using TheNameSpaceWhereClassIsDeclared; 

Die Set-Methode der Eigenschaft aufgerufen wird, wenn von datatabase mit EF lesen, oder wenn in einer MVC-Controllers bearbeiten Methode zugeordnet.

Warnung, wenn Sie in Webformularen Daten in der lokalen Zeitzone bearbeiten, MÜSSEN Sie das Datum vor dem Senden an den Server in UTC umwandeln.