Mediatly 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Mediatly --version 1.0.0
                    
NuGet\Install-Package Mediatly -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mediatly" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mediatly" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Mediatly" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mediatly --version 1.0.0
                    
#r "nuget: Mediatly, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mediatly@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mediatly&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Mediatly&version=1.0.0
                    
Install as a Cake Tool

Mediatly

A minimal, free (MIT-licensed), single-file CQRS mediator that is API-compatible with MediatR 12.x. Migrating existing MediatR code is usually just a namespace swap (using MediatR;using Mediatly;).

  • Zero dependencies beyond Microsoft.Extensions.DependencyInjection.Abstractions.
  • Commands, queries, notifications (many handlers, sequential), and pipeline behaviors.
  • Reflection at most once per request type, then a cached dictionary lookup.
  • Handlers and mediator registered scoped (safe with EF Core DbContext).
  • Optional reflection-free / Native AOT registration path.

Install

dotnet add package Mediatly

Quick start

using Mediatly;

// 1) Define a request + handler
public record GetUser(int Id) : IRequest<string>;

public class GetUserHandler : IRequestHandler<GetUser, string>
{
    public Task<string> Handle(GetUser request, CancellationToken ct)
        => Task.FromResult($"User#{request.Id}");
}

// 2) Register (reflection scan) + an open-generic behavior
services.AddMediatly(typeof(GetUserHandler).Assembly);
services.AddPipelineBehavior(typeof(LoggingBehavior<,>));

// 3) Send
var mediator = provider.GetRequiredService<IMediator>();
string user = await mediator.Send(new GetUser(42));

Native AOT / trimming

The convenient reflection APIs — assembly scanning (AddMediatly(assembly), cfg.AddHandlersFromAssembly...) and open-generic behaviors (AddPipelineBehavior(typeof(X<,>)), cfg.AddBehavior(typeof(X<,>))) — are not Native-AOT compatible, and this is fundamental to AOT (MediatR has the same restriction), not a Mediatly limitation:

Reflection-style call Why it can't be AOT
assembly scanning reflects over types the trimmer can't see → [RequiresUnreferencedCode] (IL2026)
open-generic behavior typeof(X<,>) closed at runtime via MakeGenericType[RequiresDynamicCode] (IL3050)

For trimmed / Native AOT builds, register everything explicitly and closed — no reflection at dispatch time:

services.AddHandler<GetUser, string, GetUserHandler>();
services.AddHandler<DoThing, DoThingHandler>();                 // void (Unit) request
services.AddNotificationHandler<OrderPlaced, EmailHandler>();
services.AddBehavior<GetUser, string, LoggingBehavior<GetUser, string>>(); // closed behavior

Each of these builds its dispatch wrapper with a plain new at startup (both generic arguments known at compile time). If a request reaches the mediator without a registered wrapper under AOT, you get an InvalidOperationException naming the exact AddHandler<...> call to add.

AOT-safe equivalent of a "scan + AddOpenBehavior ×N" config

A MediatR-style configuration like this is not AOT-compatible:

// ❌ Not AOT: reflection scan + open-generic behaviors
services.AddMediatly(cfg =>
{
    cfg.AddHandlersFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()); // IL2026
    cfg.AddBehavior(typeof(AuthenticationBehavior<,>));                     // IL3050
    cfg.AddBehavior(typeof(ValidationBehavior<,>));
    cfg.AddBehavior(typeof(LoggingBehavior<,>));
});

The AOT-safe form registers each request explicitly and closes the same behavior stack per request type. A one-line helper keeps it ergonomic and preserves order (first registered = outermost):

public static class Registration
{
    public static IServiceCollection AddRequestPipeline<TRequest, TResponse,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler>(
        this IServiceCollection services)
        where TRequest : IRequest<TResponse>
        where THandler : class, IRequestHandler<TRequest, TResponse>
    {
        services.AddHandler<TRequest, TResponse, THandler>();
        services.AddBehavior<TRequest, TResponse, AuthenticationBehavior<TRequest, TResponse>>(); // outermost
        services.AddBehavior<TRequest, TResponse, ValidationBehavior<TRequest, TResponse>>();
        services.AddBehavior<TRequest, TResponse, LoggingBehavior<TRequest, TResponse>>();         // innermost
        return services;
    }
}

