2015-04-07 20 views
24

Ho un programma C# in cui tutti gli oggetti DateTime sono DateTimeKind.UTC. Quando si salvano gli oggetti nel database, esso memorizza l'UTC come previsto. Tuttavia, quando li ritengono, sono DateTimeKind.Unspecified. C'è un modo per dire a Entity Framework (Code First) quando si creano oggetti DateTime in C# per usare sempre DateTimeKind.UTC?Come specificare che gli oggetti DateTime recuperati da EntityFramework devono essere DateTimeKind.UTC

+0

Mostrare le righe di codice in cui si ricevono i dati dal db in C# –

risposta

15

No, non c'è. Ed è in realtà DateTimeKind.Unspecified.

Tuttavia, se si è interessati a supportare più fusi orari, è consigliabile prendere in considerazione l'utilizzo di DateTimeOffset. È come un normale DateTime, tranne che non rappresenta una "prospettiva" di tempo, rappresenta una vista assoluta, in cui 3PM (UTC-3) è uguale a 4PM (UTC-2). DateTimeOffset contiene sia il DateTime che il fuso orario ed è supportato da EntityFramework e SQL Server.

+0

ma che dire di questo ... https: //social.msdn.microsoft.com/Forums/en-US/1a86afb2-e5c7 -46b6-9759-d815fad8da5e/entity-framework-convert-datetime-between-types? Forum = adodotnetentityframework? –

+0

Stanno creando un'altra proprietà non persistente per la conversione di DateTime in UTC o Local. Non esattamente ciò che l'OP vuole. –

+2

'DateTimeOffset' * non * contiene il fuso orario: contiene un * offset UTC * per l'istante in cui il valore è stato creato.Ha un po 'più di informazioni sul valore reale, ma è difficilmente più utile di un valore di 'DateTime' UTC nuda. Se vuoi un valore relativo a un fuso orario, devi memorizzare quel fuso orario da qualche parte e convertire il valore, proprio come faresti per un 'DateTime'. – Suncat2000

7

È possibile che il proprio datacontext corregga tutti i valori rilevanti man mano che procede. Quanto segue fa con una cache di proprietà per tipi di entità, in modo da evitare di dover esaminare il tipo ogni volta:

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

Questo può anche essere combinato con attributi in modo da permettere di impostare la DateTimeKind ogni proprietà dovrebbe avere, memorizzando un insieme di regole su ciascuna proprietà, anziché solo il PropertyInfo e cercando l'attributo in GetDateProperties.

+0

Sembra carino! Esiste una corrispondenza mancata di 'GetPropertyRules'' GetDateProperties' su – Artyom

+0

E non sarebbe meglio avere 'readonly statico privato List EmptyPropsList = new List (0);'? – Artyom

+0

@Artyom ironicamente nel momento in cui l'ho scritto avrei pensato che l'esplicito 0 fosse migliore e quanto sopra lo mancava per errore, ma da allora ho confrontato i percorsi del codice e ho imparato che c'è un leggero vantaggio nell'usare senza capacità argomento per un elenco che si intende mantenere vuoto. –

2

La mia soluzione, utilizzando il codice prima: Dichiarare le proprietà DateTime in questo modo:

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

inoltre possibile creare la proprietà come:

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

ToKindUtc() è un'estensione di cambiare DateTimeKind.Unspecified a DateTimeKind.Utc oppure chiamare ToUniversalTime() se il tipo è DateTimeKind.Local Qui il codice per le estensioni:

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

Ricordarsi di includere nel file del modello.

using TheNameSpaceWhereClassIsDeclared; 

Il metodo set di proprietà viene chiamata durante la lettura da datatabase con EF, o quando assegnata nel metodo di modifica di un controller MVC.

Avviso, se nei moduli Web, se si modificano le date nel fuso orario locale, è necessario convertire la data in UTC prima di inviare al server.

Problemi correlati