Closed BenjiFarquhar closed 2 years ago
@BenjaminFarquhar PostgreSQL 14 introduced a new multirange type, which is very similar to an array of ranges but supports various range-related operations efficiently. Npgsql 6 maps List<NpgsqlRange<T>>
to these new types by default - that indeed doesn't work on PG versions before 13.
You can explicitly tell the provider to map to old-style arrays over ranges, e.g. via [Column(TypeName="tzrange[]")]
, but if upgrading to PG14 is option, I'd suggest doing that instead and switching to the new multirange type.
I'll add a breaking change note in the release notes.
@BenjaminFarquhar PostgreSQL 14 introduced a new multirange type, which is very similar to an array of ranges but supports various range-related operations efficiently. Npgsql 6 maps
List<NpgsqlRange<T>>
to these new types by default - that indeed doesn't work on PG versions before 13.You can explicitly tell the provider to map to old-style arrays over ranges, e.g. via
[Column(TypeName="tzrange[]")]
, but if upgrading to PG14 is option, I'd suggest doing that instead and switching to the new multirange type.I'll add a breaking change note in the release notes.
@roji Thanks, that's good to know. I updated to PostgreSQL 14 and that made it work!
Great! Let me know if you run into any other trouble!
Great! Let me know if you run into any other trouble!
@roji Actually, I'm getting this:
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.
When I try to save this field to the db: public List<NpgsqlRange<System.DateTime>> Dates { get; set; }
I think the bottom of the release notes talk about this.
What is the fix? It mentions to do: [Column(TypeName = "int4range[]")]
(I assume translating my code to that would actually be [Column(TypeName = "daterange[]")]
?) but it kinda sounds like it is saying that that is how you force it to use the old postgreSQL way of doing it?
The problem code may be here where I map the dto to the db entity type:
CreateMap<EventItemDto, EventItem>()
.ForMember(dest => dest.Dates,
opt => opt.MapFrom(src => src.Dates.Select(
date => new NpgsqlRange<System.DateTime>(
date.StartDate,
date.EndDate
)
)
.ToList()));
Okay wait the solution was to add the time zone, makes sense:
CreateMap<EventItemDto, EventItem>()
.ForMember(dest => dest.Dates,
opt => opt.MapFrom(src => src.Dates.Select(
date => new NpgsqlRange<System.DateTime>(
date.StartDate.ToUniversalTime(),
date.EndDate.ToUniversalTime()
)
)
.ToList()));
Yeah. Npgsql 6 clearly enforces the difference between PostgreSQL timestamptz
(which is a UTC timestamp) and timestamp
which is a timestamp in an unspecified/unknown time zone). If your intention is to store UTC timestamps in the database, you should be using the tstzmultirange
type (multirange over timestamptz
), and then you must make sure that all DateTimes you write have Kind=Utc; this is why ToUniversalTime makes things work.
I recommend giving this blog post a read, and possibly also this one, in case you're interested in non-UTC timestamps (it's important to think about why you're choosing UTC or not UTC, and not just do UTC without knowing why). I also really recommend giving NodaTime a look - this is a far better way of working with timestamps and date/time data in .NET.
I have this database field:
public List<NpgsqlRange<System.DateTime>> Dates { get; set; }
But today I have upgraded EF Core from 5 to 6.
Which caused an error to appear about the above field:
42704: type "tstzmultirange" does not exist
I just confirmed that downgrading EF Core back to 5 from 6 completely fixes this issue. So I guess EF Core 6 has a bug for this specific field type.
Full error: