SkillAgentSearch skills...

Mediator

A high performance implementation of Mediator pattern in .NET using source generators.

Install / Use

/learn @martinothamar/Mediator

README

GitHub Downloads<br/> Abstractions NuGet current SourceGenerator NuGet current<br/> Abstractions NuGet prerelease SourceGenerator NuGet prerelease<br/>

[!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
  • 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 IMediator implementation
    • Request/Command/Query Send methods are monomorphized
    • Fast dictionary lookups for methods taking object/generic arguments (takes effect above a certain project size threshold)
    • You can use both IMediator and the concrete Mediator class, the latter allows for better performance
  • 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

Using MediatR in .NET? Maybe replace it with this

Table of Contents

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 concrete Mediator class generated by this library
  • <ColdStart | Notification | Request | StreamRequest>_IMediator: call through the IMediator interface in this library
  • <ColdStart | Notification | Request | StreamRequest>_MediatR: the MediatR library

Benchmark category descriptions

  • ColdStart - time to resolve IMediator from IServiceProvider and send a single request
  • Notification - publish a single notification
  • Request - publish a single request
  • StreamRequest - stream a single request which yields 3 responses without delay

See the benchmarks/ folder for more detailed information, including varying lifetimes and project sizes.

Benchmarks

3. Usage and abstractions

There are two NuGet packages needed to use this library

  • Mediator.SourceGenerator
    • To generate the IMediator implementation and dependency injection setup.
  • Mediator.Abstractions
    • Message types (IRequest<,>, INotification), handler types (IRequestHandler<,>, INotificationHandler<>), pipeline types (IPipelineBehavior)

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 interface
  • IStreamMessage - marker interface
  • IBaseRequest - marker interface for requests
  • IRequest - 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 commands
  • ICommand - 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 queries
  • IQuery<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

View on GitHub
GitHub Stars3.4k
CategoryDevelopment
Updated15h ago
Forks168

Languages

C#

Security Score

100/100

Audited on Mar 29, 2026

No findings