Resolve string enum parameters on the raw-SQL path#3
Merged
dmytro-khmara merged 1 commit intomasterfrom Apr 29, 2026
Merged
Conversation
The relational type-mapping plugin only resolved a mapping when EF gave it
both a CLR type and a store type. EF's raw-SQL parameter pipeline
(DatabaseFacade.ExecuteSqlRaw{,Async}) supplies only the CLR type, so the
plugin returned null, EF fell back to NpgsqlStringTypeMapping, the parameter
was pinned to NpgsqlDbType.Text, and the server rejected the statement with
42804 against any column of a native enum type.
Adds a StringEnumPgTypeRegistry that the plugin consults when storeType is
null, and a configure overload on UseStringEnumsAsPostgresEnums that lets
callers pre-register their string enums:
optionsBuilder
.UseNpgsql(dataSource)
.UseStringEnums()
.UseStringEnumsAsPostgresEnums(r => r.MapStringEnum<Sport>());
Pre-registration is required because EF Core's relational type-mapping cache
locks in the result of the very first FindMapping(typeof(TEnum), null) call,
which fires during convention setup before OnModelCreating runs --
MapStringEnumAsPostgresEnum<TEnum>() in the model builder is too late.
Covered by a new integration test that round-trips a Sport value through
ExecuteSqlRawAsync against a real Postgres enum column, and by a unit test
that pins the registry-fallback branch of the plugin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DatabaseFacade.ExecuteSqlRaw{,Async}) supplies only the CLR type, so the plugin returnednull, EF fell back toNpgsqlStringTypeMapping, and the parameter was pinned toNpgsqlDbType.Text— producing42804: column "x" is of type my_enum but expression is of type textagainst any column of a native enum type.StringEnumPgTypeRegistrythat the plugin consults whenstoreTypeis null, plus a configure overload onUseStringEnumsAsPostgresEnumsthat pre-registers each string enum.FindMapping(typeof(TEnum), null)call, which fires during convention setup beforeOnModelCreatingruns — model-levelMapStringEnumAsPostgresEnum<TEnum>()is too late.Usage
The configure delegate is optional — existing callers of
UseStringEnumsAsPostgresEnums()keep working unchanged for the EF-translated path. It only needs to be supplied when raw-SQL parameter binding is required.Test plan
RawSqlEnumParameterTests.Binds_a_string_enum_parameter_in_ExecuteSqlRawAsyncround-trips aSportvalue throughExecuteSqlRawAsyncagainst a real Postgres enum column. Fails onmasterwith42804, passes with this change.FindMapping_FallsBackToRegistry_WhenStoreTypeIsMissingpins the registry-fallback branch of the plugin.