MediatorEdge 2.0.0

dotnet add package MediatorEdge --version 2.0.0
                    
NuGet\Install-Package MediatorEdge -Version 2.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="MediatorEdge" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MediatorEdge" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="MediatorEdge" />
                    
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 MediatorEdge --version 2.0.0
                    
#r "nuget: MediatorEdge, 2.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 MediatorEdge@2.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=MediatorEdge&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=MediatorEdge&version=2.0.0
                    
Install as a Cake Tool

MediatorEdge

MediatorEdge is a lightweight, async-friendly implementation of the Mediator pattern for modern .NET applications. It is designed to keep your controllers, Minimal API endpoints, and application services thin by decoupling what you want to do (a request or event) from who handles it.

It is a small, focused alternative to larger mediator libraries — no pipeline behaviors, no streaming, no reflection-heavy configuration — just clean Send and Publish semantics powered by Microsoft.Extensions.DependencyInjection.

Note: MediatorEdge is a closed-source library. This NuGet package is published publicly and free to use in your own projects, but the source code is not distributed.


Features

  • Request/Response — dispatch a request to exactly one handler and get a strongly-typed result back (great for CQRS commands and queries).
  • Notifications — publish an event to zero-or-many handlers, executed concurrently with Task.WhenAll.
  • Async by default — every dispatch supports CancellationToken.
  • DI-native — handlers are resolved from IServiceProvider; assembly scanning registers them for you.
  • Minimal API friendly — designed to be injected directly into endpoint delegates.
  • Zero configuration — one call to AddSimpleMediator(...) and you're done.
  • Targets .NET 10 with nullable reference types enabled.

Installation

Install via the .NET CLI:

dotnet add package MediatorEdge

Or via the Package Manager Console:

Install-Package MediatorEdge

Or add it directly to your .csproj:

<PackageReference Include="MediatorEdge" Version="1.0.0" />

Quick Start

1. Register MediatorEdge

In Program.cs, register the mediator and scan an assembly for handlers:

using MediatorEdge;

var builder = WebApplication.CreateBuilder(args);

// Scans the assembly containing Program for all IRequestHandler<,> and INotificationHandler<> implementations
builder.Services.AddSimpleMediator(typeof(Program).Assembly);

var app = builder.Build();

You can pass multiple assemblies if your handlers live in different projects:

builder.Services.AddSimpleMediator(
    typeof(Program).Assembly,
    typeof(GetUserQuery).Assembly,
    typeof(UserCreatedEvent).Assembly);

2. Inject IMediator where you need it

public class UsersController : ControllerBase
{
    private readonly IMediator _mediator;

    public UsersController(IMediator mediator) => _mediator = mediator;
}

Or directly in a Minimal API endpoint:

app.MapGet("/users/{id:int}", async (int id, IMediator mediator, CancellationToken ct) =>
{
    var user = await mediator.Send(new GetUserQuery(id), ct);
    return user is null ? Results.NotFound() : Results.Ok(user);
});

Requests (Commands & Queries)

A request is a message that returns a response and is handled by exactly one handler. This is the foundation of CQRS.

Defining a Query

using MediatorEdge;

public record GetUserQuery(int Id) : IRequest<UserDto>;

public record UserDto(int Id, string Name, string Email);

public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
{
    private readonly IUserRepository _repository;

    public GetUserHandler(IUserRepository repository) => _repository = repository;

    public async Task<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken)
    {
        var user = await _repository.FindByIdAsync(request.Id, cancellationToken);
        return new UserDto(user.Id, user.Name, user.Email);
    }
}

Defining a Command

Commands typically mutate state. If you don't need a return value, use a simple result type such as Unit, bool, or the created entity's ID.

public record CreateUserCommand(string Name, string Email) : IRequest<int>;

public class CreateUserHandler : IRequestHandler<CreateUserCommand, int>
{
    private readonly IUserRepository _repository;
    private readonly IMediator _mediator;

    public CreateUserHandler(IUserRepository repository, IMediator mediator)
    {
        _repository = repository;
        _mediator = mediator;
    }

    public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        var user = new User { Name = request.Name, Email = request.Email };
        await _repository.AddAsync(user, cancellationToken);

        // Fire a notification so other parts of the system can react
        await _mediator.Publish(new UserCreatedEvent(user.Id, user.Email), cancellationToken);

        return user.Id;
    }
}

