sql >> Databasteknik >  >> RDS >> Sqlserver

DateTime.Kind inställd på ospecificerad, inte UTC, vid laddning från databasen

Förutsatt att du använder EF6 och du vill ställa in Kind egenskapen för någon DateTime värde hämtat från databasen till Utc .

Liknande frågor har ställts, och svaren tenderar att tyda på att du ansluter dig till ObjectContext.ObjectMaterialized händelse, men den startade inte för frågor som använder projektion.

Lösningen jag kommer att föreslå fungerar för både entitets- och projektionsfrågor genom att utföra konverteringen på DbDataReader nivå (som används av den här typen av frågor).

För att göra det behöver vi en anpassad DbDataReader implementering som fångar upp GetDateTime metod. Tyvärr implementerar DbDataReader härledd klass kräver mycket kod. Lyckligtvis har jag redan skapat en basklass från mitt svar på Dynamisk översätt för att undvika C#-syntaxfel som helt enkelt delegerar varje metod till den underliggande DbDataReader exempel, så jag tar det därifrån:

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

och bygg den faktiska klassen som vi behöver ovanpå den:

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

När vi har det måste vi ansluta den till EF-infrastrukturen med EF avlyssning .

Vi börjar med att skapa en anpassad DbCommandInterceptor härledd klass:

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

registrera det (till exempel från din DbContext härledd klass statisk konstruktor):

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

och vi är klara.

Nu varje DateTime värde som kommer från databasen kommer att ha Kind egenskapen inställd på Utc .




  1. VÄLJ ANTAL över en-till-många-relationer

  2. Hur man hittar de vanligaste orden i en MySQL-databas och snittar en andra kolumn

  3. Oracle Natural Joins and Count(1)

  4. Ta reda på om ett objekt är en främmande nyckel med OBJECTPROPERTY() i SQL Server