在EF Core的PostgreSQL中设置仅允许Kind.Unspecified的DateTime列
我定义了如下数据库列:
[MaxLength(10)] [Required] public List<NpgsqlRange<System.DateTime>> Dates { get; set; }
如何设置使其仅允许Kind.Unspecified类型?插入数据时我遇到以下错误:
System.InvalidCastException : 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. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.
我看到的所有解决方案都是使用Npgsql.EnableLegacyTimestampBehavior,但这是让UTC列允许未指定类型,我并不需要这个,而是希望该列强类型为未指定类型。另外,当我启用EnableLegacyTimestampBehavior时,数据会以带"+12"的UTC格式存储到PostgreSQL中,这不是我想要的。
解决方案
1. 显式指定PostgreSQL列类型为不带时区的范围数组
问题根源是Npgsql默认将List<NpgsqlRange<DateTime>>映射为PostgreSQL的tstzrange[](带时区的时间范围数组),这种类型要求DateTime必须是UTC。我们需要手动指定列类型为tsrange[](不带时区的时间范围数组),对应.NET的DateTimeKind.Unspecified。
在DbContext的OnModelCreating方法中配置:
modelBuilder.Entity<你的实体类>() .Property(e => e.Dates) .HasColumnType("tsrange[]") // 指定不带时区的时间范围数组类型 .HasConversion( // 写入时确保DateTime为Unspecified v => v.Select(r => new NpgsqlRange<DateTime>( r.Start.HasValue ? DateTime.SpecifyKind(r.Start.Value, DateTimeKind.Unspecified) : null, r.End.HasValue ? DateTime.SpecifyKind(r.End.Value, DateTimeKind.Unspecified) : null )).ToList(), // 读取时确保返回的DateTime为Unspecified v => v.Select(r => new NpgsqlRange<DateTime>( r.Start.HasValue ? DateTime.SpecifyKind(r.Start.Value, DateTimeKind.Unspecified) : null, r.End.HasValue ? DateTime.SpecifyKind(r.End.Value, DateTimeKind.Unspecified) : null )).ToList() ) .IsRequired() .HasMaxLength(10);
2. 在实体层添加类型验证(可选)
为了避免意外传入非Unspecified类型的DateTime,可以在属性的setter中添加验证逻辑:
private List<NpgsqlRange<DateTime>> _dates; [MaxLength(10)] [Required] public List<NpgsqlRange<DateTime>> Dates { get => _dates; set { if (value == null) throw new ArgumentNullException(nameof(Dates)); foreach (var range in value) { if (range.Start.HasValue && range.Start.Value.Kind != DateTimeKind.Unspecified) throw new ArgumentException("日期范围的起始时间必须为Unspecified类型"); if (range.End.HasValue && range.End.Value.Kind != DateTimeKind.Unspecified) throw new ArgumentException("日期范围的结束时间必须为Unspecified类型"); } _dates = value; } }
3. 避免全局强制UTC配置
确保没有在项目中设置全局强制UTC的Npgsql配置,比如:
// 不要添加这行代码 // NpgsqlConnection.GlobalTypeMapper.MapDateTime(DateTimeKind.Utc);
内容的提问来源于stack exchange,提问作者BeniaminoBaggins

