IronAlpine.Framework.Mediator 3.1.1

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

IronAlpine.Framework.Mediator

In-process mediator for requests (commands/queries), notifications and streams, with an explicit, ordered pipeline and FluentValidation integration.

  • Package id: IronAlpine.Framework.Mediator (3.1.1, net10.0)
  • Depends on: IronAlpine.Framework.Kernel, FluentValidation 12
  • INotification and IDomainEvent live in the Kernel (IronAlpine.Kernel.Primitives).

Installation

<PackageReference Include="IronAlpine.Framework.Mediator" Version="3.1.1" />

Registration

using IronAlpine.Mediator.Extensions;

services.AddIronAlpineMediator(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<CreateOrderCommandHandler>();
    cfg.UseValidationBehavior(typeof(CreateOrderCommandHandler).Assembly);
});

AddIronAlpineMediator throws if no assembly is registered. It registers:

Service Lifetime
IMediator (IronAlpineMediator), ISender, IPublisher transient
every IRequestHandler<,>, INotificationHandler<>, IStreamRequestHandler<,> found in the scanned assemblies (non-abstract classes) transient
open behaviours added with AddOpenBehavior / Use*Behavior transient, in call order
FluentValidation validators from the validation assemblies + IRequestValidator<> scoped

Two request handlers (or two stream handlers) for the same request/response pair throw at registration. Notification handlers can be many.

Configuration API (IronAlpineMediatorConfiguration)

Method Purpose
RegisterServicesFromAssembly(assembly), RegisterServicesFromAssemblyContaining<T>() handler scanning
UseValidationBehavior(params Assembly[]) registers validators from the assemblies (none given: the handler assemblies) and adds ValidationBehavior<,>
AddOpenBehavior(Type) adds an open generic IPipelineBehavior<,> (must have two type arguments)
AddOpenStreamBehavior(Type) adds an open generic IStreamPipelineBehavior<,>
UseNotificationPublishStrategy(NotificationPublishStrategy) Sequential (default) or ParallelWhenAll
ConfigureServices(Action<IServiceCollection>) extra registrations executed by AddIronAlpineMediator (used by package extensions)

Other packages add behaviour shortcuts as extension methods on the same configuration object:

Method Package / namespace
UseLoggingBehavior() IronAlpine.Framework.Observability / IronAlpine.Observability
UseCachingBehavior() IronAlpine.Framework.Caching / IronAlpine.Caching
UseAuditingBehavior(), UseTransactionBehavior(mode), UseResultAwareTransactionBehavior(), UseClassicTransactionBehavior() IronAlpine.Framework.Data / IronAlpine.Data

Pipeline order

Behaviours run in the order they are registered: the first registered behaviour is the outermost. The recommended order is:

services.AddIronAlpineMediator(cfg =>
{
    cfg.RegisterServicesFromAssembly(applicationAssembly);
    cfg.UseLoggingBehavior();                 // 1. log every request, including cache hits and validation failures
    cfg.UseCachingBehavior();                 // 2. return cached query results before any other work
    cfg.UseValidationBehavior(applicationAssembly); // 3. reject invalid requests
    cfg.UseAuditingBehavior();                // 4. write an audit row for IAuditableCommand requests
    cfg.UseResultAwareTransactionBehavior();  // 5. one DB transaction around the handler
});

Behaviours apply only to Send (requests). Publish (notifications, domain events, integration events received from the event bus) does not go through IPipelineBehavior.

A behaviour whose generic constraints do not match a request (for example CachingBehavior requires ICacheableQuery) is skipped for that request.

Requests and handlers

using IronAlpine.Kernel.Results;
using IronAlpine.Mediator.Contracts;

public sealed record CreateOrderCommand(Guid CustomerId, IReadOnlyList<OrderLineDto> Lines) : IRequest<Result<Guid>>;

public sealed class CreateOrderCommandHandler(IRepository<Order> orders, IUnitOfWork unitOfWork)
    : IRequestHandler<CreateOrderCommand, Result<Guid>>
{
    public async Task<Result<Guid>> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        var order = Order.Create(request.CustomerId);
        orders.Add(order);
        await unitOfWork.SaveChangesAsync(cancellationToken);
        return Result.Success(order.Id);
    }
}

// caller
Result<Guid> result = await sender.Send(new CreateOrderCommand(customerId, lines), ct);
object? untyped = await sender.Send((object)command, ct);   // resolves the response type at runtime

IRequest<TResponse> is the only request contract; many applications define their own marker interfaces on top of it (for example ICommand : IRequest<Result> and IQuery<T> : IRequest<Result<T>>) to distinguish commands from queries in custom behaviours. A missing handler throws RequestHandlerNotFoundException (IronAlpine.Mediator.Errors).

Validation

using FluentValidation;

public sealed class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>
{
    public CreateOrderCommandValidator()
    {
        RuleFor(x => x.CustomerId).NotEmpty();
        RuleFor(x => x.Lines).NotEmpty();
    }
}

ValidationBehavior<TRequest, TResponse> (requires TResponse : class):

  • runs every IValidator<TRequest> registered for the request (validators are scoped, internal types included);
  • when invalid and TResponse is Result or Result<T>, returns Result.Failure(Error.ValidationError(errors)) - code Validation.Failed, ErrorType.Validation, one ValidationError(PropertyName, ErrorMessage) per failure (FluentValidation error codes are not copied);
  • for any other response type throws FluentValidation.ValidationException;
  • requests without validators pass through.

