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
<PackageReference Include="IronAlpine.Framework.Mediator" Version="3.1.1" />
<PackageVersion Include="IronAlpine.Framework.Mediator" Version="3.1.1" />
<PackageReference Include="IronAlpine.Framework.Mediator" />
paket add IronAlpine.Framework.Mediator --version 3.1.1
#r "nuget: IronAlpine.Framework.Mediator, 3.1.1"
#:package IronAlpine.Framework.Mediator@3.1.1
#addin nuget:?package=IronAlpine.Framework.Mediator&version=3.1.1
#tool nuget:?package=IronAlpine.Framework.Mediator&version=3.1.1
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,FluentValidation12 INotificationandIDomainEventlive 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
TResponseisResultorResult<T>, returnsResult.Failure(Error.ValidationError(errors))- codeValidation.Failed,ErrorType.Validation, oneValidationError(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 scopedDbContext.- No handler registered:
Publishcompletes 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 withRegisterServicesFromAssembly(...)for validators (since 3.0.5; up to 3.0.4 it registered noIRequestValidator<>and everySendfailed). Pass assemblies explicitly when validators live elsewhere; then only those assemblies are scanned.- Call
AddIronAlpineMediatoronce 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 | Versions 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. |
-
net10.0
- FluentValidation (>= 12.0.0)
- FluentValidation.DependencyInjectionExtensions (>= 12.0.0)
- IronAlpine.Framework.Kernel (>= 3.1.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
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 |
IronAlpine framework release. See the package README and repository documentation for details.