Mediator.Net
A powerful and flexible mediator implementation for .NET that enables clean architecture by decoupling request/response handling through the mediator pattern.
π Languages
π Table of Contents
- Features
- Installation
- Quick Start
- Usage Examples
- Handler Registration
- Pipeline & Middleware
- Dependency Injection Integration
- Official Middleware Packages
- Advanced Features
- Documentation
- Contributing
- License
- Support
π Features
- Command/Query Separation: Clear separation between commands, queries, and events
- Pipeline Support: Extensible middleware pipeline for cross-cutting concerns
- Streaming Support: Handle multiple responses with
IAsyncEnumerable - Dependency Injection: Built-in support for popular IoC containers
- Event Publishing: Publish events from within handlers
- Flexible Registration: Both explicit and assembly scanning registration
- Middleware Ecosystem: Rich collection of pre-built middlewares
π¦ Installation
Install the main package via NuGet:
Install-Package Mediator.Net
Or via .NET CLI:
dotnet add package Mediator.Net
π Quick Start
Basic Setup
// Create and configure mediator var mediaBuilder = new MediatorBuilder(); var mediator = mediaBuilder.RegisterHandlers(typeof(Program).Assembly).Build();
Define Messages and Handlers
// Command (no response) public class CreateUserCommand : ICommand { public string Name { get; set; } public string Email { get; set; } } public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { public async Task Handle(IReceiveContext<CreateUserCommand> context, CancellationToken cancellationToken) { // Handle the command var user = new User(context.Message.Name, context.Message.Email); // Save user... // Publish an event await context.Publish(new UserCreatedEvent { UserId = user.Id }); } } // Request/Response public class GetUserQuery : IRequest<UserDto> { public int UserId { get; set; } } public class GetUserQueryHandler : IRequestHandler<GetUserQuery, UserDto> { public async Task<UserDto> Handle(IReceiveContext<GetUserQuery> context, CancellationToken cancellationToken) { // Handle the query and return response return new UserDto { Id = context.Message.UserId, Name = "John Doe" }; } } // Event public class UserCreatedEvent : IEvent { public int UserId { get; set; } } public class UserCreatedEventHandler : IEventHandler<UserCreatedEvent> { public async Task Handle(IReceiveContext<UserCreatedEvent> context, CancellationToken cancellationToken) { // Handle the event Console.WriteLine($"User {context.Message.UserId} was created!"); } }
π Usage Examples
Sending Commands
// Command with no response await mediator.SendAsync(new CreateUserCommand { Name = "John Doe", Email = "john@example.com" }); // Command with response var result = await mediator.SendAsync<CreateUserCommand, CreateUserResponse>( new CreateUserCommand { Name = "Jane Doe", Email = "jane@example.com" });
Handling Requests
// Request with response var user = await mediator.RequestAsync<GetUserQuery, UserDto>( new GetUserQuery { UserId = 123 });
Publishing Events
// Publish event to all handlers await mediator.Publish(new UserCreatedEvent { UserId = 123 });
Streaming Responses
Create handlers that return multiple responses:
public class GetMultipleUsersStreamHandler : IStreamRequestHandler<GetUsersQuery, UserDto> { public async IAsyncEnumerable<UserDto> Handle( IReceiveContext<GetUsersQuery> context, [EnumeratorCancellation] CancellationToken cancellationToken) { for (var i = 0; i < 10; i++) { await Task.Delay(100, cancellationToken); yield return new UserDto { Id = i, Name = $"User {i}" }; } } } // Consume the stream await foreach (var user in mediator.CreateStream<GetUsersQuery, UserDto>(new GetUsersQuery())) { Console.WriteLine($"Received: {user.Name}"); }
π§ Handler Registration
Assembly Scanning (Recommended)
var mediator = new MediatorBuilder() .RegisterHandlers(typeof(Program).Assembly) .Build();
Explicit Registration
var mediator = new MediatorBuilder() .RegisterHandlers(() => new List<MessageBinding> { new MessageBinding(typeof(CreateUserCommand), typeof(CreateUserCommandHandler)), new MessageBinding(typeof(GetUserQuery), typeof(GetUserQueryHandler)), new MessageBinding(typeof(UserCreatedEvent), typeof(UserCreatedEventHandler)) }) .Build();
π Pipeline & Middleware
Mediator.Net supports five types of pipelines for different scenarios:
Pipeline Types
| Pipeline | Description | Triggers For |
|---|---|---|
| GlobalReceivePipeline | Executes for all messages | Commands, Requests, Events |
| CommandReceivePipeline | Executes only for commands | ICommand |
| RequestReceivePipeline | Executes only for requests | IRequest |
| EventReceivePipeline | Executes only for events | IEvent |
| PublishPipeline | Executes when events are published | IEvent (outgoing) |
Creating Custom Middleware
1. Create Middleware Extension
public static class LoggingMiddleware { public static void UseLogging<TContext>( this IPipeConfigurator<TContext> configurator, ILogger logger = null) where TContext : IContext<IMessage> { logger ??= configurator.DependencyScope?.Resolve<ILogger>(); configurator.AddPipeSpecification(new LoggingMiddlewareSpecification<TContext>(logger)); } }
2. Create Middleware Specification
public class LoggingMiddlewareSpecification<TContext> : IPipeSpecification<TContext> where TContext : IContext<IMessage> { private readonly ILogger _logger; public LoggingMiddlewareSpecification(ILogger logger) { _logger = logger; } public bool ShouldExecute(TContext context, CancellationToken cancellationToken) => true; public Task BeforeExecute(TContext context, CancellationToken cancellationToken) { _logger.LogInformation("Processing message: {MessageType}", context.Message.GetType().Name); return Task.CompletedTask; } public Task Execute(TContext context, CancellationToken cancellationToken) { return Task.CompletedTask; } public Task AfterExecute(TContext context, CancellationToken cancellationToken) { _logger.LogInformation("Completed processing: {MessageType}", context.Message.GetType().Name); return Task.CompletedTask; } public void OnException(Exception ex, TContext context) { _logger.LogError(ex, "Error processing message: {MessageType}", context.Message.GetType().Name); throw ex; } }
Configuring Pipelines
var mediator = new MediatorBuilder() .RegisterHandlers(typeof(Program).Assembly) .ConfigureGlobalReceivePipe(x => x.UseLogging()) .ConfigureCommandReceivePipe(x => x.UseValidation()) .ConfigureRequestPipe(x => x.UseCaching()) .ConfigureEventReceivePipe(x => x.UseEventStore()) .ConfigurePublishPipe(x => x.UseOutboxPattern()) .Build();
ποΈ Dependency Injection Integration
Microsoft.Extensions.DependencyInjection
Install-Package Mediator.Net.MicrosoftDependencyInjection
services.AddMediator(builder => { builder.RegisterHandlers(typeof(Program).Assembly); });
Autofac
Install-Package Mediator.Net.Autofac
var builder = new ContainerBuilder(); var mediatorBuilder = new MediatorBuilder() .RegisterHandlers(typeof(Program).Assembly); builder.RegisterMediator(mediatorBuilder); var container = builder.Build();
Other Supported Containers
- SimpleInjector:
Mediator.Net.SimpleInjector - StructureMap:
Mediator.Net.StructureMap - Ninject:
Mediator.Net.Ninject
π Official Middleware Packages
Serilog Logging
Install-Package Mediator.Net.Middlewares.Serilog
.ConfigureGlobalReceivePipe(x => x.UseSerilog(LogEventLevel.Information))
Unit of Work
Install-Package Mediator.Net.Middlewares.UnitOfWork
Provides CommittableTransaction support for transactional operations.
EventStore Integration
Install-Package Mediator.Net.Middlewares.EventStore
Automatically persists events to EventStore.
π― Advanced Features
Context Services
Share services between middleware and handlers:
// In middleware public Task Execute(TContext context, CancellationToken cancellationToken) { context.RegisterService(new AuditInfo { Timestamp = DateTime.UtcNow }); return Task.CompletedTask; } // In handler public async Task Handle(IReceiveContext<MyCommand> context, CancellationToken cancellationToken) { if (context.TryGetService(out AuditInfo auditInfo)) { // Use the audit info } }
Publishing Events from Handlers
public async Task Handle(IReceiveContext<CreateOrderCommand> context, CancellationToken cancellationToken) { // Process the command var order = new Order(context.Message.CustomerId); // Publish domain event await context.Publish(new OrderCreatedEvent { OrderId = order.Id, CustomerId = order.CustomerId }); }
π Documentation
For more detailed documentation, examples, and advanced scenarios, visit our Wiki.
π€ Contributing
We welcome contributions! Please see our Contributing Guide for details.
π License
This project is licensed under the MIT License - see the LICENSE.txt file for details.
πββοΈ Support
- π Documentation
- π¬ Stack Overflow (use the
mediator.nettag) - π Issues
β If you find this project useful, please give it a star!

