Mediator
A high performance implementation of Mediator pattern in .NET using source generators.
Install / Use
/learn @martinothamar/MediatorREADME
[!NOTE] Want to contribute? See the Contributing Guide for information on building, testing, and submitting changes.
Mediator
This is a high performance .NET implementation of the Mediator pattern using source generators. It provides a similar API to the great MediatR library while delivering better performance and full Native AOT support. Packages target .NET Standard 2.0 and .NET 8.
The mediator pattern is great for implementing cross cutting concerns (logging, metrics, etc) and avoiding "fat" constructors due to lots of injected services.
Goals for this library
- High performance
- Efficient and fast by default, slower configurations are opt-in (in the spirit of non-pessimization)
- See benchmarks for comparisons
- AOT friendly
- Full Native AOT support without reflection or runtime code generation
- Cold start performance matters for lots of scenarios (serverless, edge, apps, mobile)
- Build time errors instead of runtime errors
- The generator includes diagnostics (example: if a handler is not defined for a request, a warning is emitted)
- Catch configuration mistakes during development, not during runtime
- Stability
- Stable API that only changes for good reason - fewer changes means less patching for you
- Follows semantic versioning strictly
In particular, a source generator in this library is used to
- Generate code for DI registration
- Generate code for the
IMediatorimplementation- Request/Command/Query
Sendmethods are monomorphized - Fast dictionary lookups for methods taking
object/generic arguments (takes effect above a certain project size threshold) - You can use both
IMediatorand the concreteMediatorclass, the latter allows for better performance
- Request/Command/Query
- Generate diagnostics related messages and message handlers
NuGet packages:
Mediator.SourceGenerator- Typically installed in edge/outermost executable projects (for example: ASP.NET Core app, worker service, Function app)
- Do not install it into every layer/project in the same deployed artifact, that will lead to errors
Mediator.Abstractions- Install this alongside the SourceGenerator reference and in projects that define messages and handlers
dotnet add package Mediator.SourceGenerator --version 3.0.*
dotnet add package Mediator.Abstractions --version 3.0.*
or
<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.*" />
See this great video by @Elfocrash / Nick Chapsas, covering both similarities and differences between Mediator and MediatR
Table of Contents
- Mediator
- Table of Contents
- 2. Benchmarks
- 3. Usage and abstractions
- 4. Getting started
- 4.1. Add packages
- 4.2. Add Mediator to DI container
- 4.3. Create
IRequest<>type - 4.4. Use pipeline behaviors
- 4.5. Constrain
IPipelineBehavior<,>message with open generics - 4.6. Use notifications
- 4.7. Polymorphic dispatch with notification handlers
- 4.8. Notification handlers also support open generics
- 4.9. Notification publishers
- 4.10. Use streaming messages
- 5. Diagnostics
- 6. Differences from MediatR
- 7. Versioning
- 8. Related projects
2. Benchmarks
Here is a brief comparison benchmark hightighting the difference between MediatR and this library using Singleton lifetime.
Note that this library yields the best performance when using the Singleton service lifetime.
<ColdStart | Notification | Request | StreamRequest>_Mediator: the concreteMediatorclass generated by this library<ColdStart | Notification | Request | StreamRequest>_IMediator: call through theIMediatorinterface in this library<ColdStart | Notification | Request | StreamRequest>_MediatR: the MediatR library
Benchmark category descriptions
ColdStart- time to resolveIMediatorfromIServiceProviderand send a single requestNotification- publish a single notificationRequest- publish a single requestStreamRequest- stream a single request which yields 3 responses without delay
See the benchmarks/ folder for more detailed information, including varying lifetimes and project sizes.

3. Usage and abstractions
There are two NuGet packages needed to use this library
- Mediator.SourceGenerator
- To generate the
IMediatorimplementation and dependency injection setup.
- To generate the
- Mediator.Abstractions
- Message types (
IRequest<,>,INotification), handler types (IRequestHandler<,>,INotificationHandler<>), pipeline types (IPipelineBehavior)
- Message types (
You install the source generator package into your edge/outermost project (i.e. ASP.NET Core application, Background worker project),
and then use the Mediator.Abstractions package wherever you define message types and handlers.
Standard message handlers are automatically picked up and added to the DI container in the generated AddMediator method.
Pipeline behaviors need to be added manually (including pre/post/exception behaviors).
For example implementations, see the /samples folder. See the ASP.NET Core clean architecture sample for a more real world setup.
3.1. Message types
IMessage- marker interfaceIStreamMessage- marker interfaceIBaseRequest- marker interface for requestsIRequest- a request message, no return value (ValueTask<Unit>)IRequest<out TResponse>- a request message with a response (ValueTask<TResponse>)IStreamRequest<out TResponse>- a request message with a streaming response (IAsyncEnumerable<TResponse>)IBaseCommand- marker interface for commandsICommand- a command message, no return value (ValueTask<Unit>)ICommand<out TResponse>- a command message with a response (ValueTask<TResponse>)IStreamCommand<out TResponse>- a command message with a streaming response (IAsyncEnumerable<TResponse>)IBaseQuery- marker interface for queriesIQuery<out TResponse>- a query message with a response (ValueTask<TResponse>)IStreamQuery<out TResponse>- a query message with a streaming response (IAsyncEnumerable<TResponse>)INotification- a notification message, no return value (ValueTask)
As you can see, you can achieve the exact same thing with requests, commands and queries. But I find the distinction in naming useful if you for example use the CQRS pattern or for some reason have a preference on naming in your application.
3.2. Handler types
IRequestHandler<in TRequest>IRequestHandler<in TRequest, TResponse>IStreamRequestHandler<in TRequest, out TResponse>ICommandHandler<in TCommand>ICommandHandler<in TCommand, TResponse>IStreamCommandHandler<in TCommand, out TResponse>IQueryHandler<in TQuery, TResponse>IStreamQueryHandler<in TQuery, out TResponse>INotificationHandler<in TNotification>
These types are used in correlation with the message types above.
3.3. Pipeline types
IPipelineBehavior<TMessage, TResponse>IStreamPipelineBehavior<TMessage, TResponse>MessagePreProcessor<TMessage, TResponse>MessagePostProcessor<TMessage, TResponse>- `StreamMessagePreProcessor<TMessage, TRespons
Related Skills
node-connect
341.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.4kCommit, push, and open a PR

