.Net6下Dapper+Npgsql映射PostgreSQL枚举报类型不匹配错误
.NET 6 + Dapper + Npgsql 枚举映射失效问题排查
问题场景
- 技术栈:ASP.Net API(.Net 6)、Dapper 2.0.123、Dapper.Contrib 2.0.78、Npgsql 6.0.4
- 数据库结构:PostgreSQL中存在自定义枚举类型
campaigns_status_enum,表campaigns的status列使用该枚举类型,类型定义如下:
CREATE TYPE public.campaigns_status_enum AS ENUM ('active', 'inactive', 'draft');
- 项目实体定义:
[Table("campaigns")] public class Campaign { [Key] public int id { get; set; } // 其他无关字段省略 public ECampaignStatus status { get; set; } public enum ECampaignStatus { [NpgsqlTypes.PgName("active")] active, [NpgsqlTypes.PgName("inactive")] inactive, [NpgsqlTypes.PgName("draft")] draft } }
- 已做配置:
- 在
Startup.cs的ConfigureServices中、注入数据库服务前添加Npgsql枚举映射:
NpgsqlConnection.GlobalTypeMapper.MapEnum<Common.Campaigns.Campaign.ECampaignStatus>("campaigns_status_enum");- 数据库连接打开后调用
ReloadTypes()重载类型:
IDbConnection db = new NpgsqlConnection(ConnectionString); if (db.State != ConnectionState.Open) { db.Open(); ((NpgsqlConnection)db).ReloadTypes(); } - 在
- 报错现象:调用Dapper.Contrib的
UpdateAsync、InsertAsync时抛出异常:
42804: column "status" is of type campaigns_status_enum but expression is of type integer POSITION: 172
错误原因与修复方案
你的配置存在3个核心问题,按优先级修复即可:
- 枚举映射注册时机错误
Npgsql 6.x版本要求全局类型映射必须在第一个Npgsql连接实例被创建之前执行,你把MapEnum放在ConfigureServices阶段执行,很可能已经晚于Npgsql内部初始化类型映射的时机,导致配置根本不生效。
修复方式:把
MapEnum逻辑移到项目最启动入口,也就是Program.cs的最顶部(var builder = WebApplication.CreateBuilder(args);之前),确保早于所有服务注册、连接实例创建的逻辑。
- 缺少Dapper层的枚举类型处理器
Npgsql本身的类型映射只负责底层参数值的转换,但Dapper默认会把所有枚举类型按整数类型序列化传参,根本不会走到Npgsql的枚举映射逻辑,这也是报错提示expression is of type integer的直接原因。
修复方式:为目标枚举添加Dapper全局TypeHandler,替换Dapper默认的枚举序列化逻辑,代码如下:
public class CampaignStatusTypeHandler : SqlMapper.TypeHandler<Campaign.ECampaignStatus> { public override void SetValue(IDbDataParameter parameter, Campaign.ECampaignStatus value) { parameter.Value = value; // 标记参数为自定义类型,让Npgsql按枚举映射规则处理 ((NpgsqlParameter)parameter).NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Unknown; } public override Campaign.ECampaignStatus Parse(object value) { return Enum.Parse<Campaign.ECampaignStatus>(value.ToString()!); } } // 程序启动时和Npgsql映射一起注册 SqlMapper.AddTypeHandler(new CampaignStatusTypeHandler());
注意:使用Dapper.Contrib做自动CRUD时无法单独给参数指定类型,必须通过全局TypeHandler的方式处理。
- 多余的ReloadTypes()调用
只要在首次创建连接前完成所有枚举映射注册,Npgsql会在第一次建立连接时自动加载所有自定义类型,不需要每次打开连接都调用ReloadTypes(),重复调用反而可能导致类型缓存被覆盖,把这段逻辑删掉即可。
可选检查项
如果修复以上问题后依然报错,确认两点:
- 枚举成员上的
PgName特性值和数据库枚举值完全一致,PostgreSQL枚举值区分大小写 - 如果自定义枚举没有放在
publicschema下,MapEnum时要写全类型名,例如"myschema.campaigns_status_enum"
内容的提问来源于stack exchange,提问作者nighthawk
相关产品推荐
相关产品推荐

