MediatorEdge 2.0.0
dotnet add package MediatorEdge --version 2.0.0
NuGet\Install-Package MediatorEdge -Version 2.0.0
<PackageReference Include="MediatorEdge" Version="2.0.0" />
<PackageVersion Include="MediatorEdge" Version="2.0.0" />
<PackageReference Include="MediatorEdge" />
paket add MediatorEdge --version 2.0.0
#r "nuget: MediatorEdge, 2.0.0"
#:package MediatorEdge@2.0.0
#addin nuget:?package=MediatorEdge&version=2.0.0
#tool nuget:?package=MediatorEdge&version=2.0.0
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
IMediatoris 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 | Versions 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. |
-
net10.0
-
net8.0
-
net9.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
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.