CleanArch.DevKit.Mediator 1.1.1

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

CleanArch.DevKit.Mediator

Médiateur générant son code à la compilation pour .NET 10. Découverte des handlers via Roslyn, dispatch zéro-réflexion, aucune scan d'assembly au runtime.

Rôle

Découple l'application des handlers de commandes/queries/événements via une interface IMediator. Contrairement aux médiateurs classiques, les handlers sont détectés à la compilation par un générateur de source : la classe Mediator et la méthode AddMediator() sont émises avec des appels typés directs — pas de MakeGenericMethod, pas de cache de delegates, pas de réflexion sur le chemin chaud.

Installation

dotnet add package CleanArch.DevKit.Mediator

Le générateur Roslyn est embarqué dans le package : aucun package séparé à installer.

Fonctionnalités

  • Déclarer une classe partial Mediator pour activer la génération
  • Envoyer une requête avec réponse (IRequest<TResponse>)
  • Envoyer une commande fire-and-forget (IRequest)
  • Publier une notification à plusieurs handlers (INotification)
  • Consommer un flux de réponses (IStreamRequest<TResponse>)
  • Ajouter un comportement de pipeline (IPipelineBehavior<,>)
  • Choisir la stratégie de publication des notifications (séquentielle ou parallèle)
  • Diagnostics Roslyn à la compilation (MED001MED003)

Déclarer la classe Mediator

Ajouter un fichier qui contient la classe partielle vide. Le générateur la complète :

namespace MyApp;

public partial class Mediator { }

Puis enregistrer dans le conteneur DI :

services.AddMediator();

La classe doit être dans un namespace (pas en global) et dans le même projet que les handlers — le générateur ne scanne pas les assemblies référencées.


Envoyer une requête avec réponse

public sealed record GetUser(int Id) : IRequest<User>;

public sealed class GetUserHandler(IUserRepository repo) : IRequestHandler<GetUser, User>
{
    public Task<User> Handle(GetUser query, CancellationToken ct)
        => repo.FindAsync(query.Id, ct);
}

// Appel
var user = await mediator.Send(new GetUser(42), ct);

Envoyer une commande fire-and-forget

IRequest (sans paramètre de type) est un raccourci pour IRequest<Unit> — utile quand la commande ne renvoie rien :

public sealed record DeleteUser(int Id) : IRequest;

public sealed class DeleteUserHandler(IUserRepository repo) : IRequestHandler<DeleteUser>
{
    public async Task<Unit> Handle(DeleteUser cmd, CancellationToken ct)
    {
        await repo.DeleteAsync(cmd.Id, ct);
        return Unit.Value;
    }
}

await mediator.Send(new DeleteUser(42), ct);

Unit est un readonly record struct qui matérialise l'absence de valeur.


Publier une notification

Une notification peut être traitée par zéro, un ou plusieurs handlers :

public sealed record UserCreated(int Id) : INotification;

public sealed class SendWelcomeEmail : INotificationHandler<UserCreated>
{
    public Task Handle(UserCreated evt, CancellationToken ct) => /* ... */;
}

public sealed class UpdateAuditLog : INotificationHandler<UserCreated>
{
    public Task Handle(UserCreated evt, CancellationToken ct) => /* ... */;
}

await mediator.Publish(new UserCreated(42), ct);

Consommer un flux de réponses

public sealed record GetLogs(DateTime Since) : IStreamRequest<LogEntry>;

public sealed class GetLogsHandler(ILogRepository repo) : IStreamRequestHandler<GetLogs, LogEntry>
{
    public async IAsyncEnumerable<LogEntry> Handle(
        GetLogs query,
        [EnumeratorCancellation] CancellationToken ct)
    {
        await foreach (var entry in repo.StreamAsync(query.Since, ct))
            yield return entry;
    }
}

// Consommation
await foreach (var entry in mediator.CreateStream(new GetLogs(start), ct))
    Console.WriteLine(entry);

Annoter le CancellationToken du handler avec [EnumeratorCancellation] est nécessaire pour que WithCancellation(...) côté appelant fonctionne.


Ajouter un comportement de pipeline

