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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DcsvIo.D2.Handler.Repo.Postgres" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DcsvIo.D2.Handler.Repo.Postgres" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="DcsvIo.D2.Handler.Repo.Postgres" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DcsvIo.D2.Handler.Repo.Postgres --version 0.1.1
                    
#r "nuget: DcsvIo.D2.Handler.Repo.Postgres, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DcsvIo.D2.Handler.Repo.Postgres@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DcsvIo.D2.Handler.Repo.Postgres&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=DcsvIo.D2.Handler.Repo.Postgres&version=0.1.1
                    
Install as a Cake Tool

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:

  1. DbUpdateException wrapping a PostgresException — the EF save-pipeline path. The unwrap helper finds the inner PostgresException and dispatches on its SqlState.
  2. Raw PostgresException — thrown directly during a read / query / DDL when EF doesn't wrap it. Same SqlState switch.
  3. Network-level failureSocketException / IOException anywhere in the inner-exception chain (connection refused, socket reset, host unreachable, transport-stream fault). Returns ConnectionFailure. The walk descends up to 10 inner-exception levels so AggregateException / 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 nullBaseRepoHandler 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.AbstractionsIDbExceptionClassifier + DbFailureKind
  • Microsoft.EntityFrameworkCoreDbUpdateException type
  • NpgsqlPostgresException + NpgsqlException types
  • Microsoft.Extensions.DependencyInjection.AbstractionsIServiceCollection

  • DcsvIo.D2.Handler.Repo.Abstractions — interface + DbFailureKind enum + extension factories
  • DcsvIo.D2.Handler.RepoBaseRepoHandler consumes the classifier
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 106 7/17/2026
0.1.0 107 7/17/2026