Package Description
License
—
Deps
3
Install Size
—
Vulns
✓ 0
Published
Dec 7, 2025
$ dotnet add package RmToolkit.MediatorA strongly-typed mediator implementation for .NET — similar to MediatR but explicitly designed for high performance, assembly-scoped behaviors, module isolation, and a clean extensibility model.
Supports:
ISender / IPublisher APIdotnet add package RmToolkit.Mediator
or:
<PackageReference Include="RmToolkit.Mediator" Version="*" />
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMediator(conf =>
{
conf.HandlerAssembliesToRegister = new[]
{
typeof(Program).Assembly,
MyModule.AssemblyInfo.Assembly
};
conf.RequestHandlerLifetime = ServiceLifetime.Transient;
conf.NotificationPublisherLifetime = ServiceLifetime.Transient;
conf.AddBehavior(typeof(ValidationBehavior<,>));
});
var app = builder.Build();
app.Run();
public interface ISender
{
Task<TResponse> SendAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default)
where TRequest : IRequest<TResponse>;
Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
Task<Result> SendAsync(ICommand command, CancellationToken cancellationToken = default);
Task<Result<TResponse>> SendAsync<TResponse>(ICommand<TResponse> command, CancellationToken cancellationToken = default);
}
public interface IPublisher
{
Task PublishAsync<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification;
}
public interface IRequest<TResponse> { }
public interface ICommand : IRequest<Result> { }
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }
public interface IQuery<TResponse> : IRequest<Result<TResponse>> { }
public interface INotification { }
Your mediator is configured via:
builder.Services.AddMediator(conf =>
{
conf.HandlerAssembliesToRegister = [...];
conf.RequestHandlerLifetime = ServiceLifetime.Transient;
conf.NotificationPublisherLifetime = ServiceLifetime.Transient;
conf.AddBehavior(typeof(LoggingBehavior<,>));
});
public IMediatorConfigurator AddBehavior<TBehavior>(
Action<BehaviorBuilder>? configure = null,
ServiceLifetime lifetime = ServiceLifetime.Transient
)
=> AddBehavior(typeof(TBehavior), configure, lifetime);
public IMediatorConfigurator AddBehavior(
Type behaviorType,
Action<BehaviorBuilder>? configure = null,
ServiceLifetime lifetime = ServiceLifetime.Transient
)
{
_services.AddBehavior(behaviorType, configure, lifetime);
return this;
}
public sealed record CreateOrder(int OrderId) : ICommand<int>;
internal sealed class CreateOrderHandler : ICommandHandler<CreateOrder, int>
{
public async Task<Result<int>> Handle(CreateOrder request, CancellationToken ct)
{
return Result.Success(request.OrderId);
}
}
var result = await sender.SendAsync(new CreateOrder(55));
public sealed record UserRegistered(string UserId, string Email) : INotification;
internal class SendWelcomeEmailHandler : INotificationHandler<UserRegistered>
{
public Task Handle(UserRegistered notification, CancellationToken ct)
{
Console.WriteLine($"Sending welcome email to {notification.Email}");
return Task.CompletedTask;
}
}
await publisher.PublishAsync(new UserRegistered("u1", "a@example.com"));
A pipeline behavior wraps the request handler:
public interface IPipelineBehavior<TRequest, TResponse>
{
Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TRequest, TResponse> next,
CancellationToken ct
);
}
public sealed class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TRequest, TResponse> next, CancellationToken ct)
{
Console.WriteLine("Validation...");
return await next(request, ct);
}
}
conf.AddBehavior(typeof(ValidationBehavior<,>));
You can apply a behavior only to handlers in specific assemblies.
conf.AddBehavior(typeof(ModuleAIdempotentBehavior<,>),
b => b.ForAssemblyHandlers(ModuleA.AssemblyInfo.Assembly));
conf.AddBehavior(typeof(ModuleBIdempotentBehavior<,>),
b => b.ForAssemblyHandlers(ModuleB.AssemblyInfo.Assembly));
This lets you:
app.MapGet("/orders/{id:int}", async (int id, ISender sender) =>
{
var result = await sender.SendAsync(new CreateOrder(id));
return result;
});
Yes, unless you configure assembly filtering using BehaviorBuilder.
Yes — module-level behaviors execute only for handlers in the module’s assembly.
Yes, from assemblies listed in:
conf.HandlerAssembliesToRegister
MIT License — free for personal and commercial use.