Npgsql 读取空二维数组报 InvalidCastException,非空数据却正常
Npgsql 9.0.5、10.0.3 读取 PostgreSQL 空数组可能丢失 CLR 维数。保留 NULL 与空矩阵差别,在读取边界单独恢复零元素二维值。
同一列里的非空矩阵能读,遇到空数组才报下面的异常,优先检查 Npgsql 是否把空值构造成了错误维数的 CLR 数组:
System.InvalidCastException: Unable to cast object of type 'System.Double[]' to type 'System.Double[,]'.
这条问题在 Npgsql 9.0.0、9.0.5 和 10.0.3 的空多维数组读取中有明确表现;8.0.9 的相同读取能够保留请求的维数。它不要求使用 EF Core,也不需要配置值转换器,直接调用 GetFieldValue<double[,]>() 就可能触发。
空数组没有带回原来的二维形状
PostgreSQL 的空数组按零维数组传输。Npgsql 10.0.3 的数组转换器根据服务器返回的维数创建集合;维数为零时得到 Array.Empty<double>(),随后再转成调用方要求的 double[,] 就会失败。非空的二维值带有维度信息,因此同一个调用可以正常返回。
SQL NULL、零元素数组、包含空值的数组是三种情况。不要把所有读取异常统一替换成空矩阵,否则会掩盖真实类型错误,也会丢掉数据库原有的空值含义。
只在读取边界处理零元素结果
如果当前查询需要继续兼容受影响的正式版本,可以在应用已经确认是 double precision[]、业务目标为二维数值矩阵的读取位置,使用普通对象读取,再区分实际返回类型。以下为读取辅助方法,reader 已位于有效行,ordinal 指向目标列:
static double[,]? ReadMatrix(Npgsql.NpgsqlDataReader reader, int ordinal)
{
if (reader.IsDBNull(ordinal))
return null;
return reader.GetValue(ordinal) switch
{
double[,] matrix => matrix,
double[] { Length: 0 } => new double[0, 0],
_ => throw new InvalidOperationException(
"Expected a two-dimensional double matrix or an empty array.")
};
}
该片段采用启用了可空引用类型的 C# 项目语法。它只把驱动返回的零元素一维数组恢复为 0×0 的二维数组;非空的一维值仍被拒绝,不能擅自把它变成一行。普通二维数据保持原值,SQL NULL 仍返回 null。
这里约定的是元素为普通非空 double 的列。若数组允许 NULL 元素,或应用注册了自定义类型映射,应另行按实际返回类型处理,不能把上述转换直接复制到 double?[,] 或其他元素类型上。
EF Core 的自动物化如果在到达业务代码前就抛异常,这个辅助方法不会自动拦住它。需要为该查询采用显式 ADO.NET 读取边界,或等待并核对匹配的驱动与提供程序修正;不要只在实体属性的 getter 里补一个空数组分支。
不把主分支修正当作正式版已修复
截至 2026 年 10 月 23 日,已核的正式 10.x 发布仍到 10.0.3。主分支已经有保留空数组请求维数的修正,但这不代表正在部署的 9.x、10.x 包已包含它。后续升级应核对对应正式版本及实际解析依赖,不直接混入下一主版本的驱动来绕开问题。
在隔离环境覆盖 SQL NULL、空数组、非空二维数组和意外的一维数组,确认空值语义、维数与元素顺序都符合业务契约。不要为了消除一次转换异常改写表中全部空数组;那会改变数据含义,且代码回退不能自动恢复。