Un IPipelineBehavior<TRequest, TResponse> enveloppe l'exécution du handler — utile pour la journalisation, la validation, le retry, etc. Les behaviors s'exécutent dans l'ordre d'enregistrement, le premier inscrit étant le plus externe :

public sealed class TimingBehavior<TRequest, TResponse>(ILogger<TimingBehavior<TRequest, TResponse>> logger)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        var sw = Stopwatch.StartNew();
        var response = await next(ct);
        logger.LogInformation("{Request} took {Ms}ms", typeof(TRequest).Name, sw.ElapsedMilliseconds);
        return response;
    }
}

// Enregistrement (générique ouvert)
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(TimingBehavior<,>));

Pour court-circuiter le handler, ne pas appeler next(ct) et retourner directement une réponse.

Le package CleanArch.DevKit.Mediator.Behaviors fournit LoggingBehavior, PerformanceBehavior et UnhandledExceptionBehavior prêts à l'emploi.


Choisir la stratégie de publication

Quand plusieurs handlers traitent la même notification, contrôler leur invocation :

services.AddMediator(options =>
{
    // Séquentiel (défaut) : un handler après l'autre dans l'ordre d'enregistrement.
    // La première exception arrête la chaîne.
    options.NotificationPublisherStrategy = NotificationPublisherStrategy.Sequential;

    // Parallèle : tous les handlers via Task.WhenAll.
    // Toutes les exceptions sont agrégées dans une AggregateException.
    options.NotificationPublisherStrategy = NotificationPublisherStrategy.Parallel;
});

Diagnostics à la compilation

Code Sévérité Condition
MED001 Error Un IRequest<T> n'a aucun handler
MED002 Error Plusieurs handlers pour le même IRequest<T>
MED003 Error Aucune public partial class Mediator { } détectée dans le projet

Les exceptions runtime InvalidMessageException et MultipleHandlersException servent de filet de sécurité si le diagnostic est ignoré.


Limitation connue

Tous les handlers et la déclaration partial class Mediator doivent vivre dans le même projet. Les types des assemblies référencées ne sont pas scannés. Pour une solution multi-projets, agréger les handlers dans le projet qui déclare la classe partielle.

Migration depuis MediatR

Les signatures sont volontairement compatibles :

Avant (MediatR) Après (CleanArch.DevKit.Mediator)
using MediatR; using CleanArch.DevKit.Mediator;
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(...)) services.AddMediator()
(aucune classe à déclarer) Ajouter public partial class Mediator { }
IRequestPreProcessor, IRequestPostProcessor Réécrire en IPipelineBehavior<,>

IRequest<T>, IRequest, INotification, IStreamRequest<T>, IPipelineBehavior<,> et les signatures des handlers sont identiques.

Product Compatible and additional computed target framework versions.
.NET 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 (5)

Showing the top 5 NuGet packages that depend on CleanArch.DevKit.Mediator:

Package Downloads
CleanArch.DevKit.Mediator.Validation

Source-generated fluent validation with a ValidationBehavior pipeline. Bridges to CleanArch.DevKit.Mediator. Part of the CleanArch.DevKit set.

CleanArch.DevKit.Mediator.Results

Result<T> + Error model with railway-oriented extensions (Map / Bind / Match) and a ResultBehavior that auto-converts ValidationException into a ValidationError. Part of the CleanArch.DevKit set.

CleanArch.DevKit.Mediator.Domain

Bridge between CleanArch.DevKit.Domain and CleanArch.DevKit.Mediator: IDomainEventNotification (combines IDomainEvent + INotification) and a typed PublishDomainEventsAsync extension on IMediator. Install this only if you use both Domain and Mediator together. Part of the CleanArch.DevKit set.

CleanArch.DevKit.Mediator.Behaviors

Common pipeline behaviors (LoggingBehavior, PerformanceBehavior, UnhandledExceptionBehavior) for CleanArch.DevKit.Mediator. Part of the CleanArch.DevKit set.

CleanArch.DevKit.Mediator.Testing

Mock-free testing helpers for CleanArch.DevKit.Mediator. FakeMediator with fluent setup and recording for IRequest, INotification and IStreamRequest. Part of the CleanArch.DevKit set.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 147 5/17/2026
1.1.0 132 5/17/2026
1.0.0 153 5/15/2026
0.1.0-preview.1 68 5/14/2026