From c33399637fe989e338f81ff27d9b6f0eb906408d Mon Sep 17 00:00:00 2001 From: Dmytro Khmara Date: Mon, 27 Apr 2026 19:19:18 +0100 Subject: [PATCH] Tidy README and document Postgres native-enum interop --- README.md | 92 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index c2e0f1f..9a98b76 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # StrEnum.Dapper -Allows to use [StrEnum](https://github.com/StrEnum/StrEnum/) string enums with Dapper. +Lets you use [StrEnum](https://github.com/StrEnum/StrEnum/) string enums with Dapper. -Supports Dapper v2.0.4+ +Supports Dapper 2.0.4+. ## Installation -You can install [StrEnum.Dapper](https://www.nuget.org/packages/StrEnum.Dapper/) using the .NET CLI: +Install [StrEnum.Dapper](https://www.nuget.org/packages/StrEnum.Dapper/) via the .NET CLI: ``` dotnet add package StrEnum.Dapper @@ -14,7 +14,7 @@ dotnet add package StrEnum.Dapper ## Usage -Define a string enum and an entity that uses it: +### Defining a string enum and an entity ```csharp public class Sport: StringEnum @@ -34,13 +34,13 @@ public class Race ### Initialization -Invoke the `StrEnumDapper.UseStringEnums` method before the first use of Dapper: +Call `StrEnumDapper.UseStringEnums()` before the first use of Dapper: ```csharp StrEnumDapper.UseStringEnums(); ``` -Note, that the `UseStringEnums` method searches for string enums in all the _loaded_ assemblies, and then registers them with Dapper. Since .NET loads assemblies in a lazy fashion, make sure that the assemblies containing string enums are loaded before calling `UseStringEnums`. You can do that either implicitly, by referencing types in such assemblies, or by explicitly loading such assemblies. For example, if `Sport` is located in a separate assembly, you can load it the following way: +`UseStringEnums()` searches all *loaded* assemblies for string enums and registers each with Dapper. Since .NET loads assemblies lazily, make sure the assemblies containing your string enums are loaded before this call — either implicitly, by referencing a type from them, or explicitly: ```csharp Assembly.Load(typeof(Sport).Assembly.GetName()); @@ -52,51 +52,75 @@ Assembly.Load(typeof(Sport).Assembly.GetName()); connection.Execute(@"INSERT Races(Id, Name, Sport) values (@id, @name, @sport)", new[] { - new - { - id = Guid.NewGuid(), - name = "Chornohora Sky Marathon", - sport = Sport.TrailRunning - }, - new - { - id = Guid.NewGuid(), - name = "Cape Town Cycle Tour", - sport = Sport.RoadCycling - }, - new - { - id = Guid.NewGuid(), - name = "Cape Epic", - sport = Sport.MountainBiking - } + new { id = Guid.NewGuid(), name = "Chornohora Sky Marathon", sport = Sport.TrailRunning }, + new { id = Guid.NewGuid(), name = "Cape Town Cycle Tour", sport = Sport.RoadCycling }, + new { id = Guid.NewGuid(), name = "Cape Epic", sport = Sport.MountainBiking } }); ``` ### Running queries -You can pass string enums in query parameters and map them to your entities: +Pass string enums as query parameters and map them onto your entities: ```csharp -var trailRuns = connection.Query(@"SELECT Id, Name, Sport FROM Races WHERE Sport = @sport", new { sport = Sport.TrailRunning }); +var trailRuns = connection.Query( + @"SELECT Id, Name, Sport FROM Races WHERE Sport = @sport", + new { sport = Sport.TrailRunning }); ``` -Note, that you cannot use string enums in the `IN` clause - Dapper [does not support](https://github.com/DapperLib/Dapper/issues/1134) it yet. The following won't work: +Dapper [doesn't yet support](https://github.com/DapperLib/Dapper/issues/1134) string enums in `IN` clauses, so this won't work: ```csharp -var cyclingRaces = connection.Query(@"SELECT Id, Name, Sport FROM Races WHERE Sport in @sports", - new { sports = new[]{ Sport.TrailRunning, Sport.MountainBiking }}); +var cyclingRaces = connection.Query( + @"SELECT Id, Name, Sport FROM Races WHERE Sport in @sports", + new { sports = new[] { Sport.TrailRunning, Sport.MountainBiking } }); ``` -The workaround would be to case each of the string enum's members to string: +Cast each member to `string` as a workaround: ```csharp -var cyclingRaces = connection.Query(@"SELECT Id, Name, Sport FROM Races WHERE Sport in @sports", - new { sports = new[]{ (string)Sport.TrailRunning, (string)Sport.MountainBiking }}); +var cyclingRaces = connection.Query( + @"SELECT Id, Name, Sport FROM Races WHERE Sport in @sports", + new { sports = new[] { (string)Sport.TrailRunning, (string)Sport.MountainBiking } }); ``` +## Postgres native enum columns + +`StrEnum.Dapper` flattens `StringEnum` to its underlying `string` value at the Dapper layer, which Postgres accepts straight into a `text` column. Native Postgres enum columns (`CREATE TYPE ... AS ENUM`) are a different story: Postgres has no implicit cast from `text` to a user-defined enum, so you'll see: + +``` +42804: column "sport" is of type sport but expression is of type text +``` + +`StrEnum.Dapper` does not currently bind the parameter to the enum's OID for you. Two practical workarounds: + +* **Cast in SQL:** add `::your_enum` to the parameter site. Works against existing tables without ceremony. + + ```csharp + connection.Execute( + "INSERT INTO races (id, name, sport) VALUES (@id, @name, @sport::sport)", + new { id = Guid.NewGuid(), name = "Chornohora Sky Marathon", sport = Sport.TrailRunning }); + ``` + +* **Bypass the Dapper handler for that parameter** with a hand-rolled `NpgsqlParameter` that sets `DataTypeName`: + + ```csharp + var p = new DynamicParameters(); + p.Add("id", Guid.NewGuid()); + p.Add("name", "Chornohora Sky Marathon"); + p.Add(new NpgsqlParameter + { + ParameterName = "sport", + DataTypeName = "sport", + Value = (string)Sport.TrailRunning, + }); + connection.Execute("INSERT INTO races (id, name, sport) VALUES (@id, @name, @sport)", p); + ``` + +If you only need raw Npgsql wire support (no Dapper), use [StrEnum.Npgsql](https://github.com/StrEnum/StrEnum.Npgsql/) — `NpgsqlDataSourceBuilder.MapStringEnum()` binds `Sport` instances directly to the enum OID. There's no first-class `StrEnum.Npgsql.Dapper` package today; if you'd find one useful, please open an issue. + ## License -Copyright © 2022 [Dmitry Khmara](https://dmitrykhmara.com). +Copyright © 2026 [Dmytro Khmara](https://dmytrokhmara.com). -StrEnum is licensed under the [MIT license](LICENSE.txt). \ No newline at end of file +StrEnum is licensed under the [MIT license](LICENSE.txt).