Zero structural overhead mediator
Compiled at build time.
Runs like a direct call.
No reflection. No runtime overhead.
A mediator that disappears
in your pipeline.
Four design decisions that keep DSoftStudio.Mediator within nanoseconds of a direct method call — even with five pipeline behaviors stacked on top.
No runtime composition.
Define a request, write a handler, register with one call. The source generator inspects your registrations and bakes the dispatch table at compile time — no reflection scan at startup, no expression trees, no delegate wrapping on the hot path.
- Three steps from zero to handler
- Real stack traces — no proxies, no DispatchProxy
- AOT-safe by construction
Constant allocation cost.
Nothing per Send on .NET 11, and the same figure at 0, 3 or 5 behaviors — depth changes the latency, never the allocation. On .NET 10 it is a flat 72 B, the async state machine that runtime async removes. Publish is zero-alloc on both. Constant allocation profiles give you tighter p99 / p999 latency under load.
- 0 B / Send on .NET 11 · 72 B on .NET 10 · 0 B / Publish
- No delegate and no closure per behavior
- Validated with BenchmarkDotNet · .NET 10 and .NET 11
Notifications by exact compile-time type.
The source generator emits a closed dispatch table. Publishing DerivedEvent never invokes INotificationHandler<BaseEvent>. No reflection walk. No duplicate-handler problem.
- Closed dispatch table at compile time
- No accidental base-type fan-out
- Production-validated · 2000+ parallel requests
Straightforward migration.
IRequest, INotification and IStreamRequest markers are identical. Handlers swap Task for ValueTask, namespaces change, and registration moves to AddMediator. Pipeline behaviors need a small refactor — next() becomes next.Handle(request, ct). Your architecture stays.
- Same Send / Publish call sites
- Task → ValueTask in handlers · namespace swap
- Handler lifetimes derived from the constructor
- Step-by-step migration guide on docs.dsoftstudio.com
The cost of your pipeline should be
your code — not your mediator.
Microbenchmarks measure framework overhead in isolation. We measured something more honest: a real enterprise pipeline — Validation → Logging → Metrics → async DB write — across four mediator libraries, each measured against its own direct call so the comparison is what the mediator added, not whose machine was faster.
GC pressure compounds at scale.
DSoft allocates 144 B per request in this pipeline — no more than calling the handler directly, which measures 168 B in the same benchmark. MediatR allocates 1,016 B for the same work; at 10k req/s that is ~8.7 MB/s of short-lived Gen0 garbage you weren't budgeting for.
Allocation profile shapes tail latency.
More GC collections mean more variance in p99 / p999. Constant-allocation pipelines produce tighter distributions — and tighter distributions are what SLAs are written against.
Pipeline depth shouldn't change cost.
DSoft allocates nothing per Send whether you have 0, 3 or 5 behaviors. MediatR wraps each behavior in a new delegate and closure, so allocation grows with depth: 248 B at zero behaviors and 1,024 B at five — a far hotter Gen0 path for the same handler.
Implicit pipelines become opaque.
DSoft emits the full behavior chain at compile time — visible in the source-generated code, inspectable in your IDE, fully deterministic. Mediators that assemble the chain at runtime through service resolution hide the order: behavior interactions become inferred rather than read.
This mediator does not execute your pipeline.
It becomes your pipeline at compile time.
No surprises.
No hidden cost.
No runtime magic.
A source-generated mediator for .NET 8 through .NET 11. Drop it into your composition root and keep your existing handlers.
dotnet add package DSoftStudio.Mediator