Eternet.Crud.Relational 3.1.22

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

Eternet.Crud.Relational

Relational CRUD runtime, generated relational bridge conventions, bulk primitives, and relational outbox support for Eternet.Mediator.

Installation

dotnet add package Eternet.Crud.Relational

Current Package Shape

As of April 2026, the supported authoring model is:

  • request-first relational CRUD, where the public contract stays on the contracts/public handler and the internal relational request becomes the semantic source of truth
  • aggregate-root semantics for single-entity create, update, delete, and upsert lanes
  • first-class homogeneous bulk create, bulk delete, and bulk update lanes
  • generated relational bridges that bind deterministic request/runtime inputs and publish reserved downstream outputs
  • explicit IDomainEvent plus relational outbox projection as the default outbox model
  • legacy CreateEntityCommand<Response> / UpdateEntityCommand<Response> / DeleteEntityCommand<Response> and [HandlerForEntity<TEntity>] only as compatibility paths

The modern relational lane is centered on a single explicit relational request type:

public sealed partial class UpdateJournalEntryHandler :
    UpdateJournalEntry,
    IRelationalUpdateImplementation<UpdateJournalEntryRelational.Request>
{
    public override Response Handle(Request request) => request.StepsResults!.Response!;
}

public static partial class UpdateJournalEntryRelational
{
    public sealed partial record Request
        : RelationalUpdateEntityCommand<AccountingContext, JournalEntry, int>
    {
        public override int GetKey() => Id;
    }
}

For update work, the internal relational request above is the supported replacement for authoring new features on UpdateEntityCommand<Response>.

Recent Changes

The last two weeks materially changed both Eternet.Crud.Relational and Eternet.Crud.Relational.Generator:

  • bulk delete, bulk create, and bulk update are now first-class relational lanes instead of bespoke custom-write patterns
  • relational CRUD moved to the request-first API where IRelational*Implementation<TRelationalRequest> declares one explicit internal relational request type
  • create/update/delete/upsert semantics are documented and enforced at the aggregate-root boundary
  • generated bridge steps now return StepResult<T> and preserve compatible custom relational result types for single-entity lanes
  • GeneratedPipelineRuntime is no longer the public runtime anchor for generated bridge glue; generated code now stays behind assembly-local helpers backed by GeneratedBindingRuntime
  • relational outbox authoring now defaults to explicit IDomainEvent emission plus registered projector translation; the old legacy outbox compatibility APIs were removed
  • generated bridge policy is emitted as assembly metadata so test/runtime discovery can reason about handlers across files and referenced assemblies
  • nested relational handlers now get deterministic unique bridge artifact names
  • [RelationalEntityBinding("Alias")] can publish an additional downstream alias for the aggregate entity while keeping the default Entity output
  • scalar binding in generated bridges was tightened so request members bind deterministically by member name and type

Preferred Authoring Model

For new relational features:

  1. Keep the public request/contract in the contracts or public server layer and express API semantics with IEndpointCreate, IEndpointUpdate, IEndpointDelete, IEndpointUpsert, or the relevant query contract.
  2. Keep the public handler on that contract and implement one of the IRelationalCreate/Update/Delete/UpsertImplementation<TRelationalRequest> interfaces.
  3. Declare an app-owned internal relational request that inherits the appropriate relational request base: RelationalCreateEntityCommand<...>, RelationalUpdateEntityCommand<...>, RelationalDeleteEntityCommand<...>, or RelationalUpsertEntityCommand<...>.
  4. Let Eternet.Crud.Relational.Generator augment that request, generate the bridge, and publish the reserved downstream outputs.
  5. Keep domain materialization/mutation and IDomainEvent emission in the app-owned relational business handler or pipeline steps.

The preferred bridge ownership split is:

  • public handler: API contract shape, authorization/binding concerns when needed, and final response assembly from request.StepsResults
  • generated bridge: exact public request property transfer, supported keyed-state compatibility transfer, dispatch to the internal relational request, reserved output publishing, and generated relational pipeline result projection
  • relational request/handler pipeline: write validation, lookups, derived write state, mutation, domain events, and the relational result

For new migrations, prefer a bridge-only public handler when the public side only needs response assembly. Put write-specific derived state on the relational pipeline instead of producing it as public keyed state. Public keyed-state binding remains supported for compatibility and for transport-layer facts that genuinely belong before the bridge. When the internal relational request has generated pipeline steps, the generated bridge copies that request's StepsResults object to request.StepsResults.RelationalStepsResults. Public response assembly can read auxiliary relational-side outputs through that nested property without moving lookup/check steps back to the public pipeline.

