When to Choose a Lightweight Mediator
MediatR is the default choice for the mediator pattern in .NET. It is well-documented, widely used, and backed by a long track record. If you are on a team that already uses it, you have good reasons to stay.
MicroMediator is a newer option built with different priorities: lower allocation, explicit lifetime management, and no assembly scanning. It is not a drop-in replacement with a faster badge. It makes different trade-offs.
This post works through those trade-offs with real benchmark data so you can make an informed choice. All benchmarks run on .NET 10.0, Intel Core Ultra 7 165H, BenchmarkDotNet v0.15.8. The benchmark suite is publicly available on GitHub.
Where MicroMediator is faster
Basic dispatch
The clearest difference is at the dispatch level, before any handler logic runs.
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator (Singleton) | 24.64 ns | 96 B |
| MediatR | 55.86 ns | 296 B |
MicroMediator caches a typed dispatch wrapper per request type after the first call. MediatR resolves handlers through reflection on each request. That difference accounts for both the speed and allocation gap here.
This is also where the benchmark conditions matter most. MicroMediator uses a Singleton handler. MediatR registers handlers as transient by default via assembly scanning. Singleton dispatch removes DI resolution cost entirely. If you register MicroMediator handlers as Scoped or Transient, the gap narrows. The handler lifetimes post covers this in detail.
With a validation pipeline
Adding FluentValidation pipeline behaviour to both:
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator | 378.0 ns | 1.65 KB |
| MediatR | 824.1 ns | 4.02 KB |
The ratio holds across pipeline overhead. MicroMediator runs the same validation logic in less than half the time with less than half the allocation.
Exception handling
This is the least-discussed difference and one of the more significant ones.
| Method | Failure rate | Mean | Allocated |
|---|---|---|---|
| MicroMediator | 0% | 54.20 ns | 32 B |
| MediatR | 0% | 1,010.19 ns | 536 B |
| MicroMediator | 10% | 228.87 ns | 90 B |
| MediatR | 10% | 1,206.12 ns | 569 B |
| MicroMediator | 50% | 926.77 ns | 320 B |
| MediatR | 50% | 1,754.17 ns | 704 B |
At zero failure rate, MediatR costs 18x more per operation in this benchmark. The gap narrows as failure rate increases and exception handling overhead dominates both paths, but MicroMediator stays consistently cheaper throughout.
The benchmark measures 1,000 requests per invocation with OperationsPerInvoke set accordingly, so the per-operation figures are accurate.
Cold start
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator | 397 μs | 9.5 KB |
| MediatR | 868 μs | 602 KB |
MediatR scans assemblies at startup to discover and register handlers. MicroMediator does not. The 63x allocation difference at cold start is a direct consequence. For serverless functions and containerised workloads that spin up under load, this matters.
Large payloads
Dispatch cost stays consistent regardless of payload size. At 1MB payloads across 100 repeated calls:
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator | 39,298 ns | 72 B |
| MediatR | 43,280 ns | 12,872 B |
The handler logic dominates execution time — both are processing the same bytes. The allocation gap comes entirely from the dispatch mechanism, not the payload itself.
Where MediatR holds its own
Raw throughput
At 10,000 sequential requests, execution time is effectively equal:
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator Sequential | 445 μs | 234 KB |
| MediatR Sequential | 451 μs | 2,187 KB |
MediatR is within 1% on execution time. The memory gap is 9x, but if your workload is not memory-constrained, the speed difference will not be your bottleneck.
Long-running workloads
Over 10,000 iterations of sustained request processing:
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator | 2,268 μs | 234 KB |
| MediatR | 2,049 μs | 2,812 KB |
MediatR is 10% faster here. The allocation ratio is 12x in MicroMediator's favour, but MediatR edges ahead on raw time. This is the benchmark that cuts against the simple narrative. At sustained load with handler logic that is not trivial, MediatR's execution speed is competitive.
Streaming
As covered in the streaming post, both libraries stream at essentially identical speed. At 10,000 items:
| Method | Mean | Allocated |
|---|---|---|
| MicroMediator stream | 5,454 μs | 40,840 B |
| MediatR stream | 5,245 μs | 40,984 B |
MediatR is marginally faster. The streaming performance story is about early exit and memory behaviour, not about which library you use.
Cancellation
Both libraries handle cancellation identically. At 100ms cancellation on a long-running handler:
| Method | Mean |
|---|---|
| MicroMediator | 108,756 μs |
| MediatR | 108,815 μs |
No meaningful difference.
The real trade-off
The benchmark data points to a consistent pattern rather than a simple winner.
MicroMediator allocates significantly less in almost every scenario. At 10,000 requests the ratio is 9x. At cold start it is 63x. Over 10,000 long-running iterations it is 12x. If your application runs in a memory-constrained environment, generates GC pressure under load, or initialises frequently, those numbers compound.
MediatR matches or beats MicroMediator on raw execution speed in sustained workloads. The long-running benchmark shows MediatR running 10% faster over 10,000 iterations. Streaming is essentially identical. At high volume with non-trivial handler logic, the execution time gap closes.
The dispatch speed advantage at 24ns vs 56ns matters when dispatch is a meaningful fraction of your total request cost. In most real applications it is not. A database call at 1ms dwarfs a 30ns dispatch difference by four orders of magnitude.
When to choose MicroMediator
- You are in a memory-constrained environment (containers, serverless, edge)
- Cold start time matters to your workload
- You want explicit lifetime control and are willing to pay for it with more verbose registration
- You are starting a new project and have no existing MediatR investment
- You process high volumes of lightweight requests where allocation compounds
When to stay with MediatR
- You have an existing codebase already using MediatR
- Your team knows it well and migration cost outweighs the allocation savings
- Your workload is sustained and CPU-bound rather than memory-sensitive
- You need the broader ecosystem: community extensions, documentation, and established patterns
Migration
If you do want to switch, the surface area is small. Both libraries implement the mediator pattern with IRequest<T>, IRequestHandler<T, R>, and pipeline behaviours. The main differences are registration style and the streaming interface.
MediatR:
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssemblyContaining<Startup>());
MicroMediator:
services
.AddMediator()
.AddSingletonHandler<GetProductByIdQueryHandler>()
.AddScopedHandler<CreateOrderCommandHandler>();
The explicit registration is more work upfront. It also removes assembly scanning, which is where the cold start cost comes from.
Pipeline behaviours follow the same pattern. Replace IPipelineBehavior<TRequest, TResponse> implementations from MediatR with IPipelineBehavior<TRequest, TResponse> from MicroMediator's abstractions. The interface shape is deliberately similar.
MicroMediator is available on NuGet: TechnicalDogsbody.MicroMediator. The full benchmark suite is on GitHub if you want to run it against your own workload.
Andy Blyth
Andy Blyth, an Optimizely MVP (OMVP) and Technical Architect at MSQ DX with a keen interest in martial arts, occasionally ventures into blogging when memory serves.