Dispatching Requests

// Minimal API
app.MapPost("/users", async (CreateUserCommand cmd, IMediator mediator, CancellationToken ct) =>
{
    var id = await mediator.Send(cmd, ct);
    return Results.Created($"/users/{id}", new { id });
});

app.MapGet("/users/{id:int}", async (int id, IMediator mediator, CancellationToken ct) =>
{
    var user = await mediator.Send(new GetUserQuery(id), ct);
    return Results.Ok(user);
});

Notifications (Events / Fan-out)

A notification is a message that can be handled by zero, one, or many handlers. All handlers run concurrently.

Defining a Notification

public record UserCreatedEvent(int UserId, string Email) : INotification;

Multiple Handlers

public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedEvent>
{
    private readonly IEmailService _email;

    public SendWelcomeEmailHandler(IEmailService email) => _email = email;

    public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        await _email.SendAsync(
            to: notification.Email,
            subject: "Welcome!",
            body: "Thanks for signing up.",
            cancellationToken);
    }
}

public class AuditLogHandler : INotificationHandler<UserCreatedEvent>
{
    private readonly IAuditLog _audit;

    public AuditLogHandler(IAuditLog audit) => _audit = audit;

    public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
        => _audit.WriteAsync($"User {notification.UserId} created", cancellationToken);
}

public class ProvisionDefaultsHandler : INotificationHandler<UserCreatedEvent>
{
    public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        // Provision default settings, workspace, etc.
        return Task.CompletedTask;
    }
}

Publishing

await mediator.Publish(new UserCreatedEvent(user.Id, user.Email), cancellationToken);

All three handlers above will be invoked in parallel. Publish completes when all handlers have finished (via Task.WhenAll). If any handler throws, the aggregate exception propagates to the caller.


Full Example — Minimal API + CQRS

using MediatorEdge;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IUserRepository, InMemoryUserRepository>();
builder.Services.AddSingleton<IEmailService, ConsoleEmailService>();
builder.Services.AddSimpleMediator(typeof(Program).Assembly);

var app = builder.Build();

app.MapGet("/users/{id:int}", async (int id, IMediator mediator, CancellationToken ct)
    => Results.Ok(await mediator.Send(new GetUserQuery(id), ct)));

app.MapPost("/users", async (CreateUserCommand cmd, IMediator mediator, CancellationToken ct)
    => Results.Created($"/users/{await mediator.Send(cmd, ct)}", null));

app.Run();

// ---- Contracts ----
public record GetUserQuery(int Id) : IRequest<UserDto>;
public record CreateUserCommand(string Name, string Email) : IRequest<int>;
public record UserCreatedEvent(int UserId, string Email) : INotification;
public record UserDto(int Id, string Name, string Email);

API Reference

Type Description
IRequest<TResponse> Marker interface for a request that returns TResponse.
IRequestHandler<TRequest, TResponse> Handles a single request and returns a Task<TResponse>. One handler per request type.
INotification Marker interface for a fan-out event.
INotificationHandler<TNotification> Handles a notification. Zero or many handlers per notification type.
IMediator Runtime entry point — exposes Send and Publish.
Mediator Default IMediator implementation, resolved from DI.
ServiceCollectionExtensions.AddSimpleMediator(...) Registers IMediator and scans the given assemblies for handlers.

IMediator

Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
Task Publish(INotification notification, CancellationToken cancellationToken = default);

Lifetimes

  • IMediator is registered as Scoped.
  • All discovered handlers are registered as Scoped.

This means handlers share the scope of the incoming request (e.g. an HTTP request), so they can safely take scoped dependencies like DbContext.


Requirements

  • .NET 10 or newer
  • Microsoft.Extensions.DependencyInjection.Abstractions (transitively referenced)

License

Distributed under the MIT license. The package binary is free to use in commercial and personal projects. Source code is not publicly distributed.


Author

Built and maintained by Sheriff Kurdi.

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
2.0.0 90 9/27/2026
1.0.0 160 12/28/2025

v2.0.0
- Multi-target support for net8.0, net9.0, and net10.0 (previously net10.0 only).
- Aligned Microsoft.Extensions.DependencyInjection.Abstractions to 8.0.0 for broader compatibility.
- XML documentation now shipped with the package for full IntelliSense support.
- Deterministic builds enabled.
- Expanded README with quick-start, CQRS, and notification examples.