martinothamar/Mediator: a source-generated mediator for .NET
A high performance implementation of Mediator pattern in .NET using source generators.
At a glance
- What is it?
- Mediator is a .NET implementation of the mediator pattern that generates DI registration, dispatch code and diagnostics at build time, targeting .NET Standard 2.0 and .NET 8 with full Native AOT support. It is aimed at teams already using MediatR-style request/handler pipelines who need AOT compatibility or faster cold start.
- Who is it for?
- Adopt Mediator if you already structure application code around request/handler pipelines and need Native AOT support or faster cold start than reflection-based dispatch allows. Skip it if your solution has many projects that each define handlers and would each need the SourceGenerator package, since the README warns that installing it into every layer of one deployed artifact leads to errors.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 29 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Mediator solves for .NET teams
The mediator pattern exists to keep cross-cutting concerns (logging, metrics, validation) out of constructors and out of the call sites that invoke business logic. A request object goes into a mediator, a handler picks it up, and pipeline behaviors wrap the call. The cost of that indirection in .NET has traditionally been reflection: a runtime scan of assemblies to find handlers, a dictionary lookup, and a delegate invocation built at startup.
Mediator replaces that runtime scan with a source generator. The README states the library targets .NET Standard 2.0 and .NET 8, and that it provides an API similar to MediatR while delivering better performance and full Native AOT support. That last point is the real differentiator. Reflection-based dispatch is a common reason a .NET application cannot be published with Native AOT, because trimming removes the metadata the runtime needs to find handler types. Generating the registration and the dispatch code at compile time removes that dependency.
The intended audience is a team that already organizes code as requests, handlers and pipeline behaviors, and now wants either a smaller cold start (serverless, edge, mobile) or an AOT-published binary. It is not a general-purpose messaging library and it does not replace a bus between processes.
How the source generator builds dispatch code
The generator does three things according to the README: it generates code for DI registration, generates the IMediator implementation, and emits diagnostics about messages and handlers.
The generated IMediator is not a single generic method that boxes arguments and looks up a handler by type. Request, command and query Send methods are monomorphized, meaning a distinct strongly typed method is produced per request type. Dictionary lookups are kept only for the paths that take object or generic arguments, and the README notes those lookups matter above a certain project size threshold. Both IMediator and the concrete Mediator class are usable, and the README says the concrete class allows for better performance, which makes sense: an interface call can be devirtualized but the concrete type removes the question entirely.
The diagnostics are the part teams underestimate. If a handler is not defined for a request, a warning is emitted at build time. Configuration mistakes surface during development instead of as a runtime resolution failure under load. That shifts a class of production incident into the compiler output, which is a genuine change in failure mode rather than a cosmetic one.
Pipelines, notifications and streaming messages are all part of the abstraction surface. The README documents pipeline behaviors, notification publishers, polymorphic dispatch for notification handlers, and open generics for both pipeline behaviors and notification handlers. Streaming messages are supported through a stream request category in the benchmarks.
Installing Mediator and sending a first request
Two packages are involved. Mediator.SourceGenerator is installed in the edge or outermost executable project (an ASP.NET Core app, a worker service, a Function app). Mediator.Abstractions is installed alongside it and in the projects that define messages and handlers. The README gives the CLI form:
dotnet add package Mediator.SourceGenerator --version 3.0.*
dotnet add package Mediator.Abstractions --version 3.0.*The same README shows the equivalent PackageReference form, and it matters that the generator is marked private. The generator is a build-time analyzer, so it should not flow to consumers of a library:
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.*">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Mediator.Abstractions" Version="3.0.*" />The README is explicit about placement: do not install the SourceGenerator into every layer or project in the same deployed artifact, because that leads to errors. Registration happens once, in the executable project. After adding the packages, the generated code handles DI registration, so the application registers Mediator through the generated extension rather than by scanning assemblies at startup. The README's getting-started section then walks through defining an IRequest<> type, adding pipeline behaviors, and using notifications and streaming messages.
One practical check after the first build: look at the generated sources in the analyzer output. If the generator did not run, the IMediator implementation will not exist and the build will fail on the missing type rather than at runtime, which is the intended behavior.
Where Mediator is the wrong choice
The package placement rule is the sharpest limitation. Mediator.SourceGenerator belongs in the outermost executable project, and the README warns that installing it into every layer of the same deployed artifact leads to errors. A solution structured as many small class libraries, each with its own handlers and each referencing the generator, does not fit that model. Teams in that shape either consolidate the generator reference into the host project or stay on a reflection-based mediator.
The second constraint is lifetime. The README states the library yields the best performance when using the Singleton service lifetime. If your handlers depend on scoped services such as a DbContext, you cannot simply register everything as singleton, and the benchmark advantage narrows. The README also points to the benchmarks folder for varying lifetimes and project sizes, which implies the fast path is not uniform across configurations.
The third is versioning discipline. The README commits to strict semantic versioning and describes the API as stable, changing only for good reason. That is a benefit for consumers, but it also means the library will not chase every API shape MediatR adds. If your codebase depends on a MediatR feature that Mediator does not implement, the README's differences-from-MediatR section is where to check before migrating. Finally, the most recent release listed is v3.1.0-rc.1, a release candidate, so teams that avoid prerelease packages will be on the 3.0.2 line.
Mediator compared with MediatR
MediatR is the reference implementation most .NET developers know, and the README names it directly as the library Mediator provides a similar API to. The difference is in when work happens. MediatR discovers handlers through assembly scanning and builds the dispatch machinery at runtime, which is flexible and works without a build-time dependency. Mediator moves registration and dispatch generation into the compiler, which is why it can support Native AOT and why a missing handler becomes a build warning instead of a startup exception.
That trade has a cost. MediatR will happily resolve handlers from assemblies loaded after startup; Mediator's generated code is fixed at compile time. MediatR does not care which project references it; Mediator's generator has a placement rule. MediatR's flexibility is exactly what makes trimming and AOT difficult, and Mediator's compile-time rigidity is exactly what makes them work.
For teams not pursuing AOT and not measuring cold start, the practical difference is smaller than the benchmark table suggests. The generated diagnostics are arguably the more useful day-to-day feature, because they turn handler wiring mistakes into build output.
Licence, maintenance and upgrade cost
Mediator is MIT licensed, and the README describes it as free and open source, forever. MIT permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive licence with no copyleft obligation on your application code. This is a description of the licence text, not legal advice; have counsel review if your organisation has specific requirements.
The repository is not archived, and the last push was on 2026-09-01. Recent releases listed are v3.1.0-rc.1 and v3.0.2, both dated 2026-03-22, and v3.1.0-preview.22 dated 2026-03-02. There is a gap between the most recent release and the last push, which is consistent with ongoing work that has not been tagged, but the release list alone does not show what changed after March.
Upgrade cost is bounded by the versioning policy. The README states the library follows semantic versioning strictly and describes the API as stable, changing only for good reason. In practice that means patch and minor upgrades within 3.x should not require code changes, and the 3.1.0-rc.1 line is where new work lands first. The generator is a build-time dependency, so an upgrade that changes generated code shape will surface as a compile error rather than a runtime surprise, which is the failure mode you want.
Editorial conclusion
Adopt Mediator if you already structure application code around request/handler pipelines and need Native AOT support or faster cold start than reflection-based dispatch allows. Skip it if your solution has many projects that each define handlers and would each need the SourceGenerator package, since the README warns that installing it into every layer of one deployed artifact leads to errors. Before committing, verify that your handler registration fits the single outermost project the generator expects, and check which package versions your build resolves from the 3.0.* or 3.1.0-rc.1 lines.
Frequently asked questions
What is martinothamar/Mediator?
It is a .NET implementation of the mediator pattern built on source generators, providing an API similar to MediatR. The README states it targets .NET Standard 2.0 and .NET 8 and supports Native AOT without reflection or runtime code generation.
How do I set up Mediator in a .NET project?
Install Mediator.SourceGenerator in the outermost executable project and Mediator.Abstractions alongside it and in projects that define messages and handlers. The README warns against installing the SourceGenerator into every layer of the same deployed artifact, because that leads to errors.
Does Mediator work with Native AOT?
Yes. The README lists full Native AOT support without reflection or runtime code generation as a goal, and notes cold start performance matters for serverless, edge, apps and mobile scenarios.
How does Mediator differ from MediatR?
The README says Mediator provides a similar API to MediatR while delivering better performance and full Native AOT support. Mediator generates DI registration and the IMediator implementation at build time, and emits diagnostics such as a warning when no handler is defined for a request.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/martinothamar-mediator)