Stratara.Mediator 4.0.2

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Stratara.Mediator --version 4.0.2
                    
NuGet\Install-Package Stratara.Mediator -Version 4.0.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="Stratara.Mediator" Version="4.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Stratara.Mediator" Version="4.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Stratara.Mediator" />
                    
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 Stratara.Mediator --version 4.0.2
                    
#r "nuget: Stratara.Mediator, 4.0.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 Stratara.Mediator@4.0.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=Stratara.Mediator&version=4.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Stratara.Mediator&version=4.0.2
                    
Install as a Cake Tool

Stratara.Mediator

Derived. The behaviour described here is specified under openspec/specs/. Those specifications are the source; this page explains and illustrates them.

License: MIT.

In-process mediator with DI-resolved handlers and pipeline behaviors. Drop-in replacement for MediatR-style routing without the runtime cost of MethodInfo.Invoke — uses a typed wrapper cache and direct DI dispatch.

Quick start

// The mediator traces every dispatch, so an OpenTelemetry Tracer must be resolvable.
// AddMediator() does not register one — pick your own instrumentation name:
services.AddSingleton(TracerProvider.Default.GetTracer("Your.App"));

services.AddMediator()
    .AddCommandHandlersFromAssemblyContaining<Program>()
    .AddQueryHandlersFromAssemblyContaining<Program>()
    .AddPipelineBehaviorWithResult(typeof(LoggingBehavior<,>))
    .AddPipelineBehavior(typeof(LoggingBehavior<>));

// Optional: wrap in authorization decorator
services.AddAuthorizingMediator<MyAuthorizationProvider>();

IMediator is registered scoped. Resolve it from a scope (a request scope in ASP.NET Core, or an explicit IServiceProvider.CreateScope() in a console host) — resolving it from the root provider throws.

What's in the box

  • IMediator.HandleAsync<TResult>(IRequest<TResult>, CancellationToken) — routes queries and commands-with-result to IQueryHandler<TRequest, TResult> through any registered IPipelineBehavior<TRequest, TResult> chain.
  • IMediator.HandleAsync<TRequest>(TRequest, CancellationToken) — routes void commands to ICommandHandler<TRequest> through any registered IPipelineBehavior<TRequest> chain.
  • AuthorizingMediator decorator — checks [RequireRole] attributes via IAuthorizationProvider and [RequirePermission] attributes via IPermissionResolver (both AND) on the request's runtime type before delegating to the inner mediator. Its startup validator fails fast when a permission-guarded type is registered without an authorizing mediator or without a resolver, so a guard can never be silently skipped.
  • BucketLockPool — concurrency primitive that serialises IAggregateScopedCommand dispatch per bucket id. Used by message-bus consumers (e.g. the MediatorCommandWorker in Stratara.Outbox.RabbitMQ) to keep aggregate writes single-writer.

Pipeline behavior contract

Behaviors run outer-to-inner in DI registration order:

public sealed class LoggingBehavior<TRequest, TResult> : IPipelineBehavior<TRequest, TResult>
    where TRequest : IRequest<TResult>
{
    public async Task<TResult> HandleAsync(
        TRequest request, Func<Task<TResult>> next, CancellationToken cancellationToken)
    {
        // before
        var result = await next();
        // after
        return result;
    }
}

Tenant isolation

AddStrataraTenantIsolation() registers a pipeline behavior that enforces tenant isolation at the mediator entrance — before the handler runs — for any request that opts in by implementing the ITenantScopedRequest marker. Requests that do not implement the marker pass through untouched.

public sealed record GetCustomerQuery(Guid CustomerId, Guid TenantId)
    : IQuery<CustomerDto>, ITenantScopedRequest;

services
    .AddStrataraValidation()           // validation stays outermost
    .AddStrataraTenantIsolation();     // then tenant isolation

The behavior compares the request's TenantId (the data owner) against the ambient session's data-owner tenant (SessionContext.TenantId), not the actor tenant (SessionContext.ActorTenantId). A request whose payload names a different tenant than the established session subject is rejected with TenantAccessDeniedException (translated to HTTP 403 on ASP.NET hosts that register AddStrataraProblemDetails() from Stratara.ServiceDefaults.AspNetCore; surfaced through the message-failure path on workers).

Default vs. strict mode

  • TenantIsolationMode.Default — enforces only the subject match. A privileged cross-tenant operation (actor tenant ≠ data-owner tenant) passes, because the calling endpoint is expected to have promoted the session's data-owner tenant to the target before dispatch.
  • TenantIsolationMode.Strict — additionally routes every cross-tenant operation through an ICrossTenantAuthorizer. Stratara registers a deny-all default (via TryAdd), so strict mode rejects all cross-tenant access until you register your own authorizer that grants it:
services.AddStrataraTenantIsolation(o => o.Mode = TenantIsolationMode.Strict);
services.AddScoped<ICrossTenantAuthorizer, PlatformAdminCrossTenantAuthorizer>();
internal sealed class PlatformAdminCrossTenantAuthorizer(IHttpContextAccessor http)
    : ICrossTenantAuthorizer
{
    public ValueTask<bool> IsCrossTenantAllowedAsync(SessionContext session, CancellationToken ct) =>
        ValueTask.FromResult(http.HttpContext?.User.IsInRole("PlatformAdmin") ?? false);
}

