如何为自定义 Postgresql 范围类型创建类型映射?

How create type mapping for custom Postgresql range type?

我在项目中使用 Dapper 和 PostgreSQL。我的架构中有一些自定义枚举、复合和范围类型,例如:

create type timerange as range (
    subtype = time,
    subtype_diff = time_subtype_diff
);

并且我在 table myproject.working_time_intervals 中使用了这个范围,它与模型 class WorkingInterval:

相关联
create table working_time_intervals
(
    id              serial                   not null
        constraint working_time_intervals_pk
            primary key,
    day_of_week     myproject.day_of_week not null,
    begin_end_range myproject.timerange
);
public class WorkingInterval
{
    public int Id { get; set; }
    public DayOfWeek DayOfWeek { get; set; }
    public NpgsqlRange<TimeSpan> BeginEndRange { get; set; }
}

此外,当我尝试使用此 class 并在数据库中插入数据并获得结果时,我发现了异常

var result = await connection.QueryAsync<WorkingInterval>(
    @"insert into myproject.working_time_intervals(day_of_week, begin_end_range) 
      select unnest(@DayOfWeeks), myproject.timerange(unnest(@Begins)::time, unnest(@Ends)::time, '[]')
      returning *",
      new
      {
           DayOfWeeks = intervals.Select(i => i.DayOfWeek).ToArray(),
           Begins = intervals.Select(i => i.Begin).ToArray(),
           Ends = intervals.Select(i => i.End).ToArray()
      });
System.NotSupportedException: The CLR array type NpgsqlTypes.NpgsqlRange`1[System.TimeSpan][] isn't supported by Npgsql or your PostgreSQL. If you wish to map it to an  PostgreSQL composite type array you need to register it before usage, please refer to the documentation.\r\n   at Npgsql.TypeMapping.ConnectorTypeMapper.GetByClrType(Type type)\r\n   at Npgsql.

如何为时间范围创建映射?

这里的问题是 .NET TimeSpan 默认情况下未映射到 PostgreSQL time - 它映射到 interval (see the docs table on mappings);这意味着默认情况下 NpgsqlRange<TimeSpan> 也不会映射到您的 PostgreSQL timerange

所有这些仍然可以工作,但是你需要明确地告诉 Npgsql 要发送哪种类型。在不使用 Dapper 的情况下,以下应该可以正常工作:

cmd = new NpgsqlCommand(@"INSERT INTO foo (range) VALUES (@range)", conn);
cmd.Parameters.AddWithValue("range", NpgsqlDbType.Range | NpgsqlDbType.Time, new NpgsqlRange<TimeSpan>(TimeSpan.FromHours(1), TimeSpan.FromHours(2)));
cmd.ExecuteNonQuery();

cmd = new NpgsqlCommand(@"INSERT INTO foo (ranges) VALUES (@ranges)", conn);
cmd.Parameters.AddWithValue("ranges", NpgsqlDbType.Range | NpgsqlDbType.Array | NpgsqlDbType.Time, new[]
{
    new NpgsqlRange<TimeSpan>(TimeSpan.FromHours(1), TimeSpan.FromHours(2)),
    new NpgsqlRange<TimeSpan>(TimeSpan.FromHours(3), TimeSpan.FromHours(4))
});
cmd.ExecuteNonQuery();

使用 Dapper,您需要进行一些自定义设置 NpgsqlDbType,