EF Core+Npgsql无法将枚举映射为PostgreSQL枚举类型(传为整数)
问题现象
尝试通过EF Core将模型中的枚举映射到PostgreSQL自定义枚举类型,但EF Core始终将枚举值以整数而非PostgreSQL枚举类型发送,导致数据库报错。
枚举定义
public enum EventType { Purchase, Signup, OfferTrading }
DbContext配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); modelBuilder.HasPostgresEnum<EventType>( name: "event_type", nameTranslator: new NpgsqlSnakeCaseNameTranslator()); // 也曾尝试: // modelBuilder.HasPostgresEnum<EventType>("event_type"); // modelBuilder.HasPostgresEnum<EventType>(); base.OnModelCreating(modelBuilder); }
NpgsqlDataSource配置
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); dataSourceBuilder.MapEnum<EventType>( pgName: "event_type", nameTranslator: new NpgsqlSnakeCaseNameTranslator()); // 也曾尝试: // dataSourceBuilder.MapEnum<EventType>("event_type"); // dataSourceBuilder.MapEnum<EventType>(); var dataSource = dataSourceBuilder.Build(); services.AddSingleton(dataSource); services.AddDbContext<AppDbContext>((sp, options) => options.UseNpgsql( sp.GetRequiredService<NpgsqlDataSource>(), b => b.MigrationsAssembly("*******.Infrastructure")) .UseSnakeCaseNamingConvention() .EnableSensitiveDataLogging() .EnableDetailedErrors() .AddInterceptors(new AuditInterceptor()));
设计时配置
已实现带有相同配置的IDesignTimeDbContextFactory<>。
实体配置
builder.Property(c => c.EventType) .HasColumnType("event_type") .IsRequired();
曾尝试在此处添加字符串转换器,但仅将错误从integer改为text,仍未转换为event_type PostgreSQL类型。
迁移情况
初始迁移中枚举类型已正确创建(已确认存在于数据库):
migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:event_type", "purchase,signup,offer_trading");
表定义中:
event_type = table.Column<int>(type: "event_type", nullable: false),
生成的SQL
插入数据时EF Core生成的SQL:
INSERT INTO campaigns (id, created_at, event_type, external_template_id, name, partner_id, priority, updated_at, validity_period_end, validity_period_start, contact_channels) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10)
参数显示:
..., @p2 = '0', ... // 预期应为: @p2 = 'purchase' (DbType = event_type)
参数类型未设置为event_type,仅发送整数值;使用字符串转换器时发送字符串,但仍未设置正确的PostgreSQL枚举类型,错误依旧。
最终错误
PostgresException: 42804: column "event_type" is of type event_type but expression is of type integer
使用版本
- .NET 10
- EF Core 10
- Npgsql.EntityFrameworkCore.PostgreSQL 10.0.1
- Npgsql 10.0.2
- PostgreSQL 18
未配置全局枚举转换(之前曾配置,现已移除)。
核心问题
EF Core + Npgsql如何正确绑定枚举为PostgreSQL枚举类型而非以整数或文本发送?还缺少哪些配置?
解决方案
1. 修复迁移中的列类型定义
迁移代码中table.Column<int>是核心问题,EF Core错误地将列类型标记为int,即使指定了type: "event_type"。手动修改迁移文件,将Column<int>改为Column<EventType>:
event_type = table.Column<EventType>(type: "event_type", nullable: false),
修改完成后重新应用迁移。
2. 调整枚举注册与实体配置的顺序
在OnModelCreating中,先注册PostgreSQL枚举,再应用实体配置。当前代码先加载实体配置后注册枚举,会导致实体配置无法感知到PostgreSQL枚举的存在,默认使用整数映射。调整顺序:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 先注册PostgreSQL枚举 modelBuilder.HasPostgresEnum<EventType>( name: "event_type", nameTranslator: new NpgsqlSnakeCaseNameTranslator()); // 再应用实体配置 modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); base.OnModelCreating(modelBuilder); }
3. 移除自定义转换器
不要为枚举添加字符串或其他自定义转换器,Npgsql会自动处理枚举与PostgreSQL自定义枚举的映射,手动添加转换器会覆盖默认行为。
4. 同步设计时工厂的配置顺序
确保IDesignTimeDbContextFactory中的配置逻辑与OnModelCreating一致,先注册枚举再加载实体配置。
5. 清理并重新生成迁移
若旧迁移存在遗留错误,删除旧迁移文件后重新生成:
dotnet ef migrations remove dotnet ef migrations add InitialCreate dotnet ef database update
内容的提问来源于stack exchange,提问作者Henrique Miranda