When a generated mediator pipeline already owns custom multi-entity behavior, migrate it to EntityChangesCommand<TDbContext, TResponse> instead of forcing a primitive companion. This keeps lookup/check/mutation steps visible on the public pipeline, removes only the explicit SaveChanges step, and lets UnitOfWorkBehavior own the transaction and final save. The generator analyzer marks this deterministic path with CodeFixKind=RelationalEntityChangesPipeline only for preserved-state cases it can prove, such as ticket-action preservation or one shared transaction. It keeps the diagnostic non-codefixable for database-generated key reads, intermediate flushes, get-or-create flows, and bulk item derived-state flows.

For generated pipeline migrations, ECR015 is intentionally single-DbContext. If the analyzer sees pipeline evidence for more than one DbContext, it does not offer the deterministic migration. Transaction boundary steps are identified semantically by following local call trees to EF Core DatabaseFacade.BeginTransaction*, DatabaseFacade.CommitTransaction*, or IDbContextTransaction.Commit* calls. When the boundary belongs to the same DbContext as the recommended relational command, the diagnostic emits TransactionBoundarySteps, TransactionStartSteps, and TransactionCommitSteps so EAC can remove explicit start/commit steps without relying on step names.

For homogeneous set writes, keep the same request-first model and switch the internal relational request to one of the bulk bases:

  • RelationalBulkCreateCommand<...>
  • RelationalBulkDeleteCommand<...>
  • RelationalBulkUpdateCommand<...>

Bulk create consumes the relational request's GetItems() result. If each item needs a lookup-derived value such as a vendor or tenant id, include that value in the typed item input before GetItems() materializes entities, derive it in the relational lane, or choose EntityChangesCommand<TDbContext, TResponse> for a richer workflow.

Compatibility And Roadmap

Legacy relational surfaces still compile, but they are no longer the design target for new code:

  • [HandlerForEntity<TEntity>] remains compatibility-only
  • CreateEntityCommand<Response>, UpdateEntityCommand<Response>, and DeleteEntityCommand<Response> remain compatibility-only
  • ConfigureGeneratedRelationalCommand(...) remains deprecated migration scaffolding
  • legacy *RelationalHooks are already unsupported
  • Eternet.Crud.Relational.Generator discovers the relational key path from deterministic EF metadata and explicit relational contracts instead of naming heuristics (Id, <EntityName>Id, [Key]) or IIntIdentity/IStringIdentity; the remaining cleanup is the runtime/public-surface identity coupling

The relational identity decoupling work is tracked in docs/plans/relational/identity-decoupling-plan.md:

  • remove nondeterministic key and lookup heuristics from the relational lane (done)
  • stop treating IIntIdentity and IStringIdentity as part of the modern relational authoring model
  • decouple the modern relational lane from IIdentity
  • obsolete legacy relational authoring entry points and back that stance with analyzer diagnostics and migration guidance

That future wave is intentionally scoped to Crud.Relational first; it does not require broad immediate cleanup across all of Eternet.Mediator.

Migration Note: ScopedStates Retirement

Eternet.Crud.Relational 3.0.0 aligned with the ScopedStates retirement in Eternet.Mediator.

  • GeneratedRelationalCommandBinder no longer depends on a public ScopedStates service or constructor argument.
  • Relational bridge binding now resolves values through GeneratedBindingRuntime and the ambient generated runtime scope.
  • Tests or custom infrastructure that previously created ScopedStates manually should migrate to GeneratedBindingRuntime.EnterScope() and GeneratedBindingRuntime.Publish(...) when they need runtime state in-process.

Migration guide: ../../Eternet.Mediator/docs/scoped-states-breaking-change-v3.md

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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
3.1.22 34 5/14/2026
3.1.21 90 5/11/2026
3.1.20 96 5/8/2026
3.1.19 111 5/5/2026
3.1.18 100 5/4/2026
3.1.17 98 5/1/2026
3.1.16 85 5/1/2026
3.1.15 88 5/1/2026
3.1.14 88 5/1/2026
3.1.13 88 4/30/2026
3.1.12 131 4/23/2026
3.1.11 99 4/20/2026
3.1.10 95 4/20/2026
3.1.9 120 4/13/2026
3.1.8 130 4/12/2026
3.1.7 95 4/12/2026
3.1.6 110 4/10/2026
3.1.5 92 4/10/2026
3.1.4 94 4/8/2026
3.1.3 151 4/6/2026
Loading failed

Aggregate-root lane semantics: create makes new roots; update handles root-governed child mutations.