DcsvIo.D2.Handler.Repo 0.1.2

dotnet add package DcsvIo.D2.Handler.Repo --version 0.1.2
                    
NuGet\Install-Package DcsvIo.D2.Handler.Repo -Version 0.1.2
                    
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" Version="0.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DcsvIo.D2.Handler.Repo" Version="0.1.2" />
                    
Directory.Packages.props
<PackageReference Include="DcsvIo.D2.Handler.Repo" />
                    
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 --version 0.1.2
                    
#r "nuget: DcsvIo.D2.Handler.Repo, 0.1.2"
                    
#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@0.1.2
                    
#: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&version=0.1.2
                    
Install as a Cake Addin
#tool nuget:?package=DcsvIo.D2.Handler.Repo&version=0.1.2
                    
Install as a Cake Tool

DcsvIo.D2.Handler.Repo

EF-flavored BaseRepoHandler<TSelf, TInput, TOutput> — sits on top of BaseHandler from DcsvIo.D2.Handler. Overrides HandleAsync to convert any database exception captured during ExecuteAsync into a typed D2Result failure (concurrency conflict, unique violation, deadlock, connection failure, etc.) so callers can branch on what actually went wrong instead of getting a generic 500.

Provider-agnostic by design: catches the BCL-typed DbUpdateConcurrencyException directly and routes everything else through an injected IDbExceptionClassifier. Provider-specific knowledge lives in sibling packages (e.g. DcsvIo.D2.Handler.Repo.Postgres).

Install

dotnet add package DcsvIo.D2.Handler.Repo

Public API

Type Role
BaseRepoHandler<TSelf, TInput, TOutput> Abstract subclass of BaseHandler. Constructor takes an injected IDbExceptionClassifier. Override HandleAsync calls RunCorePipelineAsync then dispatches the captured exception through the classifier to a typed D2Result factory.
MapDbException (virtual) Per-handler refinement hook — attach domain-specific TKMessage + InputError; return null for default factory.

Zero provider deps in this package (no Npgsql). Refs DcsvIo.D2.Handler, DcsvIo.D2.Handler.Abstractions, DcsvIo.D2.Handler.Repo.Abstractions, DcsvIo.D2.Result, and Microsoft.EntityFrameworkCore.


Mapping

Captured exception Classified as Default D2Result
DbUpdateConcurrencyException ConcurrencyConflict (BCL — handled directly) D2Result.ConcurrencyConflict()
IDbExceptionClassifier.Classify(ex) returns UniqueViolation UniqueViolation D2Result.UniqueViolation()
Returns ForeignKeyViolation ForeignKeyViolation D2Result.ForeignKeyViolation()
Returns NotNullViolation NotNullViolation D2Result.NotNullViolation()
Returns CheckViolation CheckViolation D2Result.CheckViolation()
Returns Timeout Timeout D2Result.DbTimeout()
Returns Deadlock Deadlock D2Result.DbDeadlock()
Returns ConnectionFailure ConnectionFailure D2Result.DbConnectionFailure()
Classifier returns null unknown Falls through — BaseHandler's UnhandledException preserved

OperationCanceledException is intentionally NOT remapped here — BaseHandler.RunCorePipelineAsync already handles it (returns D2Result.Canceled for caller-initiated cancellation, D2Result.ServiceUnavailable for downstream timeouts not tied to the request token).


Per-handler refinement

The default factory dispatch produces a generic message ("This value is already in use") with no field-level information — useful for diagnostics + programmatic discrimination, but weak UX for form-driven flows.

Handlers that know their constraint identity should override MapDbException to attach a domain-specific TKMessage + InputError:

public sealed class CreateUser(
    HandlerContext<CreateUser> context,
    IDbExceptionClassifier classifier,
    IAppDbContext db)
    : BaseRepoHandler<CreateUser, CreateUserInput, UserDto>(context, classifier), ICreateUser
{
    protected override async ValueTask<D2Result<UserDto?>> ExecuteAsync(
        CreateUserInput input, CancellationToken ct)
    {
        var user = User.Create(input);
        db.Users.Add(user);
        await db.SaveChangesAsync(ct);
        return D2Result<UserDto?>.Created(user.ToDto());
    }

    protected override D2Result<UserDto?>? MapDbException(Exception ex, DbFailureKind kind)
    {
        // The DB-side unique index `users_email_key` covers the email column.
        if (kind == DbFailureKind.UniqueViolation && IsEmailIndex(ex))
        {
            return D2Result<UserDto?>.UniqueViolation(
                messages: [TK.Auth.Errors.EMAIL_ALREADY_TAKEN],
                inputErrors: [new InputError("email", "EMAIL_ALREADY_TAKEN")]);
        }

        return null; // fall back to the generic factory
    }
}

Returning null from the override means "use the default" — handlers only customize the cases they care about.


Caller-side discrimination

Callers branch on the typed booleans from DcsvIo.D2.Handler.Repo.Abstractions:

var result = await createUser.HandleAsync(input);

if (result.IsUniqueViolation)        return Conflict(result);                  // 409, surface to user
if (result.IsConcurrencyConflict)    return await ReloadAndMergeAsync(input);  // optimistic-concurrency retry
if (result.IsTransientDbFailure)     return await retry.RetryAsync(...);       // deadlock / timeout / connection
if (result.IsForeignKeyViolation)    return BadRequest(result);                // referenced item missing

The roll-up IsTransientDbFailure covers deadlock + timeout + connection-failure (caller may safely retry). Concurrency conflicts are intentionally excluded from the roll-up — they need reload-then-merge, not a blind retry.

IsTransientDbFailure is a separate axis from the built-in IsTransientRetryable (IsServiceUnavailable || IsRateLimited) on D2Result from the result lib. A generic retry policy that wants to catch BOTH the HTTP-flavored AND DB-flavored transient sets should check the union: result.IsTransientRetryable || result.IsTransientDbFailure.


DI registration

BaseRepoHandler requires an IDbExceptionClassifier from DI. The composition root registers a provider-specific implementation:

services.AddD2Handler();
services.AddD2Postgres();   // registers PostgresDbExceptionClassifier as IDbExceptionClassifier
services.AddDbContext<AppDbContext>(o => o.UseNpgsql(...));
services.AddTransient<ICreateUser, CreateUser>();

Without a registered classifier, resolving any BaseRepoHandler subclass fails fast at the container.


Dependencies

  • DcsvIo.D2.Handler — base + HandlerContext<T>
  • DcsvIo.D2.Handler.AbstractionsIHandler, HandlerOptions
  • DcsvIo.D2.Handler.Repo.AbstractionsIDbExceptionClassifier, DbFailureKind, D2Result extension factories
  • DcsvIo.D2.Result — base D2Result
  • Microsoft.EntityFrameworkCoreDbUpdateConcurrencyException

No Npgsql, no provider-specific deps.


  • DcsvIo.D2.Handler.Repo.Abstractions — vocabulary + extension factories + booleans
  • DcsvIo.D2.Handler.Repo.Postgres — PostgreSQL classifier impl
  • DcsvIo.D2.Handler — base handler
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.2 104 7/17/2026
0.1.1 110 7/17/2026