// One line per request type — no scanning, no open generics:
services.AddRequestPipeline<GetUser,   string, GetUserHandler>();
services.AddRequestPipeline<PlaceOrder, string, PlaceOrderHandler>();

Trade-off: you list each request type once (O(number of requests) instead of a single scan), and behaviors are closed per request type so each gets its own correctly-ordered pipeline. For a large app, a source generator can emit these AddRequestPipeline<...> calls at build time to restore scan-like ergonomics while staying AOT-safe.

Publishing

dotnet publish -c Release -r win-x64   # requires the "Desktop development with C++" workload

Performance

Vs MediatR 12.4.1 (last Apache-2.0 release). BenchmarkDotNet v0.15.8, .NET 8.0.24, AMD Ryzen 7 5800U (16 logical / 8 physical cores), Windows 10; MediatR is the baseline in each group. Reproduce with the Mediatly.Benchmarks project.

Warm dispatch — Send / Publish / dynamic Send(object)

Method Mean Ratio Allocated Alloc Ratio
Send_Mediatly 89.95 ns 1.72× faster 64 B 4.13× less
Send_MediatR 154.56 ns baseline 264 B baseline
Publish_Mediatly 105.05 ns 1.69× faster 56 B 11.0× less
Publish_MediatR 177.46 ns baseline 616 B baseline
SendObject_Mediatly 97.60 ns 1.99× faster 160 B 2.25× less
SendObject_MediatR 194.68 ns baseline 360 B baseline

Send = plain request/response; Publish = notification to 2 handlers; Send(object) = the non-generic dynamic-dispatch path.

Startup — register + BuildServiceProvider + first (cold) dispatch

Method Mean Allocated Ratio
Startup_Mediatly 6.92 µs 13.28 KB 12.96× faster
Startup_MediatR 89.67 µs 196.75 KB baseline

100,000 requests — batch throughput + memory

Library Time for 100k Memory for 100k req/s
Mediatly 9.56 ms 12.97 MB 10,461,997/s
MediatR 20.01 ms 32.04 MB 4,997,573/s

Notification fan-out — Publish to N handlers

Handlers Mediatly MediatR Alloc ratio
1 107.5 ns / 56 B 174.7 ns / 256 B 4.6× less
5 138.8 ns / 56 B 364.3 ns / 736 B 13× less
20 344.6 ns / 56 B 927.5 ns /2536 B 45× less

Mediatly's per-publish allocation stays flat at 56 B regardless of handler count; MediatR's grows with the number of handlers.

Pipeline scaling — Send through N pass-through behaviors

Behaviors Mediatly MediatR
0 91.8 ns / 64 B 154.1 ns / 264 B
1 158.9 ns /232 B 184.2 ns / 456 B
3 359.6 ns /440 B 258.0 ns / 744 B
5 253.3 ns /648 B 314.9 ns /1032 B

Per-behavior overhead is similar for both, and Mediatly allocates less at every stage. On a shared machine the 3-behavior Mediatly figure showed high run-to-run variance (±42 ns, P95 411 ns), so treat the ordering at that single point as noise.

Multi-threaded throughput — scope per request, 16 logical cores

Library 1-thread req/s all-core req/s speedup
Mediatly 3,940,561/s 12,724,068/s 3.2×
MediatR 3,239,471/s 11,095,351/s 3.4×

Summary: ~1.7× faster on warm dispatch, ~13× faster to cold-start, and 4–45× less allocation depending on scenario. The gains come from Mediatly doing less (no streaming / processors / publish strategies). Numbers are indicative — rerun with the Mediatly.Benchmarks project; sub-microsecond benchmarks vary on a shared machine.

Not included (by design)

IStreamRequest/streaming behaviors, request pre/post processors, and custom publish strategies are intentionally omitted to keep the library tiny. They can be added as extensions.

License

MIT.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 116 7/29/2026
1.0.1 108 7/29/2026
1.0.0 105 7/29/2026

1.0.0 — Initial public release. Minimal CQRS mediator API-compatible with MediatR 12.x: commands, queries, notifications, and pipeline behaviors, with reflection-based scanning plus a reflection-free / Native AOT registration path.