DateTime.UtcNow 由 .net entity framework / postgresql 自动转换为本地

DateTime.UtcNow is auto converted to local by .net entity framework / postgresql

提问人:Aamer 提问时间:8/3/2023 更新时间:8/4/2023 访问量:434

问:

我使用将所有 DateTimes 作为 UTC 存储在数据库中。它们以这种格式存储得很好: 但是,当我使用 Linq 命令从数据库中检索数据时,例如:. 我以这样的本地格式获取日期:这是我的当地时间。DateTime.UtcNow2023-08-03 13:54:15.474570 +00:00await _context.SomeEntity.ToListAsync()2023-08-03T15:54:15.47457+02:00

最重要的是,当实体更新(除日期时间以外的其他值)时,新的错误日期时间将存储回数据库中。

我正在使用以下版本:

.Net 7.0

Postgres 软件包 7.0.4

已经尝试解决问题:

1-将所有属性从DateTime更改为DateTimeOffset。

2-申请。UseNodaTime() 到 db 选项,如下所示: .options.UseNpgsql(configuration.GetConnectionString("DefaultConnection"), x => x.UseNodaTime());

3- 在保存值之前指定种类。我想我必须在保存之前获取值时这样做,但这是一种不可能的解决方案,适用于我从数据库获取值的任何地方。DateTime.SpecifyKind(dt, DateTimeKind.Utc)

c# asp.net postgresql datetime entity-framework-core

评论

2赞 Adrian Klaver 8/3/2023
Postgres 中时间戳字段的数据类型是什么?
2赞 JonasH 8/3/2023
我的猜测是您的列类型是 ,并且 npgsql 会自动将此类列类型的 UTC DateTimes 转换为本地。timestamp [ (p) ] with time zone
0赞 Aamer 8/3/2023
完全正确,我进入数据库并将列类型从(带时区的时间戳)更改为无时区,从而解决了问题。如何从dbcontext中的模型构建器中执行此操作的任何建议?
2赞 Adrian Klaver 8/3/2023
您现在创建了另一个问题,即未锚定的时间戳。读取时间戳select '2023-08-03 13:54:15.474570 +00:00'::timestamp; 2023-08-03 13:54:15.47457
0赞 Matteo1010 8/4/2023
如果选择current_time结果是否正确?

答:

-1赞 Krishan 8/4/2023 #1

您遇到 Entity Framework Core 问题,默认情况下,除非指定为 UTC,否则它会将 DateTime 对象更改为系统的本地时间。

您可以通过两种方式解决此问题:

  1. 重写 DbContext 中的 SaveChanges:重写 DbContext 中的方法允许您在保存所有 DateTime 对象之前手动将其转换为 UTC。此方法可确保所有 DateTime 属性都保存在 UTC 中。SaveChangesSaveChangesAsync
public override int SaveChanges()
{
    UpdateDates();
    return base.SaveChanges();
}

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
    UpdateDates();
    return base.SaveChangesAsync(cancellationToken);
}

private void UpdateDates()
{
    var entities = ChangeTracker.Entries()
    .Where(e => e.Entity is SomeEntity && 
        (e.State == EntityState.Added || e.State == EntityState.Modified));

    foreach (var entity in entities)
    {
        if (entity.State == EntityState.Added)
        {
            ((SomeEntity)entity.Entity).CreationDate = DateTime.UtcNow;
        }

        ((SomeEntity)entity.Entity).ModificationDate = DateTime.UtcNow;
    }
}
  1. 在 EF Core 中使用值转换:通过值转换,可以指定 EF Core 应如何在提供程序类型和 .NET 类型之间进行转换。可以对所有实体的特定属性或所有 DateTime 属性执行此操作。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        var dateTimeProperties = entityType.GetProperties()
            .Where(p => p.ClrType == typeof(DateTime) || p.ClrType == typeof(DateTime?));

        foreach (var property in dateTimeProperties)
        {
            modelBuilder.Entity(entityType.Name).Property(property.Name)
                .HasConversion(
                    v => v, 
                    v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
        }
    }
}

这两种方法都确保将 DateTime 值读取为 UTC。如果您处理不同时区的日期,则可以考虑将它们存储为 DateTimeOffset。

评论

0赞 Gert Arnold 8/4/2023
我对此投了反对票,因为第一句话不是真的。至少对于 EF Core 来说不是。它似乎特定于 postrgreSql 查询提供程序。最好添加它,以免偏离其他 EF 用户。另外,第一个例子中的“转换”是什么?