Skip to Content

Handler Lifetimes in MicroMediator

Why Your Choice Matters

Most developers reach for a mediator library, register their handlers, and move on. Lifetime management is an afterthought. That decision quietly shapes the performance and correctness of every request your application processes.

MicroMediator makes lifetime explicit by design. There is no global default. You choose Singleton, Scoped, or Transient per handler at registration. That forces a decision that most libraries let you skip.

This post explains what each lifetime means, how to choose between them, and what the performance difference looks like in practice.


The three lifetimes

Singleton

A Singleton handler is created once and shared across every request for the lifetime of the application.

builder.Services
    .AddMediator()
    .AddSingletonHandler<GetProductByIdQueryHandler>();

MicroMediator caches the handler instance and the dispatch wrapper after the first request. Subsequent calls resolve with no DI overhead. Benchmarks show 24.64ns per request and 96 bytes allocated on .NET 10 (Intel Core Ultra 7 165H).

Use Singleton when your handler has no dependencies, or only depends on other Singleton services. Stateless handlers are the most common case: database reads where the repository is itself Singleton, pure transformations, in-memory lookups.

The constraint is thread safety. A Singleton handler will be called concurrently. If it holds mutable state, you have a race condition.

Scoped

A Scoped handler is created once per request scope. In ASP.NET Core, that maps to once per HTTP request.

builder.Services
    .AddMediator()
    .AddScopedHandler<CreateOrderCommandHandler>();

Use Scoped when your handler depends on Scoped services. The most common cases are DbContext, HttpContext, and unit-of-work patterns. These services are designed to live for exactly one request. Injecting them into a Singleton handler causes a captive dependency problem: the long-lived handler holds a reference to a short-lived service, which has likely already been disposed.

Performance is lower than Singleton because DI resolution happens per request. In practice, that overhead is negligible compared to a database call.

Transient

A Transient handler is created fresh for every request.

builder.Services
    .AddMediator()
    .AddTransientHandler<ProcessPaymentCommandHandler>();

Use Transient when your handler needs to hold state that is unique to a single operation. This is the least common case. Most handlers are either stateless (Singleton) or tied to a request scope (Scoped).

Performance is similar to Scoped since both require DI resolution per request.


Choosing the right lifetime

The decision follows a simple rule: match the lifetime of your handler to the shortest-lived dependency it takes.

Handler depends onUse
Nothing, or only Singleton servicesSingleton
DbContext, HttpContext, or other Scoped servicesScoped
Per-operation mutable stateTransient

When in doubt, start with Scoped. It is safe for the majority of real-world handlers and avoids the captive dependency trap. Promote to Singleton once you are confident the handler and all its dependencies are stateless.


What the benchmarks show

All benchmarks run on .NET 10.0, Intel Core Ultra 7 165H, BenchmarkDotNet v0.15.8.

Basic dispatch

The baseline comparison uses a Singleton handler with identical logic in both libraries — a simple value multiplication with no pipeline overhead.

MethodMeanAllocated
MicroMediator (Singleton)24.64 ns96 B
MediatR55.86 ns296 B

MicroMediator dispatches in roughly half the time and allocates 3x less. The gap comes from the dispatch mechanism: MicroMediator caches a typed wrapper per request type after the first call. MediatR resolves through reflection on each request.

With validation pipeline

Adding a FluentValidation pipeline behaviour to both libraries:

MethodMeanAllocated
MicroMediator378.0 ns1.65 KB
MediatR824.1 ns4.02 KB

The ratio holds. MicroMediator runs the same validation logic in less than half the time with less than half the allocation.

Throughput at scale

At 10,000 sequential requests, raw execution time converges because the handler logic dominates:

MethodMeanAllocated
MicroMediator Sequential445 μs234 KB
MediatR Sequential451 μs2,187 KB

Execution time is within 1% of each other. Memory allocation is not: MediatR allocates 9x more across the same workload. At sustained load in a memory-constrained environment, that difference compounds through GC pressure.

Cold start

Cold start matters for serverless functions and containerised workloads where instances spin up under load.

MethodMeanAllocated
MicroMediator397 μs9.5 KB
MediatR868 μs602 KB

MicroMediator initialises 2.2x faster and allocates 63x less on first request. MediatR's assembly scanning drives the allocation cost. MicroMediator's explicit registration scans nothing.


Explicit registration as a forcing function

Most mediator libraries scan assemblies and register handlers automatically. Convenient, but it hides lifetime decisions. You get transient by default without thinking about it.

MicroMediator requires explicit registration. That feels like friction at first. In practice it means lifetime is always a conscious choice, not an accident.

For small projects the difference is minor. For enterprise codebases with dozens of handlers spanning multiple teams, explicit lifetimes reduce the class of bugs that are genuinely hard to diagnose. The cold start numbers above show the other payoff: no assembly scanning means faster initialisation and a fraction of the startup allocation.


What comes next

The next post covers streaming large datasets with IAsyncEnumerable. Understanding Singleton handler performance matters there too, because streaming handler registration follows the same lifetime model.

The package is available on NuGet: TechnicalDogsbody.MicroMediator. Source and benchmarks are on GitHub.

Andy Blyth

Andy Blyth, an Optimizely MVP (OMVP) and Technical Architect at 26 DX with a keen interest in martial arts, occasionally ventures into blogging when memory serves.

optimizely-mvp-technology

SaaS CMS Cert

contentful-certified-professional

Andy Blyth