Replace the FluentValidation adapter by registering your own IRequestValidator<TRequest> (returns a ValidationSummary) before AddIronAlpineMediator.

Notifications

using IronAlpine.Kernel.Primitives;

public sealed record OrderShippedNotification(Guid OrderId) : INotification;

public sealed class SendShipmentMailHandler : INotificationHandler<OrderShippedNotification>
{
    public Task Handle(OrderShippedNotification notification, CancellationToken cancellationToken) => Task.CompletedTask;
}

await publisher.Publish(new OrderShippedNotification(orderId), ct);
await publisher.Publish((object)notification, ct);   // throws ArgumentException if it is not an INotification
  • Sequential: handlers run one after another in registration order; the first exception stops the rest.
  • ParallelWhenAll: all handlers start at once. They share the same DI scope, so do not use it when handlers use the same scoped DbContext.
  • No handler registered: Publish completes without error.

Streams

public sealed record ExportOrdersRequest(DateOnly From) : IStreamRequest<OrderRow>;

public sealed class ExportOrdersHandler : IStreamRequestHandler<ExportOrdersRequest, OrderRow>
{
    public async IAsyncEnumerable<OrderRow> Handle(
        ExportOrdersRequest request,
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        yield return new OrderRow(Guid.NewGuid(), 10m);
        await Task.CompletedTask;
    }
}

await foreach (var row in sender.CreateStream(new ExportOrdersRequest(from), ct))
{
    // ...
}

Stream behaviours implement IStreamPipelineBehavior<TRequest, TResponse> and are added with AddOpenStreamBehavior.

Custom behaviours

public sealed class TimingBehavior<TRequest, TResponse>(ILogger<TimingBehavior<TRequest, TResponse>> logger)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var started = Stopwatch.GetTimestamp();
        var response = await next();
        logger.LogDebug("{Request} took {Elapsed} ms", typeof(TRequest).Name, Stopwatch.GetElapsedTime(started).TotalMilliseconds);
        return response;
    }
}

services.AddIronAlpineMediator(cfg =>
{
    cfg.RegisterServicesFromAssembly(applicationAssembly);
    cfg.AddOpenBehavior(typeof(TimingBehavior<,>));
});

Add generic constraints (for example where TRequest : IMyCommandMarker) to restrict a behaviour to some requests.

Pitfalls

  • UseValidationBehavior() without assemblies scans the assemblies registered with RegisterServicesFromAssembly(...) for validators (since 3.0.5; up to 3.0.4 it registered no IRequestValidator<> and every Send failed). Pass assemblies explicitly when validators live elsewhere; then only those assemblies are scanned.
  • Call AddIronAlpineMediator once per service collection; a second call adds the behaviours and handlers again.
  • Behaviours do not run for notifications. Logging, validation, caching and transactions are not applied to INotificationHandler<T> code.
  • Handler wrappers are cached per request type for the process lifetime; handlers themselves are resolved from the current scope on every call.
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 (4)

Showing the top 4 NuGet packages that depend on IronAlpine.Framework.Mediator:

Package Downloads
IronAlpine.Framework.Data

EFCore data stack for IronAlpine: repositories, interceptors (audit, soft-delete, versioning), transaction and auditing behaviors, and domain event dispatching. Unified entry point: AddIronAlpineData<TDbContext>. Repository base classes remain public extension points for custom repositories.

IronAlpine.Framework.EventBus

Kafka-backed event bus with outbox/inbox pattern, dead letter support, and replay functionality. Unified namespace: IronAlpine.EventBus. Kafka implementation is internal; only contracts and topology builder are public. SSL cert parameters fully configurable (SslCaLocation, SslCertLocation, SslKeyLocation, SslKeyPassword).

IronAlpine.Framework.Observability

Logging (Serilog), OpenTelemetry tracing/metrics, and Resilience (Polly) for IronAlpine microservices. Provides UseLoggingBehavior() extension for the mediator pipeline. EventMetadataContext and correlation tracking included. All implementations internal.

IronAlpine.Framework.Caching

Distributed caching for IronAlpine: mediator caching behavior, cache contracts/DTOs, Redis-backed cache services, and EF Core-backed cache setting management. Works with IronAlpine.Mediator and IronAlpine.Data.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.1 236 10/21/2026
3.0.9 271 10/15/2026
3.0.8 165 10/13/2026
3.0.7 146 10/13/2026
3.0.6 149 10/9/2026
3.0.5 227 10/5/2026
3.0.4 240 10/2/2026
3.0.3 215 9/30/2026
3.0.2 1,123 7/22/2026
3.0.1 177 7/22/2026
3.0.0 399 7/20/2026
2.0.3 204 7/20/2026
2.0.2 231 7/13/2026
2.0.1 233 7/13/2026
2.0.0 220 7/13/2026
2.0.0-preview.2 97 7/13/2026
2.0.0-preview.1 98 7/6/2026
1.0.2 192 7/3/2026
1.0.1 186 7/3/2026
Loading failed

IronAlpine framework release. See the package README and repository documentation for details.