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

使用Npgsql创建PostgreSQL枚举类型后二进制导入失败,如何解决?

问题:Npgsql二进制导入PostgreSQL枚举类型失败

在.NET项目中使用Npgsql创建PostgreSQL枚举类型后执行二进制导入操作时,抛出System.ArgumentException,提示"A PostgreSQL type with the name mood was not found in the database",但数据库中已成功创建该枚举类型。

项目文件

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Npgsql" Version="7.0.2" />
  </ItemGroup>
</Project>

源码文件

using Npgsql;

var connectionString =
  "Host=localhost;" +
  "Port=5432;" +
  "Username=postgres;" +
  "Password=postgres;" +
  "Database=test_db;";

using var connection = new NpgsqlConnection(connectionString);

connection.Open();

var cmd = connection.CreateCommand();

cmd.CommandText =
  "create type mood as enum ('happy', 'sad');" +
  "create table people (age integer, cmood mood);";

cmd.ExecuteNonQuery();

var copyCmd =
  "copy people (age, cmood)" +
  "from stdin (format binary)";

using var writer = connection.BeginBinaryImport(copyCmd);

writer.StartRow();
writer.Write(12);
writer.Write(Mood.Happy);
writer.Complete();

enum Mood
{
  Null = 0,
  Sad = 1,
  Happy = 2
}

问题详情

本地PostgreSQL服务已运行,空数据库test_db已创建。执行程序时触发上述异常,但通过命令行查询数据库确认mood枚举类型已成功创建。


原因分析

Npgsql在建立连接后会缓存数据库的元数据(包括枚举类型),而代码是在连接打开后才创建的mood枚举类型,此时Npgsql的本地元数据缓存中并没有这个新类型的信息。二进制导入(BeginBinaryImport)依赖元数据来匹配类型编码规则,缓存未更新就会导致无法识别新枚举类型,抛出异常。

另外,当前.NET枚举值的顺序和PostgreSQL枚举定义顺序不匹配,会导致导入后数据值错误(比如.NET的Happy=2会对应PostgreSQL中第二个枚举值sad)。


解决方法

1. 刷新元数据缓存

在创建枚举和表之后,调用connection.ReloadTypes()刷新Npgsql的元数据缓存,让它感知到新创建的mood类型:

cmd.ExecuteNonQuery();
// 刷新元数据缓存
connection.ReloadTypes();

var copyCmd =
  "copy people (age, cmood)" +
  "from stdin (format binary)";
// 后续二进制导入代码不变

2. 修正.NET枚举与PostgreSQL枚举的映射

确保.NET枚举与PostgreSQL枚举的定义顺序或名称对应一致:

方式一:匹配枚举顺序(推荐)

PostgreSQL中mood的顺序是'happy'(第一个)、'sad'(第二个),对应.NET枚举的索引值:

enum Mood
{
  Happy = 0,
  Sad = 1
}

方式二:使用特性显式指定映射

如果不想调整枚举值,可通过PgName特性绑定枚举值与PostgreSQL枚举名称:

using NpgsqlTypes;

enum Mood
{
  [PgName(null)]
  Null = 0,
  [PgName("sad")]
  Sad = 1,
  [PgName("happy")]
  Happy = 2
}

3. 重新打开连接

如果不想调用ReloadTypes(),可关闭当前连接后重新打开,新连接会重新加载最新的元数据:

cmd.ExecuteNonQuery();
// 关闭并重新打开连接
connection.Close();
connection.Open();

var copyCmd =
  "copy people (age, cmood)" +
  "from stdin (format binary)";
// 后续二进制导入代码不变

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:35:07