PAN.Mediator
1.0.0
dotnet add package PAN.Mediator --version 1.0.0
NuGet\Install-Package PAN.Mediator -Version 1.0.0
<PackageReference Include="PAN.Mediator" Version="1.0.0" />
<PackageVersion Include="PAN.Mediator" Version="1.0.0" />
<PackageReference Include="PAN.Mediator" />
paket add PAN.Mediator --version 1.0.0
#r "nuget: PAN.Mediator, 1.0.0"
#:package PAN.Mediator@1.0.0
#addin nuget:?package=PAN.Mediator&version=1.0.0
#tool nuget:?package=PAN.Mediator&version=1.0.0
PAN.Mediator
Mediador inmutable, ligero y nativo para CQRS y Arquitectura Hexagonal en .NET.
PAN.Mediator es un reemplazo propio y desde cero de MediatR. Apunta a paridad de superficie de API con MediatR — Send/Publish, pipeline behaviors, Unit, y una extensión de registro DI estilo AddXMediator — para que una app que hoy envuelve MediatR pueda migrar cambiando usings y la referencia de paquete, no reescribiendo handlers.
Multi-target: netstandard2.0, net6.0, net8.0.
Instalación
dotnet add package PAN.Mediator
Requisitos
- .NET SDK compatible con
netstandard2.0,net6.0onet8.0. Microsoft.Extensions.DependencyInjection(o cualquier contenedor compatible conIServiceProvider).
Uso rápido
1. Registrar el mediador
services.AddPanMediator(config =>
{
config.RegisterServicesFromAssembly(typeof(Program).Assembly);
config.AddOpenBehavior(typeof(LoggingBehavior<,>));
});
RegisterServicesFromAssembly/RegisterServicesFromAssemblies escanean el assembly y registran automáticamente cualquier IPanRequestHandler<,> e IPanNotificationHandler<> concreto (incluso internal).
Lifetimes configurables (todos Transient por defecto):
config.MediatorLifetime = ServiceLifetime.Scoped;
config.HandlerLifetime = ServiceLifetime.Scoped;
config.BehaviorLifetime = ServiceLifetime.Scoped;
2. Definir un request y su handler
public sealed class PingRequest : IPanRequest<string>
{
public string Message { get; init; } = string.Empty;
}
public sealed class PingRequestHandler : IPanRequestHandler<PingRequest, string>
{
public Task<string> Handle(PingRequest request, CancellationToken cancellationToken)
=> Task.FromResult($"Pong: {request.Message}");
}
Para requests sin respuesta significativa, usar la forma de conveniencia basada en Unit (equivalente a void async):
public sealed class DeleteUserCommand : IPanRequest { }
public sealed class DeleteUserHandler : IPanRequestHandler<DeleteUserCommand>
{
public Task<Unit> Handle(DeleteUserCommand request, CancellationToken cancellationToken)
{
// ...
return Unit.Task;
}
}
3. Enviar el request
public sealed class MyService(IPanMediator mediator)
{
public Task<string> Ping(string message) =>
mediator.Send(new PingRequest { Message = message });
}
4. Pipeline behaviors
public sealed class LoggingBehavior<TRequest, TResponse> : IPanPipelineBehavior<TRequest, TResponse>
where TRequest : IPanRequest<TResponse>
{
public async Task<TResponse> Handle(TRequest request, PanRequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
Console.WriteLine($"Handling {typeof(TRequest).Name}");
var response = await next();
Console.WriteLine($"Handled {typeof(TRequest).Name}");
return response;
}
}
Registrar con config.AddOpenBehavior(typeof(LoggingBehavior<,>)) (genérico abierto, aplica a todos los requests) o config.AddBehavior<MySpecificBehavior>() (un tipo concreto).
El orden de registro importa: el primer behavior registrado queda más "afuera" en la cadena. Si registrás ValidationBehavior y después LoggingBehavior, LoggingBehavior envuelve a ValidationBehavior, que envuelve al handler.
5. Notificaciones (pub/sub)
public sealed class UserCreatedNotification : IPanNotification
{
public required string Email { get; init; }
}
public sealed class SendWelcomeEmailHandler : IPanNotificationHandler<UserCreatedNotification>
{
public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
// enviar email...
return Task.CompletedTask;
}
}
await mediator.Publish(new UserCreatedNotification { Email = "user@example.com" });
Zero handlers registrados es un no-op, no un error. Por defecto los handlers se ejecutan secuencialmente en orden de registro (ver siguiente sección para hacerlo configurable).
Novedad: estrategias de Publish configurables
A diferencia de MediatR clásico, en PAN.Mediator la forma en que Publish invoca a los handlers de una notificación es pluggable vía IPanNotificationPublishStrategy<TNotification>, sin cambiar el contrato de IPanMediator, IPanNotification ni IPanNotificationHandler<T>.
Vienen dos estrategias listas para usar:
SequentialNotificationPublishStrategy<T>(default, sin configurar nada): invoca los handlers uno por uno, en orden de registro. Si uno tira una excepción, los siguientes no se ejecutan — es el comportamiento original.ParallelWhenAllNotificationPublishStrategy<T>: arranca todos los handlers a la vez y espera a que todos terminen conTask.WhenAll, aunque alguno falle. Útil cuando los handlers son independientes (ej. notificar a varios sistemas externos) y no querés que uno lento o roto bloquee al resto.
Cómo activarla
services.AddPanMediator(config =>
{
config.RegisterServicesFromAssembly(typeof(Program).Assembly);
config.UseNotificationPublishStrategy(typeof(ParallelWhenAllNotificationPublishStrategy<>));
});
Se registra como genérico abierto — igual que los pipeline behaviors — y aplica a todos los tipos de notificación del proceso. Si nunca se llama a UseNotificationPublishStrategy, el comportamiento es idéntico al de siempre (secuencial), así que no rompe código existente.
Estrategia propia
También podés implementar tu propia estrategia (por ejemplo, para tolerar fallos, aplicar timeouts por handler, o loggear el fan-out):
public sealed class MyCustomPublishStrategy<TNotification> : IPanNotificationPublishStrategy<TNotification>
where TNotification : IPanNotification
{
public async Task Publish(
TNotification notification,
IEnumerable<IPanNotificationHandler<TNotification>> handlers,
CancellationToken cancellationToken)
{
// tu lógica de fan-out
}
}
config.UseNotificationPublishStrategy(typeof(MyCustomPublishStrategy<>));
Fuera de alcance (v1)
Streaming requests (IStreamRequest), pre/post processors y exception-handling middleware no están implementados — solo Send, Publish y pipeline behaviors. Tampoco hay overloads no genéricos Send(object)/Publish(object).
Desarrollo
# Compilar los tres target frameworks
dotnet build PAN.Mediator.slnx
# Correr toda la suite de tests
dotnet test tests/PAN.Mediator.Tests/PAN.Mediator.Tests.csproj
# Correr un test puntual
dotnet test tests/PAN.Mediator.Tests/PAN.Mediator.Tests.csproj --filter "DisplayName~Send_returns_the_handlers_response"
No hay una aplicación consumidora en este repo — se desarrolla y testea de forma independiente.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
-
net6.0
-
net8.0
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.0.0 | 106 | 8/4/2026 |
Primera versión: Send/Publish, pipeline behaviors, Unit, estrategias de publish secuencial y paralela, registro DI via AddPanMediator.