简体   繁体   English

.net postgres EF 核心无法将 Kind=Local 的 DateTime 写入 PostgreSQL 类型“带时区的时间戳”

[英].net postgres EF core Cannot write DateTime with Kind=Local to PostgreSQL type 'timestamp with time zone'

I have a.Net6 console app that uses nuget packages:我有一个使用 nuget 包的.Net6 控制台应用程序:

  • "Microsoft.EntityFrameworkCore, Version 6.0.8" “Microsoft.EntityFrameworkCore,版本 6.0.8”
  • "Npgsql.EntityFrameworkCore.PostgreSQL, Version 6.0.6" “Npgsql.EntityFrameworkCore.PostgreSQL,版本 6.0.6”
  • "Npgsql, Version 6.0.6" “Npgsql,版本 6.0.6”

I am using postgres 14.4 as thats installed and used for running scripts, etc.我正在使用 postgres 14.4 作为安装并用于运行脚本等。
I run a.sql script called "createTables.sql" that looks like this我运行一个名为“createTables.sql”的.sql 脚本,看起来像这样

create table if not exists "Messages" (
    "Id" uuid primary key,
    "Time" timestamp without time zone,
    "MessageType" text,
    "Message" text
);

My EF core context looks like this我的 EF 核心上下文如下所示

namespace Database;

public class MessageContext : DbContext
{
  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
    var dbConfig = Configurer.Config.MessageDbConfig;
    optionsBuilder.UseNpgsql(new NpgsqlConnectionStringBuilder
      {
        Database = dbConfig.DbName,
        Username = dbConfig.Username,
        Password = dbConfig.Password,
        Host = dbConfig.Host,
        Port = dbConfig.Port,
        Pooling = dbConfig.Pooling
      }.ConnectionString,
     optionsBuilder => optionsBuilder.SetPostgresVersion(Version.Parse("14.4")));

    base.OnConfiguring(optionsBuilder);
  }

  public DbSet<Msg> Messages { get; set; }
}

public class Msg
{
  public Guid Id { get; set; } = Guid.NewGuid();
  public DateTime Time { get; set; } = DateTime.Now;
  public string MessageType { get; set; }
  public string Message { get; set; }
}

after running the.sql file using the psql command from the command line.使用命令行中的 psql 命令运行 .sql 文件后。 I check on pgAdmin 4 and see我检查 pgAdmin 4 并查看在此处输入图像描述
confirming that the column type is timestamp without timezone.确认列类型是没有时区的时间戳。 Yet when i create a new Message via然而,当我通过创建新消息时

    using var context = new MessageContext();
    context.Messages.Add(new Msg
    {
      MessageType = message.GetType()
        .FullName,
      Message = message.ToString()
    });
    context.SaveChanges();

when saving the changes, i get the exception保存更改时,我得到了异常

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
 ---> System.InvalidCastException: Cannot write DateTime with Kind=Local 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. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.
   at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateAndGetLength(DateTime value, NpgsqlParameter parameter)
   at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateObjectAndGetLength(Object value, NpgsqlLengthCache& lengthCache, NpgsqlParameter parameter)
   at Npgsql.NpgsqlParameter.ValidateAndGetLength()
   at Npgsql.NpgsqlParameterCollection.ValidateAndBind(ConnectorTypeMapper typeMapper)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)

Looks very contradicting because it is set without timezone and when the Msg is created is instantiates the "Time" property with the local time zone kind.看起来很矛盾,因为它没有设置时区,并且在创建 Msg 时使用本地时区类型实例化“时间”属性。 If i switch it to "DateTime.UtcNow" instead of "DateTime.Now", it works.如果我将它切换到“DateTime.UtcNow”而不是“DateTime.Now”,它可以工作。 I don't get why it throws exception when i set it to "timestamp without time zone".当我将其设置为“没有时区的时间戳”时,我不明白为什么它会引发异常。 obviously an easy solution is to just use utc time but regardless, why is the exception being thrown knowing the details?显然,一个简单的解决方案是只使用 UTC 时间,但无论如何,为什么知道细节会抛出异常?

npgsql 6.0 breaking changes npgsql 6.0 重大更改

UTC timestamps have been cleanly separated from non-UTC timestamps, aligning with the PostgreSQL types. UTC 时间戳已与非 UTC 时间戳完全分开,与 PostgreSQL 类型保持一致。 The former are represented by timestamp with time zone and DateTime with Kind UTC, the latter by timestamp without time zone and DateTime with Kind Local or Unspecified.前者用带时区的时间戳和带 UTC 种类的 DateTime 表示,后者用不带时区的时间戳和带本地或未指定种类的 DateTime 表示。 It is recommended to use UTC timestamps where possible.建议尽可能使用 UTC 时间戳。

Solution 1: enable to the pre-6.0 behavior解决方案 1:启用 6.0 之前的行为

MessageContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

Solution 2: use DateTimeOffset解决方案 2:使用DateTimeOffset

public DateTime Time { get; set; } = DateTimeOffset.Now;

Excellent article for DateTimeOffset vs DateTime DateTimeOffset vs DateTime 的优秀文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM