You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Npgsql使用GROUP BY时array_agg列类型<unknown>的解决方法

解决PostgreSQL中array_agg返回类型的读取问题

遇到这种情况很正常,因为你用array_agg(e)聚合的是整个e_book表的行,PostgreSQL会把这个结果识别为自定义行类型数组(也就是e_book[]),但Npgsql默认没有映射这个数据库行类型到你的.NET对象,所以才会显示<unknown>。下面给你两种实用的解决方案:

方案1:用JSON聚合代替行类型聚合(最简单)

直接把聚合的行转成JSON数组,Npgsql对JSON类型的支持很友好,不需要额外配置。修改你的SQL语句:

select b.*, json_agg(e) as ebooks 
from book b 
left join e_book e on e.physical_book_ean = b.ean_number 
group by b.id

然后在读取数据的时候,你可以根据自己的JSON库选择方式:

  • 用System.Text.Json:
while (reader.Read())
{
    // 读取book字段
    var bookId = reader.GetInt32("id");
    var eanNumber = reader.GetString("ean_number");
    var title = reader.GetString("title");

    // 读取ebooks数组
    JsonElement[] ebooks = null;
    if (!reader.IsDBNull(reader.GetOrdinal("ebooks")))
    {
        ebooks = reader.GetFieldValue<JsonElement[]>("ebooks");
        // 遍历处理每个电子书
        foreach (var ebook in ebooks)
        {
            var ebookId = ebook.GetProperty("id").GetInt32();
            var format = ebook.GetProperty("format").GetString();
            // ...其他字段
        }
    }
}
  • 用Newtonsoft.Json:
while (reader.Read())
{
    // 读取book字段...

    JArray ebooks = null;
    if (!reader.IsDBNull(reader.GetOrdinal("ebooks")))
    {
        ebooks = JArray.Parse(reader.GetString("ebooks"));
        foreach (var ebook in ebooks)
        {
            var ebookId = ebook["id"].Value<int>();
            // ...处理其他字段
        }
    }
}

方案2:注册复合类型实现强类型读取(类型安全)

如果你希望直接映射到.NET的EBook[]数组,需要先让Npgsql识别PostgreSQL的e_book行类型。

步骤1:定义对应的.NET类

public class EBook
{
    public int Id { get; set; }
    public string Ean { get; set; }
    public string Title { get; set; }
    public string Format { get; set; }
    public string PhysicalBookEan { get; set; }
}

步骤2:注册复合类型

在打开数据库连接之前,注册这个类型映射:

// 全局注册,整个应用程序只需一次
NpgsqlConnection.GlobalTypeMapper.MapComposite<EBook>("e_book");

步骤3:读取数据

现在你可以直接读取强类型数组了:

while (reader.Read())
{
    // 读取book字段...

    EBook[] ebooks = null;
    var ebooksOrdinal = reader.GetOrdinal("ebooks");
    if (!reader.IsDBNull(ebooksOrdinal))
    {
        ebooks = reader.GetFieldValue<EBook[]>(ebooksOrdinal);
        // 直接使用强类型对象
        foreach (var ebook in ebooks)
        {
            Console.WriteLine($"EBook ID: {ebook.Id}, Format: {ebook.Format}");
        }
    }
}

两种方案对比

  • 方案1:无需额外配置,灵活度高,适合快速开发或者需要处理动态结构的场景。
  • 方案2:类型安全,编译时就能检查字段错误,适合大型项目或者强类型要求高的场景。

内容的提问来源于stack exchange,提问作者Guerrilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:52:14