MDator.Abstractions
0.2.10
See the version list below for details.
dotnet add package MDator.Abstractions --version 0.2.10
NuGet\Install-Package MDator.Abstractions -Version 0.2.10
<PackageReference Include="MDator.Abstractions" Version="0.2.10" />
<PackageVersion Include="MDator.Abstractions" Version="0.2.10" />
<PackageReference Include="MDator.Abstractions" />
paket add MDator.Abstractions --version 0.2.10
#r "nuget: MDator.Abstractions, 0.2.10"
#:package MDator.Abstractions@0.2.10
#addin nuget:?package=MDator.Abstractions&version=0.2.10
#tool nuget:?package=MDator.Abstractions&version=0.2.10
MDator
A source-generated, MediatR-compatible mediator for .NET.
MDator replaces MediatR's runtime reflection with a C# incremental source
generator that discovers handlers, composes pipelines, and emits a strongly-typed
IMediator implementation at compile time. The public API mirrors MediatR v12
exactly -- migrating is a namespace find-replace:
- using MediatR;
+ using MDator;
Why MDator?
| MediatR v12 | MDator | |
|---|---|---|
| Handler dispatch | Reflection + MakeGenericType + wrapper cache |
Compile-time switch per request type |
| Pipeline composition | Runtime closure chain, services resolved reflectively | Generated per-request pipeline, behaviors fused at compile time |
| Open generic behaviors | Closed at runtime via DI's open-generic support | Fused by the generator into each concrete pipeline |
| Registration | Assembly scanning at startup | Source-generated [ModuleInitializer], zero startup cost |
| License | Commercial (v13+) | MIT |
Quick start
1. Reference MDator (the generator ships alongside the runtime library):
<PackageReference Include="MDator" Version="..." />
2. Define a request and handler -- identical to MediatR:
using MDator;
public record GetUser(int Id) : IRequest<User>;
public sealed class GetUserHandler : IRequestHandler<GetUser, User>
{
public Task<User> Handle(GetUser request, CancellationToken ct)
=> Task.FromResult(new User(request.Id, $"user-{request.Id}"));
}
3. Register in your composition root:
services.AddMDator();
4. Inject and use IMediator, ISender, or IPublisher:
var user = await mediator.Send(new GetUser(42));
At build time the generator emits a GeneratedMediator class that dispatches
GetUser directly to GetUserHandler -- no reflection, no dictionary lookups.
Features
Request / Response
// With response
public record Ping(string Message) : IRequest<string>;
public class PingHandler : IRequestHandler<Ping, string> { ... }
var pong = await mediator.Send(new Ping("hello"));
// Void (no response)
public record FireAndForget() : IRequest;
public class FireHandler : IRequestHandler<FireAndForget> { ... }
await mediator.Send(new FireAndForget());
// Object-typed dispatch (runtime type switch, no reflection)
object request = new Ping("hi");
object? result = await mediator.Send(request);
Notifications
public record OrderPlaced(int OrderId) : INotification;
public class EmailNotifier : INotificationHandler<OrderPlaced> { ... }
public class AuditNotifier : INotificationHandler<OrderPlaced> { ... }
await mediator.Publish(new OrderPlaced(7)); // fans out to both handlers
Configure the publisher strategy at registration time:
services.AddMDator(cfg =>
{
// Sequential, stop on first error (default, matches MediatR):
cfg.NotificationPublisher = new ForEachAwaitPublisher();
// Parallel, aggregate all errors:
cfg.NotificationPublisher = new TaskWhenAllPublisher();
// Parallel via Task.Run, aggregate:
cfg.NotificationPublisher = new TaskWhenAllContinuationPublisher();
});
Open generic pipeline behaviors
The key differentiator. Declare behaviors with an assembly-level attribute so the source generator can see them at compile time:
[assembly: MDator.OpenBehavior(typeof(LoggingBehavior<,>), Order = 0)]
[assembly: MDator.OpenBehavior(typeof(ValidationBehavior<,>), Order = 1)]
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
Console.WriteLine($"Handling {typeof(TRequest).Name}");
var response = await next();
Console.WriteLine($"Handled {typeof(TRequest).Name}");
return response;
}
}
The generator fuses LoggingBehavior<GetUser, User> directly into the
SendCore_GetUser pipeline -- no runtime generic closure, no
MakeGenericType, no allocation beyond the behavior's own closure captures.
Order controls nesting: lower values run outermost (closest to the caller).
Closed pipeline behaviors
Non-generic behaviors targeting a specific (TRequest, TResponse) pair are
discovered by the generator's class scan and registered under
IPipelineBehavior<TRequest, TResponse>. They are picked up at runtime via
IServiceProvider.GetServices<>():
public class CacheBehavior : IPipelineBehavior<GetUser, User>
{
public async Task<User> Handle(
GetUser request, RequestHandlerDelegate<User> next, CancellationToken ct)
{
// check cache, call next(), populate cache
}
}
FuseOnly mode
For maximum performance, suppress the runtime enumeration fallback entirely:
services.AddMDator(cfg => cfg.FuseOnly = true);
With FuseOnly = true, only [assembly: OpenBehavior]-declared behaviors
execute. Runtime-added behaviors are ignored, and the per-request
GetServices<IPipelineBehavior<,>>() call is eliminated.
Pre / post processors
public class AuditPreProcessor : IRequestPreProcessor<SaveItem>
{
public Task Process(SaveItem request, CancellationToken ct) { ... }
}
public class AuditPostProcessor : IRequestPostProcessor<SaveItem, string>
{
public Task Process(SaveItem request, string response, CancellationToken ct) { ... }
}
Pre-processors run before the handler; post-processors run after a successful response, inside the innermost pipeline scope.
Exception handlers and actions
// Can convert an exception into a response:
public class RecoverHandler
: IRequestExceptionHandler<SaveItem, string, InvalidOperationException>
{
public Task Handle(
SaveItem request,
InvalidOperationException exception,
RequestExceptionHandlerState<string> state,
CancellationToken ct)
{
state.SetHandled("fallback-value");
return Task.CompletedTask;
}
}
// Observes exceptions without converting them (e.g. logging):
public class LogExceptionAction : IRequestExceptionAction<SaveItem, Exception>
{
public Task Execute(SaveItem request, Exception exception, CancellationToken ct) { ... }
}
Exception handlers are ordered by type specificity at compile time (most-derived first). Actions always run, even if a handler marks the exception as handled.
Streaming
public record CountStream(int To) : IStreamRequest<int>;
public class CountHandler : IStreamRequestHandler<CountStream, int>
{
public async IAsyncEnumerable<int> Handle(
CountStream request,
[EnumeratorCancellation] CancellationToken ct)
{
for (var i = 1; i <= request.To; i++)
yield return i;
}
}
await foreach (var n in mediator.CreateStream(new CountStream(5)))
Console.WriteLine(n); // 1, 2, 3, 4, 5
Stream pipeline behaviors (IStreamPipelineBehavior<TRequest, TResponse>) and
open stream behaviors via [assembly: OpenBehavior] are also supported.
Configuration reference
services.AddMDator(cfg =>
{
// Lifetime for all generated registrations (default: Transient)
cfg.Lifetime = ServiceLifetime.Scoped;
// Notification publisher strategy (default: ForEachAwaitPublisher)
cfg.NotificationPublisher = new TaskWhenAllPublisher();
// Suppress runtime behavior enumeration for pure compile-time pipelines
cfg.FuseOnly = true;
// Add a closed behavior at runtime (e.g. from an unscanned assembly)
cfg.AddBehavior<MyClosedBehavior>();
// Source-compat marker (no-op at runtime; the generator sees the call site)
cfg.RegisterServicesFromAssemblyContaining<Program>();
});
Migrating from MediatR
- Replace the
MediatRpackage reference withMDator. - Find-replace
using MediatR;withusing MDator;. - Replace
services.AddMediatR(cfg => ...)withservices.AddMDator(cfg => ...). - Convert open behavior registrations from fluent to attribute:
- cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
+ [assembly: MDator.OpenBehavior(typeof(LoggingBehavior<,>), Order = 0)]
- Build. The generator discovers your handlers and emits the mediator.
All interface shapes (IRequest<T>, IRequestHandler<,>, INotification,
INotificationHandler<>, IPipelineBehavior<,>, IStreamRequest<>,
IStreamRequestHandler<,>, IRequestPreProcessor<>,
IRequestPostProcessor<,>, IRequestExceptionHandler<,,>,
IRequestExceptionAction<,>, Unit, RequestHandlerDelegate<T>,
StreamHandlerDelegate<T>) are source-level identical to MediatR v12.
How it works
MDator ships three assemblies:
| Assembly | TFM | Purpose |
|---|---|---|
MDator.Abstractions |
netstandard2.0 | Interfaces, Unit, attributes. Reference this from handler libraries. |
MDator |
net8.0, net9.0 | Runtime shell: AddMDator, MDatorConfiguration, notification publishers. |
MDator.SourceGenerator |
netstandard2.0 | Roslyn incremental source generator, shipped as an analyzer. |
When you reference MDator, the generator activates in the consuming project
and emits a single file (MDatorGenerated.g.cs) containing:
MDatorGeneratedRegistration-- a static class with a[ModuleInitializer]that registers every discovered handler, behavior, pre/post processor, and exception handler withIServiceCollection.GeneratedMediator-- a sealedIMediatorimplementation with:- A compile-time
switchinSend<TResponse>()dispatching each known request type to a strongly-typedSendCore_*method. - Per-request pipeline methods that chain pre-processors, the handler, post-processors, fused open behaviors, runtime behaviors, and exception wrappers -- all as generated C# with zero reflection.
- Analogous
PublishCore_*,StreamCore_*, andSendVoidCore_*methods.
- A compile-time
Multiple projects can each host handlers. Each project's generator emits its
own module initializer that appends to MDatorGeneratedHook.Registrations.
When AddMDator() runs in the composition root, all callbacks fire.
Project structure
MDator.slnx
src/
MDator.Abstractions/ netstandard2.0 -- interfaces, Unit, attributes
MDator/ net8.0;net9.0 -- runtime, DI extensions, publishers
MDator.SourceGenerator/ netstandard2.0 -- incremental generator (analyzer)
tests/
MDator.Tests/ net9.0, xUnit -- 11 integration tests
Building
dotnet build MDator.slnx
dotnet test tests/MDator.Tests/MDator.Tests.csproj
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on MDator.Abstractions:
| Package | Downloads |
|---|---|
|
MDator
A source-generated, MediatR-compatible mediator for .NET. Replaces MediatR's runtime reflection with compile-time dispatch, pipeline fusion, and zero-reflection handler registration. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.0 | 165 | 5/18/2026 |
| 0.5.0 | 107 | 5/6/2026 |
| 0.4.0 | 109 | 4/26/2026 |
| 0.3.0 | 125 | 4/15/2026 |
| 0.2.22 | 116 | 4/14/2026 |
| 0.2.22-g1d20b9cd07 | 106 | 4/14/2026 |
| 0.2.21 | 106 | 4/14/2026 |
| 0.2.20-g832a3f2f63 | 114 | 4/14/2026 |
| 0.2.17 | 113 | 4/14/2026 |
| 0.2.16 | 105 | 4/14/2026 |
| 0.2.15 | 109 | 4/14/2026 |
| 0.2.13 | 110 | 4/13/2026 |
| 0.2.11 | 101 | 4/13/2026 |
| 0.2.10 | 99 | 4/13/2026 |
| 0.2.9 | 111 | 4/13/2026 |
| 0.2.8 | 113 | 4/13/2026 |
| 0.2.7 | 106 | 4/13/2026 |
| 0.2.5 | 118 | 4/13/2026 |
| 0.2.4 | 265 | 4/13/2026 |
| 0.2.2 | 120 | 4/13/2026 |