CrashBytes.Mediator 1.1.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package CrashBytes.Mediator --version 1.1.2
                    
NuGet\Install-Package CrashBytes.Mediator -Version 1.1.2
                    
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="CrashBytes.Mediator" Version="1.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CrashBytes.Mediator" Version="1.1.2" />
                    
Directory.Packages.props
<PackageReference Include="CrashBytes.Mediator" />
                    
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 CrashBytes.Mediator --version 1.1.2
                    
#r "nuget: CrashBytes.Mediator, 1.1.2"
                    
#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 CrashBytes.Mediator@1.1.2
                    
#: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=CrashBytes.Mediator&version=1.1.2
                    
Install as a Cake Addin
#tool nuget:?package=CrashBytes.Mediator&version=1.1.2
                    
Install as a Cake Tool

CrashBytes.Mediator

A small, free, MIT-licensed mediator for .NET. Implements the request/response, notification, and pipeline-behavior patterns that applications typically pull in MediatR for, with no licensing strings.

  • Targets: net8.0, net9.0
  • Dependencies: Microsoft.Extensions.DependencyInjection.Abstractions
  • License: MIT

Why this exists

MediatR moved to a commercial license in late 2025. Most teams only need a few hundred lines of mediator plumbing — request dispatch, notification broadcast, and a middleware pipeline. This package delivers exactly that, with cached compiled-delegate dispatch so the per-call overhead is a dictionary lookup plus a virtual call.

Install

dotnet add package CrashBytes.Mediator

Quick start

using CrashBytes.Mediator;
using Microsoft.Extensions.DependencyInjection;

// 1. Define a request and its handler.
public record Ping(string Message) : IRequest<string>;

public class PingHandler : IRequestHandler<Ping, string>
{
    public Task<string> Handle(Ping request, CancellationToken ct)
        => Task.FromResult($"Pong: {request.Message}");
}

// 2. Register and scan an assembly.
var services = new ServiceCollection();
services.AddCrashBytesMediator(typeof(Program));

// 3. Resolve and send.
var provider = services.BuildServiceProvider();
var mediator = provider.GetRequiredService<IMediator>();
var response = await mediator.Send(new Ping("hi"));   // "Pong: hi"

Notifications (fire-and-forget broadcast)

public record OrderPlaced(int OrderId) : INotification;

public class SendReceiptEmail : INotificationHandler<OrderPlaced>
{
    public Task Handle(OrderPlaced n, CancellationToken ct) { /* ... */ return Task.CompletedTask; }
}

public class UpdateInventory : INotificationHandler<OrderPlaced>
{
    public Task Handle(OrderPlaced n, CancellationToken ct) { /* ... */ return Task.CompletedTask; }
}

await mediator.Publish(new OrderPlaced(42));

All registered handlers run sequentially in registration order. An exception from one handler propagates and short-circuits the rest.

Pipeline behaviors (middleware)

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger) => _logger = logger;

    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        _logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
        var response = await next();
        _logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
        return response;
    }
}

// Behaviors must be registered explicitly — order matters.
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

The first registered behavior is the outermost wrapper. With the registration above, a Send call traces:

LoggingBehavior.Handle  →  ValidationBehavior.Handle  →  TheHandler.Handle

A behavior that does not call next() short-circuits the pipeline.

Void requests

For commands that don't return a value, implement the parameterless IRequest:

public record DeleteOrder(int Id) : IRequest;

public class DeleteOrderHandler : IRequestHandler<DeleteOrder, Unit>
{
    public Task<Unit> Handle(DeleteOrder request, CancellationToken ct)
    {
        // ... do work ...
        return Unit.Task;
    }
}

await mediator.Send(new DeleteOrder(1));

Public API surface

Type Purpose
IRequest<TResponse> Marker for requests with a response.
IRequest Marker for requests with no response (returns Unit).
IRequestHandler<TRequest, TResponse> Single handler per request type.
IRequestHandler<TRequest> Convenience for void requests.
INotification Marker for broadcasts.
INotificationHandler<TNotification> Zero or more handlers per notification.
IPipelineBehavior<TRequest, TResponse> Middleware around Send.
RequestHandlerDelegate<TResponse> Pipeline next delegate.
IMediator / Mediator Dispatcher interface and default implementation.
Unit Void return type for IRequest.
services.AddCrashBytesMediator(typeof(Marker)) DI registration + handler scan.

Performance

The first dispatch for a given request type compiles a small expression tree that calls the handler's Handle method directly. The compiled delegate is cached on a static ConcurrentDictionary keyed by request type, so subsequent calls cost a dictionary lookup plus a virtual invoke — no per-call reflection.

Out of scope (for now)

  • Streaming requests (IStreamRequest<T> / IAsyncEnumerable<T>) — planned for 1.2.
  • Notification publish strategies (parallel, whenAll, etc.) — currently sequential foreach only.
  • Custom DI containers — use Microsoft.Extensions.DependencyInjection. Any container that implements IServiceProvider and resolves IEnumerable<T> correctly will work, but only MEDI is tested.

License

MIT. See LICENSE.

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 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. 
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.7 88 9/15/2026
1.1.5 132 7/15/2026
1.1.4 118 7/1/2026
1.1.3 121 6/10/2026
1.1.2 122 6/10/2026
1.0.1 124 3/30/2026
1.0.0 132 3/3/2026