![](/img/trans.png)
[英].net postgres EF core Cannot write DateTime with Kind=Local to PostgreSQL type 'timestamp with time zone'
[英].NET6 and DateTime problem. Cannot write DateTime with Kind=UTC to PostgreSQL type 'timestamp without time zone'
我有一個共同的問題。
無法將 Kind=UTC 的 DateTime 寫入 PostgreSQL 類型“沒有時區的時間戳”
我想啟用 Legacy Timestamp 行為,如下所述: https://github.com/npgsql/doc/blob/main/conceptual/Npgsql/types/datetime.md/
public MyDbContext(DbContextOptions<MyDbContext> contextOptions) : base(contextOptions)
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
}
但不起作用。 我仍然得到同樣的錯誤。
我做錯了什么。 為什么遺留行為不起作用?
一個。 通過添加解決
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
到Startup
Configure
方法。
乙。 或者,如果您根本沒有 Startup 類,並且您的所有初始化都在帶有主機構建器的 Program.cs 中,那么您的文件結尾可能如下所示:
... //adding services etc
var host = builder.Build();
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
... //your other scoped code
await host.RunAsync();
要使用System.Linq.Dynamic
查詢數據庫,我們還需要指定時間類型。
過濾器示例: $"User.BirthDate>={time.ToStringUtc()}"
public static string ToStringUtc(this DateTime time)
{
return $"DateTime({time.Ticks}, DateTimeKind.Utc)";
}
同時,@istvan-kardkovacs 的答案https://stackoverflow.com/a/70142836/7149454適用。 基本上是添加一個 . SetKindUtc()
to every = new DateTime()
您正在創建.. 在執行任何其他代碼之前,在填充數據庫的后台托管服務中,上面的開關顯然對我不起作用。
您必須為創建、插入、更新操作中的所有 DateTime 字段以及 Linq 查詢中的 DateTime 比較設置 DateTimeKind。 我創建了一個小型擴展方法並添加到所有日期字段。
public static class DateTimeExtensions
{
public static DateTime? SetKindUtc(this DateTime? dateTime)
{
if (dateTime.HasValue)
{
return dateTime.Value.SetKindUtc();
}
else
{
return null;
}
}
public static DateTime SetKindUtc(this DateTime dateTime)
{
if (dateTime.Kind == DateTimeKind.Utc) { return dateTime; }
return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
}
}
並進行單元測試以顯示功能:
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyNamespace;
[TestClass]
[ExcludeFromCodeCoverage]
public class DateTimeExtensionsTests
{
[TestMethod]
public void SetKindUtcNullInputTest()
{
DateTime? input = null;
DateTime? result = input.SetKindUtc();
Assert.IsNull(result);
}
[TestMethod]
public void SetKindUtcNonNullRegularDateInputTest()
{
DateTime? input = DateTime.Now;
DateTime? result = input.SetKindUtc();
Assert.IsNotNull(result);
/* below is the primary functionality. if the input did not have a "Kind" set, it gets set to DateTimeKind.Utc */
Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind);
}
[TestMethod]
public void SetKindUtcNonNullOffsetDateInputTest()
{
DateTime? input = DateTime.Now;
DateTime withKindUtcInput = DateTime.SpecifyKind(input.Value, DateTimeKind.Utc);
DateTime? result = withKindUtcInput.SetKindUtc();
Assert.IsNotNull(result);
/* Utc "in" remains "Utc" out */
Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind);
}
[TestMethod]
public void UnspecifiedKindIsOverwrittenTest()
{
DateTime? input = DateTime.Now;
DateTime withKindUtcInput = DateTime.SpecifyKind(input.Value, DateTimeKind.Unspecified);
DateTime? result = withKindUtcInput.SetKindUtc();
Assert.IsNotNull(result);
/* note the behavior. "DateTimeKind.Unspecified" with overwritten with DateTimeKind.Utc */
Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind);
}
[TestMethod]
public void LocalKindIsOverwrittenTest()
{
DateTime? input = DateTime.Now;
DateTime withKindUtcInput = DateTime.SpecifyKind(input.Value, DateTimeKind.Local);
DateTime? result = withKindUtcInput.SetKindUtc();
Assert.IsNotNull(result);
/* note the behavior. "DateTimeKind.Local" with overwritten with DateTimeKind.Utc */
Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind);
}
}
我在我的DbContext
中添加了代碼,以便在我的模型上的所有日期屬性上進行設置:
//dbcontext
public override int SaveChanges()
{
_changeTrackerManager?.FixupEntities(this);
return base.SaveChanges();
}
//don't forget the async method!
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
_changeTrackerManager?.FixupEntities(this);
return base.SaveChangesAsync();
}
將注入此IChangeTrackerManager
依賴項,然后在保存實體時,它將在下面調用此方法,該方法將修復所有 utc 日期時間類型。
public void FixupEntities(DbContext context)
{
var dateProperties = context.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(DateTime))
.Select(z => new
{
ParentName = z.DeclaringEntityType.Name,
PropertyName = z.Name
});
var editedEntitiesInTheDbContextGraph = context.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
.Select(x => x.Entity);
foreach (var entity in editedEntitiesInTheDbContextGraph)
{
var entityFields = dateProperties.Where(d => d.ParentName == entity.GetType().FullName);
foreach (var property in entityFields)
{
var prop = entity.GetType().GetProperty(property.PropertyName);
if (prop == null)
continue;
var originalValue = prop.GetValue(entity) as DateTime?;
if (originalValue == null)
continue;
prop.SetValue(entity, DateTime.SpecifyKind(originalValue.Value, DateTimeKind.Utc));
}
}
}
從@DLeh 稍微修改https://stackoverflow.com/a/71179214/507421
private void ConvertDateTimesToUniversalTime()
{
var modifiedEntites = ChangeTracker.Entries<IHaveAggregateRootId>()
.Where(e => (e.State == EntityState.Added || e.State == EntityState.Modified || e.State == EntityState.Deleted)).ToList();
foreach (var entry in modifiedEntites)
{
foreach (var prop in entry.Properties)
{
if (prop.Metadata.ClrType == typeof(DateTime))
{
prop.Metadata.FieldInfo.SetValue(entry.Entity, DateTime.SpecifyKind((DateTime)prop.CurrentValue, DateTimeKind.Utc));
}
else if (prop.Metadata.ClrType == typeof(DateTime?) && prop.CurrentValue != null)
{
prop.Metadata.FieldInfo.SetValue(entry.Entity, DateTime.SpecifyKind(((DateTime?)prop.CurrentValue).Value, DateTimeKind.Utc));
}
}
}
}
尼克已經回答了這個問題,我只是想為這個時區問題添加另一個解決方案。
您可以在使用此擴展程序編寫之前轉換所有日期時間,而不是啟用該選項。 這就是我所做的。
創建這個擴展類:
public static class UtcDateAnnotation
{
private const string IsUtcAnnotation = "IsUtc";
private static readonly ValueConverter<DateTime, DateTime> UtcConverter = new ValueConverter<DateTime, DateTime>(convertTo => DateTime.SpecifyKind(convertTo, DateTimeKind.Utc), convertFrom => convertFrom);
public static PropertyBuilder<TProperty> IsUtc<TProperty>(this PropertyBuilder<TProperty> builder, bool isUtc = true) => builder.HasAnnotation(IsUtcAnnotation, isUtc);
public static bool IsUtc(this IMutableProperty property)
{
if (property != null && property.PropertyInfo != null)
{
var attribute = property.PropertyInfo.GetCustomAttribute<IsUtcAttribute>();
if (attribute is not null && attribute.IsUtc)
{
return true;
}
return ((bool?)property.FindAnnotation(IsUtcAnnotation)?.Value) ?? true;
}
return true;
}
/// <summary>
/// Make sure this is called after configuring all your entities.
/// </summary>
public static void ApplyUtcDateTimeConverter(this ModelBuilder builder)
{
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (!property.IsUtc())
{
continue;
}
if (property.ClrType == typeof(DateTime) ||
property.ClrType == typeof(DateTime?))
{
property.SetValueConverter(UtcConverter);
}
}
}
}
}
public class IsUtcAttribute : Attribute
{
public IsUtcAttribute(bool isUtc = true) => this.IsUtc = isUtc;
public bool IsUtc { get; }
}
並將該轉換器添加到您的 DbContext 文件中:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyUtcDateTimeConverter();//Put before seed data and after model creation
}
這將導致您所有的 DateTime 和 DateTime? 對象在寫入 Db 之前已轉換為 Utc 類型的日期。
這將是我支持這個 PostgreSql Db 的一種方式,因為我需要支持一些數據庫(Sql Server、PostgreSql 以及很快的 MySql)。 手動將每個日期時間值轉換為 Utc 並不是一個好的解決方案。
我們的應用程序還沒有對時區的要求,但是使用該擴展,我們可以輕松地在其中添加時區支持。
當我的控制器反序列化對象並且我嘗試使用 EF 和 Npgsql.EntityFrameworkCore.PostgreSQL 插入/更新它時,我也發生了同樣的事情。 我對所有日期都使用了 ToUniversalTime(),它對我有用。
我找到了答案。 不要將這些行添加到您的 dB 上下文中。 而是在 WFP 應用程序中添加到 MainWindow.xaml.cs,如下所示:
在公共 MainWindow 方法中的 InitializeComponent 語句之前添加“EnableLegacyTimestampBehavior”行。
您不需要“DisableDateTimeInfinityConversions”語句。
您的 DateTime 代碼現在可以工作了。
你需要添加
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
就我而言,這是我犯的一個錯誤
InvitedOn = DateTime.Now
本來應該
InvitedOn = DateTime.UtcNow
它奏效了
對我來說,我已經用 dotnet ef migrations add 更新了項目
命令參考:
dotnet ef migrations add <MigrationName> --context <YoutContextClassName> --startup-project <../Path to your startup proyect> --verbose
在它 postgre 創建遷移文件之后,將所有日期時間從“帶時區”更改為“不帶時區”
我希望它對你有用
注釋:
更新:
另外或之后您需要檢查 DateTime 的種類,如果不是 Utc,您可以使用 DateTime 的靜態 SpecifyKind 方法強制它
問候
我在“沒有時區的時間戳”列中嘗試使用 Kind = UTC (我實際上正在使用DateTime.UtcNow
)堅持 DateTime 時也偶然發現了這個錯誤。 IMO,該錯誤沒有意義,因為 UTC 日期時間應該是
我的解決方法是切換到“帶時區的時間戳”,因為:
也許有點晚了,但對我來說,我剛剛創建了這個轉換器
public class DateTimeToDateTimeUtc : ValueConverter<DateTime, DateTime>
{
public DateTimeToDateTimeUtc() : base(c => DateTime.SpecifyKind(c, DateTimeKind.Utc), c => c)
{
}
}
protected sealed override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<DateTime>()
.HaveConversion(typeof(DateTimeToDateTimeUtc));
}
放置設置的好地方是 DB Context 的 static 構造函數。
在這種情況下,啟動 class 保持清潔。
如果您有多個項目使用相同的數據庫上下文,這也很有用。
例如:
public class MyContext : DbContext
{
static MyContext()
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
// Other stuff of your context
}
我有一個類似的問題,為了解決這個問題,我在 C# 中使用了 DateTime.ToUniversalTime() 方法。
例如,
Date= DateTime.Now.ToUniversalTime();
或者,
DateTime dateTime = DateTime.Now.ToUniversalTime();
要獲得更多詳細信息,您可以瀏覽此站點https://www.geeksforgeeks.org/datetime-touniversaltime-method-in-c-sharp/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.