Npgsql GetValue 读取 regproc、anyarray 报不支持 System.Object
查询系统目录时,pgAdmin 能显示的列不一定能被 Npgsql 默认二进制映射读取。区分 regproc 与伪类型 anyarray,按列保留文本结果或明确 SQL 类型。
查询 PostgreSQL 系统目录时,NpgsqlDataReader.GetValue() 可能抛出这样的错误:
Reading as 'System.Object' is not supported for fields having DataTypeName 'regproc'
遇到 anyarray 时,类型名还可能显示为 -.-。同一条查询在数据库管理工具里能显示,并不能证明 Npgsql 已有对应的 .NET 二进制映射。
Npgsql 默认采用强类型二进制结果,需要知道具体类型如何解码。管理工具常用文本格式,拿到可显示的字符串即可,两条读取路径的要求不同。
先看查询实际返回什么类型
regproc 表示 PostgreSQL 函数的 OID 别名;anyarray 则是伪类型,通常用于函数参数或返回值声明,不是一个确定的 integer[] 或 text[]。系统目录中出现它,不代表可以把任意应用表的列声明成 anyarray。
如果程序只负责展示目录值,可在 SQL 投影中转成文本。例如下面的只读查询返回函数名称文本:
SELECT 'now'::regproc::text AS function_name;
对已有目录查询中的数组值,也可以在明确的投影位置使用 ::text。这样得到的是 PostgreSQL 文本表示,不是 .NET 数组。不要再把它强转为 object[] 或用逗号直接拆分;数组元素本身可以包含逗号、引号和转义字符。
如果后续确实需要数组运算,应先确定真实元素类型,再选择有证据支持的具体类型转换。不能把所有 anyarray 都强制转成 text[],也不要为读取目录而扩大数据库权限。
保留原查询时,只把必要列请求为文本
Npgsql 提供 UnknownResultTypeList,允许按结果列指定文本读取。下面示例只有一个结果列,因此数组长度也必须是1:
await using var cmd = new NpgsqlCommand(
"SELECT 'now'::regproc AS function_id", connection);
cmd.UnknownResultTypeList = new[] { true };
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
string functionName = reader.GetString(0);
}
这里假设 connection 已用原有认证和 TLS 配置打开,cancellationToken 来自调用方。true 对应的列以字符串读取,其他列仍可维持原有类型。数组长度必须与结果列数完全一致;加减投影列时要一起更新。
多条 SQL 拼在同一个命令里时,该属性只作用于第一条查询的结果,后续结果仍按通常方式解析。若需要覆盖每个查询,应明确拆分读取边界,不能以为一次设置自动覆盖全部结果集。
不把不同版本的映射混为一谈
10.0.3 的具体问题包含缺失的 reg* 映射和没有通用解码映射的 anyarray。面向11的 reg* 映射变更不等于所有伪类型都已支持,也不能把预发布代码当作10.0.3的已发布修复。
调整后分别检查非空值、NULL、包含转义字符的数组文本,以及后续业务实际需要的 .NET 类型。能显示一行只是展示路径通过;如果调用方仍要求强类型数组,还需要完成相应的类型适配。


