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

.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
    }
}
  • 已做配置:
    1. 在Startup.cs的ConfigureServices中、注入数据库服务前添加Npgsql枚举映射:
    NpgsqlConnection.GlobalTypeMapper.MapEnum<Common.Campaigns.Campaign.ECampaignStatus>("campaigns_status_enum");
    
    1. 数据库连接打开后调用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个核心问题,按优先级修复即可:

  1. 枚举映射注册时机错误
    Npgsql 6.x版本要求全局类型映射必须在第一个Npgsql连接实例被创建之前执行,你把MapEnum放在ConfigureServices阶段执行,很可能已经晚于Npgsql内部初始化类型映射的时机,导致配置根本不生效。

修复方式:把MapEnum逻辑移到项目最启动入口,也就是Program.cs的最顶部(var builder = WebApplication.CreateBuilder(args);之前),确保早于所有服务注册、连接实例创建的逻辑。

  1. 缺少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的方式处理。

  1. 多余的ReloadTypes()调用
    只要在首次创建连接前完成所有枚举映射注册,Npgsql会在第一次建立连接时自动加载所有自定义类型,不需要每次打开连接都调用ReloadTypes(),重复调用反而可能导致类型缓存被覆盖,把这段逻辑删掉即可。

可选检查项

如果修复以上问题后依然报错,确认两点:

  • 枚举成员上的PgName特性值和数据库枚举值完全一致,PostgreSQL枚举值区分大小写
  • 如果自定义枚举没有放在public schema下,MapEnum时要写全类型名,例如"myschema.campaigns_status_enum"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:42:22