Lightweight in-process mediator for .NET 10 β queries, events, middleware & pluggable dispatch strategies.
Mediate is a simple, zero-dependency in-process messaging library for .NET 10 based on the Mediator pattern. It helps you build loosely coupled applications by routing queries (request/response) and events (fan-out notifications) through a central hub, with full middleware pipeline support and swappable dispatch strategies.
Designed to be small, explicit, and easy to extend β no magic, no hidden conventions.
| Package | Description | NuGet |
|---|---|---|
Mediate |
Core library β mediator, queries, events, middleware | |
Mediate.BackgroundEventDispatch |
ASP.NET Core extension β fire-and-forget background event dispatch |
dotnet add package Mediate// Program.cs
builder.Services.AddMediate();
// Register your handlers and middlewares from an assembly
builder.Services.AddMediateClassesFromAssembly(typeof(Program).Assembly);
// Pick a dispatch strategy (default: sequential)
builder.Services.AddMediateSequentialEventDispatchStrategy();public record GetUserQuery(int Id) : IQuery<UserDto>;
public class GetUserHandler : IQueryHandler<GetUserQuery, UserDto>
{
public Task<UserDto> Handle(GetUserQuery query, CancellationToken cancellationToken)
=> Task.FromResult(new UserDto(query.Id, "Alice"));
}public record UserCreatedEvent(int UserId) : IEvent;
public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
public Task Handle(UserCreatedEvent @event, CancellationToken cancellationToken)
{
// send email...
return Task.CompletedTask;
}
}public class UserService(IMediator mediator)
{
public async Task<UserDto> GetUser(int id)
=> await mediator.Send(new GetUserQuery(id));
public async Task CreateUser(int id)
{
// ... create user ...
await mediator.Dispatch(new UserCreatedEvent(id));
}
}Send a query and get exactly one response. Each query type maps to exactly one handler.
TResult result = await mediator.Send(new MyQuery(...));Dispatch an event to zero or more handlers simultaneously.
await mediator.Dispatch(new MyEvent(...));Wrap query and event handling with middleware β great for logging, validation, tracing, and cross-cutting concerns. Middleware runs in registration order.
Query middleware:
public class LoggingMiddleware<TQuery, TResult> : IQueryMiddleware<TQuery, TResult>
where TQuery : IQuery<TResult>
{
public async Task<TResult> Execute(
TQuery query,
NextMiddlewareDelegate<TResult> next,
CancellationToken cancellationToken)
{
Console.WriteLine($"Handling {typeof(TQuery).Name}");
TResult result = await next();
Console.WriteLine($"Done handling {typeof(TQuery).Name}");
return result;
}
}Event middleware:
public class LoggingEventMiddleware<TEvent> : IEventMiddleware<TEvent>
where TEvent : IEvent
{
public async Task Execute(
TEvent @event,
NextMiddlewareDelegate next,
CancellationToken cancellationToken)
{
Console.WriteLine($"Dispatching {@event.GetType().Name}");
await next();
}
}Control how event handlers are invoked by swapping the dispatch strategy:
| Strategy | Behavior |
|---|---|
SequentialEventDispatchStrategy |
Handlers run one after another; collects all exceptions into AggregateException |
ParallelEventDispatchStrategy |
Handlers run concurrently via Parallel.ForEachAsync |
EventQueueDispatchStrategy |
Fire-and-forget background queue (requires Mediate.BackgroundEventDispatch) |
// Sequential (default)
services.AddMediateSequentialEventDispatchStrategy();
// Parallel
services.AddMediateParallelEventDispatchStrategy();
// Custom
services.AddMediateCustomDispatchStrategy<MyStrategy>(ServiceLifetime.Scoped);For fine-grained control over provider registration:
services.AddMediateCore()
.AddServiceProviderHandlerProvider()
.AddServiceProviderMiddlewareProvider();You can plug in completely custom handler or middleware providers:
services.AddMediateCore()
.AddCustomHandlerProvider<MyHandlerProvider>()
.AddCustomMiddlewareProvider<MyMiddlewareProvider>();Auto-register all handlers and middlewares from an assembly in one call:
services.AddMediateClassesFromAssembly(typeof(Program).Assembly);The Mediate.BackgroundEventDispatch package adds a fire-and-forget dispatch strategy backed by an in-memory queue and an ASP.NET Core BackgroundService.
dotnet add package Mediate.BackgroundEventDispatchbuilder.Services.AddMediate();
builder.Services.AddMediateEventQueueDispatchStrategy();Events dispatched with mediator.Dispatch(...) are enqueued immediately and processed asynchronously in the background. The queue has a bounded capacity of 10 items with semaphore-based backpressure.
Custom exception handling:
builder.Services.AddMediateEventQueueDispatchStrategyCore()
.AddCustomExceptionHandler<MyExceptionHandler>();Full documentation, guides, and API reference at:
IMediator
βββ Send<TResult>(IQuery<TResult>) β one handler + query middleware pipeline
βββ Dispatch<TEvent>(TEvent) β N handlers + event middleware pipeline
βββ via IEventDispatchStrategy
Providers resolve handlers and middlewares from the DI container:
IQueryHandlerProvider/IEventHandlerProviderIQueryMiddlewareProvider/IEventMiddlewareProvider
Default implementations (ServiceProviderHandlerProvider, ServiceProviderMiddlewareProvider) delegate directly to IServiceProvider.
Contributions, issues, and feature requests are welcome! Feel free to open an issue or submit a pull request.
MIT Β© DementCore
