DcsvIo.D2.Handler.Repo.Postgres
0.1.1
dotnet add package DcsvIo.D2.Handler.Repo.Postgres --version 0.1.1
NuGet\Install-Package DcsvIo.D2.Handler.Repo.Postgres -Version 0.1.1
<PackageReference Include="DcsvIo.D2.Handler.Repo.Postgres" Version="0.1.1" />
<PackageVersion Include="DcsvIo.D2.Handler.Repo.Postgres" Version="0.1.1" />
<PackageReference Include="DcsvIo.D2.Handler.Repo.Postgres" />
paket add DcsvIo.D2.Handler.Repo.Postgres --version 0.1.1
#r "nuget: DcsvIo.D2.Handler.Repo.Postgres, 0.1.1"
#:package DcsvIo.D2.Handler.Repo.Postgres@0.1.1
#addin nuget:?package=DcsvIo.D2.Handler.Repo.Postgres&version=0.1.1
#tool nuget:?package=DcsvIo.D2.Handler.Repo.Postgres&version=0.1.1
DcsvIo.D2.Handler.Repo.Postgres
PostgreSQL implementation of IDbExceptionClassifier from DcsvIo.D2.Handler.Repo.Abstractions. Plugs into BaseRepoHandler via DI — composition roots call services.AddD2Postgres() once.
Provider-specific knowledge lives behind IDbExceptionClassifier; alternate-provider implementations (where they exist) follow the same shape: one IDbExceptionClassifier impl + one services.AddD2X() extension. BaseRepoHandler itself stays provider-agnostic.
Install
dotnet add package DcsvIo.D2.Handler.Repo.Postgres
Public API
| Type | Role |
|---|---|
PostgresDbExceptionClassifier |
Maps PostgreSQL / EF exceptions → DbFailureKind |
PgErrorCodes |
SQLSTATE string constants + TryGetPgException(Exception) unwrap helper |
AddD2Postgres |
DI extension — registers PostgresDbExceptionClassifier as IDbExceptionClassifier (TryAddSingleton) |
SQLSTATE → DbFailureKind matrix
| SQLSTATE | Name | DbFailureKind |
|---|---|---|
23505 |
unique_violation | UniqueViolation |
23503 |
foreign_key_violation | ForeignKeyViolation |
23502 |
not_null_violation | NotNullViolation |
23514 |
check_violation | CheckViolation |
40001 |
serialization_failure | Deadlock |
40P01 |
deadlock_detected | Deadlock |
57014 |
query_canceled (statement_timeout) | Timeout |
57P03 |
cannot_connect_now | ConnectionFailure |
53300 |
too_many_connections | ConnectionFailure |
08*** |
connection_exception class | ConnectionFailure |
DbUpdateConcurrencyException (BCL-typed) is handled directly by BaseRepoHandler and never reaches the classifier.
Codes follow the PostgreSQL SQLSTATE / error-code appendix.
Three exception shapes handled uniformly
public DbFailureKind? Classify(Exception exception)
The classifier accepts any Exception and walks three shapes:
DbUpdateExceptionwrapping aPostgresException— the EF save-pipeline path. The unwrap helper finds the innerPostgresExceptionand dispatches on itsSqlState.- Raw
PostgresException— thrown directly during a read / query / DDL when EF doesn't wrap it. SameSqlStateswitch. - Network-level failure —
SocketException/IOExceptionanywhere in the inner-exception chain (connection refused, socket reset, host unreachable, transport-stream fault). ReturnsConnectionFailure. The walk descends up to 10 inner-exception levels soAggregateException/TargetInvocationException/ EF wrappers don't hide the underlying network error.
Bare NpgsqlException instances with no recognizable inner cause (bad connection-string parse, SSL handshake failure, concurrent-connection misuse, internal Npgsql state errors) return null — BaseRepoHandler then preserves the original UnhandledException result. These are programmer / config failures and should NOT be silently treated as transient and retried.
Precedence: when both shapes apply (e.g., a PostgresException whose inner exception chain also contains a SocketException), pass 1 (SQLSTATE classification) wins. Pass 2 (network detection) only fires when pass 1 yields null — either the PostgresException had no recognized SQLSTATE, the SQLSTATE was null, or no PostgresException was found at all. Tests pin this ordering.
Client-side NpgsqlCommand.CommandTimeout blowups are NOT classified here — Npgsql surfaces them as OperationCanceledException, which BaseHandler.RunCorePipelineAsync already handles before BaseRepoHandler sees the captured exception. Server-side statement_timeout cancellations DO reach this classifier as PostgresException SQLSTATE 57014 (pass 1) → DbFailureKind.Timeout.
DI registration
// Composition root
services.AddD2Handler();
services.AddD2Postgres(); // ← registers PostgresDbExceptionClassifier
services.AddDbContext<AppDbContext>(o => o.UseNpgsql(...));
AddD2Postgres() uses TryAddSingleton — calling it multiple times is safe (no duplicate registration). To override the default classifier with a custom implementation, register the custom BEFORE the call:
services.AddSingleton<IDbExceptionClassifier, MyCustomClassifier>();
services.AddD2Postgres(); // no-op — TryAdd sees an existing registration
If a service genuinely needs more than one classifier (e.g. one connection to Postgres, one to an embedded SQLite for tests) don't try to register two IDbExceptionClassifiers into the unkeyed slot — DI's last-registered-wins resolution makes the active impl call-order-dependent and leaves the other as an orphaned singleton in the graph. Use keyed registration + keyed resolution instead:
services.AddKeyedSingleton<IDbExceptionClassifier, PostgresDbExceptionClassifier>("primary");
services.AddKeyedSingleton<IDbExceptionClassifier, SqliteDbExceptionClassifier>("scratch");
// Inject via [FromKeyedServices("primary")] IDbExceptionClassifier classifier
Keyed services are .NET 8+ — the codebase targets .NET 10, so the API is available.
Dependencies
DcsvIo.D2.Handler.Repo.Abstractions—IDbExceptionClassifier+DbFailureKindMicrosoft.EntityFrameworkCore—DbUpdateExceptiontypeNpgsql—PostgresException+NpgsqlExceptiontypesMicrosoft.Extensions.DependencyInjection.Abstractions—IServiceCollection
Related packages
DcsvIo.D2.Handler.Repo.Abstractions— interface +DbFailureKindenum + extension factoriesDcsvIo.D2.Handler.Repo—BaseRepoHandlerconsumes the classifier
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- DcsvIo.D2.Handler.Repo.Abstractions (>= 0.1.1)
- dotenv.net (>= 4.0.2)
- JetBrains.Annotations (>= 2025.2.4)
- Microsoft.EntityFrameworkCore (>= 10.0.7)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Caching.Memory (>= 10.0.7)
- Microsoft.Extensions.Configuration (>= 10.0.7)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.7)
- Microsoft.Extensions.DependencyInjection (>= 10.0.7)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Logging (>= 10.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Options (>= 10.0.7)
- Npgsql (>= 10.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.