Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

DateTime.Kind ustawiony na nieokreślony, nie UTC, podczas ładowania z bazy danych

Zakładając, że używasz EF6 i chcesz ustawić Kind właściwość dowolnej DateTime wartość pobrana z bazy danych do Utc .

Zadawano podobne pytania, a odpowiedzi sugerują podpięcie się pod ObjectContext.ObjectMaterialized zdarzenie, ale nie uruchamiał się w przypadku zapytań korzystających z projekcji.

Rozwiązanie, które zamierzam zaproponować działa zarówno dla zapytań encji jak i projekcji, wykonując konwersję w DbDataReader poziom (który jest używany przez tego typu zapytania).

W tym celu potrzebujemy niestandardowego DbDataReader implementacja, która przechwytuje GetDateTime metoda. Niestety implementacja DbDataReader klasa pochodna wymaga dużo kodu wzorcowego. Na szczęście stworzyłem już klasę bazową z mojej odpowiedzi na Dynamiczny tłumaczenie, aby uniknąć błędów składni C# który po prostu deleguje każdą metodę do bazowego DbDataReader przykład, więc zajmę się tym stamtąd:

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

i zbuduj rzeczywistą klasę, której potrzebujemy:

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

Gdy już to mamy, musimy podłączyć go do infrastruktury EF przy użyciu EF przechwytywanie .

Zaczniemy od utworzenia niestandardowego DbCommandInterceptor klasa pochodna:

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

zarejestruj go (na przykład z twojego DbContext konstruktor statyczny klasy pochodnej):

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

i skończyliśmy.

Teraz co DateTime wartość pochodząca z bazy danych będzie miała Kind właściwość ustawiona na Utc .




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak dostroić wydajność SQL Server, Azure SQL Database i Amazon RDS

  2. Jak mogę wyszukiwać rankingi użytkowników w mojej bazie danych, ale brać pod uwagę tylko najnowszy wpis dla każdego użytkownika?

  3. Nazwa kolumny i/lub nazwa tabeli jako parametry

  4. Jak zmienić kolumnę z wartości Null na Not Null w tabeli SQL Server — samouczek SQL Server / T-SQL, część 52

  5. Procedura składowana zgłasza błąd w SQL Server 2012, ale działa dobrze w SQL Server 2000