The behavior runs both in-process (queries via IMediator at the endpoint, where HttpContext is available) and worker-side (commands dispatched through the outbox, where there is no HttpContext). An ICrossTenantAuthorizer that needs request-role state should be applied on the in-process path; the worker path must base its decision on the SessionContext alone.

Dependencies

  • Stratara.Abstractions — for IMediator/IRequest/ICommand/IQuery/IPipelineBehavior contracts, plus ITenantScopedRequest/ICrossTenantAuthorizer/TenantAccessDeniedException.
  • Stratara.Diagnostics — log-event IDs for the tenant-isolation behavior.
  • Microsoft.Extensions.DependencyInjection.Abstractions.
  • Microsoft.Extensions.Logging.Abstractions.
  • OpenTelemetry.Api — emits an Activity per dispatch under the Stratara.Application source.

No EF Core, no message bus, no event sourcing. Library-safe.

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 (3)

Showing the top 3 NuGet packages that depend on Stratara.Mediator:

Package Downloads
Stratara.Validation

Vendor-neutral request validation for the Stratara framework — a mediator pipeline behavior that runs IValidator<T> implementations before the handler and throws an aggregated StrataraValidationException on failure. No FluentValidation dependency; an optional adapter is shipped separately.

Stratara.Outbox.RabbitMQ

Outbox-pattern command and event dispatch for the Stratara event-sourced stack — RabbitMQ IMessageBus implementation, retry worker, mediator command worker, and Redis-coordinated projection-replay state. Azure Service Bus support ships as the sibling Stratara.Outbox.AzureServiceBus package.

Stratara.Infrastructure

Infrastructure glue for the Stratara framework — authorization decorators, configuration providers, and DI composition helpers that wire Mediator, Outbox, Identity, and EF Core into a hosted app.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.3 47 9/3/2026
4.0.2 168 9/3/2026
4.0.1 94 9/2/2026
4.0.0 296 8/31/2026
4.0.0-preview.1 64 8/31/2026
3.4.0 156 8/28/2026
3.3.0 363 8/25/2026
3.2.3 153 8/22/2026
3.2.2 757 8/14/2026
3.2.1 403 8/2/2026
3.2.0 158 7/18/2026
3.1.7 306 7/1/2026
3.1.6 586 6/22/2026
3.1.5 168 6/22/2026
3.1.4 2,028 6/15/2026
3.1.3 174 6/10/2026
3.1.2 182 6/5/2026
3.1.1 895 6/1/2026
3.1.0 158 5/30/2026
3.0.23 160 5/28/2026

Two operational fixes, both found while a consumer rolled out bus-envelope signing and reasoned
about rebuilding its read models: a projection replay now survives a passing failure, and an
integrity failure says whether the signature was absent or wrong. Additive on every published
surface.

### Added

- **A projection replay retries a failing batch before it gives up.** Each batch — the read from
 the event store and the application of its entries — now runs under a new named policy,
 `ResilienceNames.ProjectionReplayBatch`: five attempts in all, exponential backoff from one second
 with jitter between them, any exception except cancellation. A read-store timeout or a
 dropped connection mid-rebuild no longer ends the replay; a failure that persists through every
 attempt ends it exactly as before, with the same failure record. A retried batch is applied
 again from its first entry in a fresh scope, which relies on the guarantee projections already
 give under at-least-once delivery: a second application converges (see *Write handlers that
 converge rather than accumulate* in the projection guide). Each failed attempt logs a new
 warning, `104_011`. `AddResiliencePipelines` registers six policies rather than five. Nothing
 about truncation, ordering, progress, the failure record or the lease changes.

### Changed

- **An integrity failure now says whether the signature was absent or wrong, and the existing
 warning and error ids fire only for a signature that is present and does not verify.** An
 unsigned envelope — what every not-yet-restarted publisher emits during a `Permissive` roll —
 was logged as *"signature mismatch"* under the same id as a key mismatch or tampering, so an
 operator could not tell a rolling restart from an attack without correlating host start times.
 Four new event ids carry the unsigned case: `105_004` (command, permissive), `111_004` (event
 bundle, permissive), `105_105` (command, strict) and `111_105` (event bundle, strict). The
 existing `105_003`, `111_003`, `105_104` and `111_104` keep their numbers and their level but
 now mean a present signature that did not verify; an alert keyed on one of them goes quiet
 during a roll instead of firing for every unsigned message. Strict-mode rejections name the
 case in their exception message. `BusEnvelopeIntegrityVerifier.Verify` gains an overload with
 `out BusEnvelopeIntegrityFailure` (`None`, `Absent`, `Invalid`); the existing overload and the
 `BusEnvelopeIntegrityResult` values are unchanged. An absent signature no longer reaches the
 signer, which already answered `false` for it.