Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

DateTime.Kind wird beim Laden aus der Datenbank auf „unspecified“ und nicht auf „UTC“ gesetzt

Angenommen, Sie verwenden EF6 und möchten den Kind festlegen Eigenschaft von DateTime aus der Datenbank abgerufener Wert in Utc .

Ähnliche Fragen wurden gestellt, und die Antworten deuten eher darauf hin, sich in den ObjectContext.ObjectMaterialized Ereignis, aber es wurde nicht für Abfragen ausgelöst, die Projektion verwenden.

Die Lösung, die ich vorschlagen werde, funktioniert sowohl für Entitäts- als auch für Projektionsabfragen, indem die Konvertierung am DbDataReader durchgeführt wird Ebene (die von dieser Art von Abfragen verwendet wird).

Dazu benötigen wir einen benutzerdefinierten DbDataReader Implementierung, die GetDateTime Methode. Leider Implementierung von DbDataReader abgeleitete Klasse erfordert eine Menge Boilerplate-Code. Glücklicherweise habe ich bereits eine Basisklasse aus meiner Antwort auf Dynamische Übersetzung zur Vermeidung von C#-Syntaxfehlern die einfach jede Methode an den zugrunde liegenden DbDataReader delegiert Beispiel, also nehme ich es einfach von dort:

abstract class DelegatingDbDataReader : DbDataReader
{
    readonly DbDataReader source;
    public DelegatingDbDataReader(DbDataReader source)
    {
        this.source = source;
    }
    public override object this[string name] { get { return source[name]; } }
    public override object this[int ordinal] { get { return source[ordinal]; } }
    public override int Depth { get { return source.Depth; } }
    public override int FieldCount { get { return source.FieldCount; } }
    public override bool HasRows { get { return source.HasRows; } }
    public override bool IsClosed { get { return source.IsClosed; } }
    public override int RecordsAffected { get { return source.RecordsAffected; } }
    public override bool GetBoolean(int ordinal) { return source.GetBoolean(ordinal); }
    public override byte GetByte(int ordinal) { return source.GetByte(ordinal); }
    public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) { return source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length); }
    public override char GetChar(int ordinal) { return source.GetChar(ordinal); }
    public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) { return source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length); }
    public override string GetDataTypeName(int ordinal) { return source.GetDataTypeName(ordinal); }
    public override DateTime GetDateTime(int ordinal) { return source.GetDateTime(ordinal); }
    public override decimal GetDecimal(int ordinal) { return source.GetDecimal(ordinal); }
    public override double GetDouble(int ordinal) { return source.GetDouble(ordinal); }
    public override IEnumerator GetEnumerator() { return source.GetEnumerator(); }
    public override Type GetFieldType(int ordinal) { return source.GetFieldType(ordinal); }
    public override float GetFloat(int ordinal) { return source.GetFloat(ordinal); }
    public override Guid GetGuid(int ordinal) { return source.GetGuid(ordinal); }
    public override short GetInt16(int ordinal) { return source.GetInt16(ordinal); }
    public override int GetInt32(int ordinal) { return source.GetInt32(ordinal); }
    public override long GetInt64(int ordinal) { return source.GetInt64(ordinal); }
    public override string GetName(int ordinal) { return source.GetName(ordinal); }
    public override int GetOrdinal(string name) { return source.GetOrdinal(name); }
    public override string GetString(int ordinal) { return source.GetString(ordinal); }
    public override object GetValue(int ordinal) { return source.GetValue(ordinal); }
    public override int GetValues(object[] values) { return source.GetValues(values); }
    public override bool IsDBNull(int ordinal) { return source.IsDBNull(ordinal); }
    public override bool NextResult() { return source.NextResult(); }
    public override bool Read() { return source.Read(); }
    public override void Close() { source.Close(); }
    public override T GetFieldValue<T>(int ordinal) { return source.GetFieldValue<T>(ordinal); }
    public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken) { return source.GetFieldValueAsync<T>(ordinal, cancellationToken); }
    public override Type GetProviderSpecificFieldType(int ordinal) { return source.GetProviderSpecificFieldType(ordinal); }
    public override object GetProviderSpecificValue(int ordinal) { return source.GetProviderSpecificValue(ordinal); }
    public override int GetProviderSpecificValues(object[] values) { return source.GetProviderSpecificValues(values); }
    public override DataTable GetSchemaTable() { return source.GetSchemaTable(); }
    public override Stream GetStream(int ordinal) { return source.GetStream(ordinal); }
    public override TextReader GetTextReader(int ordinal) { return source.GetTextReader(ordinal); }
    public override Task<bool> IsDBNullAsync(int ordinal, CancellationToken cancellationToken) { return source.IsDBNullAsync(ordinal, cancellationToken); }
    public override Task<bool> ReadAsync(CancellationToken cancellationToken) { return source.ReadAsync(cancellationToken); }
    public override int VisibleFieldCount { get { return source.VisibleFieldCount; } }
}

und bauen die eigentliche Klasse, die wir brauchen, darauf auf:

class UtcDateTimeConvertingDbDataReader : DelegatingDbDataReader
{
    public UtcDateTimeConvertingDbDataReader(DbDataReader source) : base(source) { }
    public override DateTime GetDateTime(int ordinal)
    {
        return DateTime.SpecifyKind(base.GetDateTime(ordinal), DateTimeKind.Utc);
    }
}

Sobald wir das haben, müssen wir es mit EF interception .

Wir beginnen mit der Erstellung eines benutzerdefinierten DbCommandInterceptor abgeleitete Klasse:

class UtcDateTimeConvertingDbCommandInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        base.ReaderExecuted(command, interceptionContext);
        if (!(interceptionContext.Result is UtcDateTimeConvertingDbDataReader)
            && interceptionContext.Result != null
            && interceptionContext.Exception == null)
            interceptionContext.Result = new UtcDateTimeConvertingDbDataReader(interceptionContext.Result);
    }
}

registrieren (zum Beispiel von Ihrem DbContext statischer Konstruktor der abgeleiteten Klasse):

public class YourDbContext : DbContext
{
    static YourDbContext()
    {
        DbInterception.Add(new UtcDateTimeConvertingDbCommandInterceptor());
    }
    // ...
}

und wir sind fertig.

Jetzt jede DateTime Wert, der aus der Datenbank kommt, hat Kind Eigenschaft auf Utc gesetzt .