Mediator.Slim
2.0.0
dotnet add package Mediator.Slim --version 2.0.0
NuGet\Install-Package Mediator.Slim -Version 2.0.0
<PackageReference Include="Mediator.Slim" Version="2.0.0" />
<PackageVersion Include="Mediator.Slim" Version="2.0.0" />
<PackageReference Include="Mediator.Slim" />
paket add Mediator.Slim --version 2.0.0
#r "nuget: Mediator.Slim, 2.0.0"
#:package Mediator.Slim@2.0.0
#addin nuget:?package=Mediator.Slim&version=2.0.0
#tool nuget:?package=Mediator.Slim&version=2.0.0
Mediator
High-performance Mediator pattern implementation for .NET with CQRS support. Inspired by MediatR with focus on performance and simplicity.
โจ Features
- ๐ High Performance - Static caching, minimal allocations, aggressive inlining, zero-allocation publish for synchronous handlers
- ๐ฆ CQRS Ready - Request/Response, Commands, Queries, and Notifications
- ๐ Pipeline Behaviors - Cross-cutting concerns (logging, validation, caching)
- โก Streaming Support -
IAsyncEnumerablefor large datasets - ๐ก๏ธ Validation - Built-in validation behavior with custom validators
- ๐ฏ Pre/Post Processors - Execute logic before/after handlers
- ๐ DI Integration - Native Microsoft.Extensions.DependencyInjection support
๐ฆ Installation
dotnet add package Mediator.Slim
DI registration (AddMediator) is included in the package โ no extra package needed.
Requirements: .NET 10.0 or later. Use version 1.0.1 for .NET 8.0.
๐ What's New in 2.0.0
- โฌ๏ธ .NET 10 - Targets
net10.0(breaking: .NET 8 is no longer supported) withMicrosoft.Extensions.*10.0 packages. - ๐งฌ Polymorphic publish -
Publishdispatches on the notification's runtime type, so publishing through a base type (e.g.DomainEventBase) or asobjectreaches the concrete type's handlers. - โก Faster notifications - Publishing to 3+ handlers is non-async, uses
ArrayPool, and allocates nothing when every handler completes synchronously.
| Handlers (sync) | 1.0.1 | 2.0.0 |
|---|---|---|
| 3 | 58 ns / 144 B | 16 ns / 0 B |
| 10 | 160 ns / 144 B | 37 ns / 0 B |
| 50 | 697 ns / 144 B | 182 ns / 0 B |
BenchmarkDotNet on .NET 10, publish fan-out to N handlers; the 1.0.1 column is the 1.0.1 code path run on .NET 10. For async handlers time is unchanged and allocations are lower.
๐ Quick Start
1. Define a Request and Handler
// Request
public record GetUserQuery(int UserId) : IRequest<UserDto>;
// Handler
public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken ct)
{
return new UserDto(request.UserId, "John Doe");
}
}
2. Register Services
services.AddMediator(config =>
{
config.AddAssembly(typeof(Program).Assembly);
});
3. Send Requests
public class UserController
{
private readonly IMediator _mediator;
public UserController(IMediator mediator) => _mediator = mediator;
public async Task<UserDto> GetUser(int id)
{
return await _mediator.Send(new GetUserQuery(id));
}
}
๐ Usage Examples
Commands (No Response)
public record CreateUserCommand(string Name, string Email) : IRequest;
public class CreateUserHandler : IRequestHandler<CreateUserCommand, Unit>
{
public async Task<Unit> Handle(CreateUserCommand request, CancellationToken ct)
{
// Create user logic
return Unit.Value;
}
}
Notifications (Pub/Sub)
public record UserCreated(int UserId, string Name) : INotification;
public class SendWelcomeEmail : INotificationHandler<UserCreated>
{
public async Task Handle(UserCreated notification, CancellationToken ct)
{
// Send email
}
}
public class LogUserCreated : INotificationHandler<UserCreated>
{
public async Task Handle(UserCreated notification, CancellationToken ct)
{
// Log event
}
}
// Usage
await _mediator.Publish(new UserCreated(1, "John"));
All handlers are started and awaited together; Publish completes when every handler has completed and throws if any handler fails.
Publishing through a base type dispatches to the handlers of the runtime type:
public abstract record DomainEventBase : INotification;
public record OrderCreated(int OrderId) : DomainEventBase;
DomainEventBase domainEvent = new OrderCreated(42);
await _mediator.Publish(domainEvent); // invokes INotificationHandler<OrderCreated>
Streaming (IAsyncEnumerable)
public record GetLogsStream(DateTime From) : IStreamRequest<LogEntry>;
public class GetLogsHandler : IStreamRequestHandler<GetLogsStream, LogEntry>
{
public async IAsyncEnumerable<LogEntry> Handle(
GetLogsStream request,
[EnumeratorCancellation] CancellationToken ct)
{
await foreach (var log in _db.GetLogsAsync(request.From, ct))
{
yield return log;
}
}
}
Pipeline Behaviors
public class LoggingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
_logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
var response = await next();
_logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
return response;
}
}
// Registration
services.AddMediator(config =>
{
config.AddAssembly(typeof(Program).Assembly);
config.AddBehavior(typeof(LoggingBehavior<,>));
});
Validation
public class CreateUserValidator : IValidator<CreateUserCommand>
{
public Task<ValidationResult> ValidateAsync(
CreateUserCommand request,
CancellationToken ct)
{
if (string.IsNullOrEmpty(request.Name))
return Task.FromResult(
ValidationResult.Failure("Name", "Name is required"));
return Task.FromResult(ValidationResult.Success);
}
}
// Registration
services.AddMediator(config =>
{
config.AddAssembly(typeof(Program).Assembly);
config.AddBehavior(typeof(ValidationBehavior<,>));
});
services.AddTransient<IValidator<CreateUserCommand>, CreateUserValidator>();
โ๏ธ Configuration
services.AddMediator(config =>
{
// Scan assemblies for handlers
config.AddAssembly(typeof(Program).Assembly);
// Configure lifetimes
config.MediatorLifetime = ServiceLifetime.Scoped;
config.HandlerLifetime = ServiceLifetime.Transient;
config.BehaviorLifetime = ServiceLifetime.Transient;
// Add behaviors (executed in order)
config.AddBehavior(typeof(LoggingBehavior<,>));
config.AddBehavior(typeof(ValidationBehavior<,>));
config.AddBehavior(typeof(RequestPreProcessorBehavior<,>));
config.AddBehavior(typeof(RequestPostProcessorBehavior<,>));
});
๐งช Testing
The library includes 84 comprehensive unit tests (xUnit v3) covering:
- โ Request/Response handling
- โ Notifications (single, multiple and 3+ handlers, polymorphic publish)
- โ Pipeline behaviors
- โ Pre/Post processors
- โ Validation
- โ Streaming with IAsyncEnumerable
- โ Cancellation support
- โ Exception handling
dotnet test
global.json opts dotnet test into Microsoft.Testing.Platform, which xUnit v3 requires on the .NET 10 SDK.
๐ Project Structure
Mediator/
โโโ src/
โ โโโ Mediator/ # NuGet package: Mediator.Slim
โ โโโ Abstractions/ # Interfaces
โ โโโ Behaviors/ # Pipeline behaviors
โ โโโ Exceptions/ # Custom exceptions
โ โโโ Validation/ # Validation support
โ โโโ Wrappers/ # Handler wrappers
โ โโโ Mediator.cs # Main implementation
โ โโโ MediatorServiceConfiguration.cs
โ โโโ ServiceCollectionExtensions.cs # AddMediator registration
โ โโโ Unit.cs # Unit type for void returns
โโโ tests/
โ โโโ Mediator.Tests/ # Unit tests
โโโ benchmarks/
โโโ Mediator.Benchmarks/ # BenchmarkDotNet vs MediatR
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
This project is licensed under the MIT License.
| 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
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.ObjectPool (>= 10.0.12)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.