[NET] EF Core Cannot write DateTime with Kind=Unspecified to PostgreSQL type ‘timestamp with time zone’ only UTC is supported

มาจดไว้ก่อน เพราะ มีงานที่ใช้ PostgreSQL แล้ว ดันเจอ Error นี้ได้

fail: Microsoft.EntityFrameworkCore.Update[10000]
      An exception occurred in the database while saving changes for context type 'AccountPositionApi.Data.AccountPositionGenDbContext'.
      Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
       ---> System.ArgumentException: Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported. Note that it's not possible to mix DateTimes with different Kinds in an array, range, or multirange. (Parameter 'value')
         at Npgsql.Internal.Converters.DateTimeConverterResolver`1.Get(DateTime value, Nullable`1 expectedPgTypeId, Boolean validateOnly)
         at Npgsql.Internal.Converters.DateTimeConverterResolver.<>c.<CreateResolver>b__0_0(DateTimeConverterResolver`1 resolver, DateTime value, Nullable`1 expectedPgTypeId)
         at Npgsql.Internal.Converters.DateTimeConverterResolver`1.Get(T value, Nullable`1 expectedPgTypeId)
         at Npgsql.Internal.PgConverterResolver`1.GetAsObjectInternal(PgTypeInfo typeInfo, Object value, Nullable`1 expectedPgTypeId) 
         at Npgsql.Internal.PgResolverTypeInfo.GetResolutionAsObject(Object value, Nullable`1 expectedPgTypeId)
         at Npgsql.Internal.PgTypeInfo.GetObjectResolution(Object value)
         at Npgsql.NpgsqlParameter.ResolveConverter(PgTypeInfo typeInfo)
         at Npgsql.NpgsqlParameter.ResolveTypeInfo(PgSerializerOptions options)
         at Npgsql.NpgsqlParameterCollection.ProcessParameters(PgSerializerOptions options, Boolean validateValues, CommandType commandType)
         at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
         at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
         at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
         --- End of inner exception stack trace ---

มาดูก่อนก่อนว่าผมกำหนดอะไรบ้าง

  • Schema
CREATE TABLE IF NOT EXISTS public.account_daily_position
(
    "ValueDate" timestamp with time zone NOT NULL,
    "PortfolioId" text COLLATE pg_catalog."default" NOT NULL,
    ...
)
  • Entity
[Table("accountdailyposition")]
public class AccountDailyPositionEntity : MasterEntity
{
   ..
   [Required]
   public DateTime ValueDate { get; set; }
   ...  
}

ทางแก้ปัญหาที่เป็นไปได้หละ

  • เปลี่ยน Data Type ของ Dotnet ไปใช้ DateTimeOffSet มันเข้ากับ timestamp with time zone ของ PostgreSQL คล้ายกับเคสของ DB2
[Table("accountdailyposition")]
public class AccountDailyPositionEntity : MasterEntity
{
   ..
   [Required]
   public DateTimeOffSet ValueDate { get; set; }
   ...  
}
  • ปรับที่ Entity ให้เป็น UTC
[Table("account_daily_position")]
public class AccountDailyPositionEntity : MasterEntity
{
   ..
   private DateTime _ValueDate;
   [Required]
   public DateTime ValueDate { get; set; }
   {
      get => _ValueDate;
      set
      {
         _ValueDate= value.Kind == DateTimeKind.Utc
                    ? value
                    : DateTime.SpecifyKind(value, DateTimeKind.Utc);
      }
   }
   ...  
}
  • เปิด flag EnableLegacyTimestampBehavior ตอน App Startup
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
  • แก้ DB Schema timestamp without time zone อันนี้อาจจะมีผลกับระบบออกแบบมาเป็น Global ข้อมูลบางส่วนหายไป

สำหรับเคสของผม ลองปรับเป็น DateTimeOffSet แทนครับ Happy Debugging > Blogging

Reference


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts sent to your email.