Skip to content

Getting Started ​

Nerdigy.Mediator gives you strongly-typed request/response dispatch, notification fan-out, and async streaming for .NET 10 applications. This guide takes you from installation to a working request and notification in under five minutes.

Installation ​

Install the DI package. It pulls in the core runtime and contracts transitively.

bash
dotnet add package Nerdigy.Mediator.DependencyInjection

This gives you all three packages:

PackagePurpose
Nerdigy.Mediator.ContractsContracts: IMediator, IRequest<T>, INotification, handler interfaces
Nerdigy.MediatorRuntime: dispatch, pipeline execution, notification publishers
Nerdigy.Mediator.DependencyInjectionDI registration via services.AddMediator(...)

Only need contracts?

If you are defining requests and handlers in a library that should not depend on the runtime, reference Nerdigy.Mediator.Contracts directly.

Register the Mediator ​

Call AddMediator on your IServiceCollection and tell it which assemblies to scan. The scanner finds your handlers, pipeline behaviors, processors, and exception components automatically.

csharp
using Nerdigy.Mediator.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMediator(options =>
{
    options.RegisterServicesFromAssemblyContaining<Program>();
});

That single call registers IMediator, ISender, and IPublisher in the container. You can inject any of the three -- IMediator combines both sending and publishing, while ISender and IPublisher let you depend on only the capability you need.

Send Your First Request ​

A request is a plain class or record that implements IRequest<TResponse>. A handler implements IRequestHandler<TRequest, TResponse> with a single Handle method.

Define the request and handler ​

csharp
using Nerdigy.Mediator.Contracts;

public sealed record GetWeatherQuery(string City) : IRequest<WeatherForecast>;

public sealed class GetWeatherHandler : IRequestHandler<GetWeatherQuery, WeatherForecast>
{
    public Task<WeatherForecast> Handle(
        GetWeatherQuery request, CancellationToken cancellationToken)
    {
        WeatherForecast forecast = new(request.City, 22, "Sunny");

        return Task.FromResult(forecast);
    }
}

public sealed record WeatherForecast(string City, int TemperatureC, string Summary);

Dispatch the request ​

Inject ISender (or IMediator) and call Send. The return type is inferred from the IRequest<TResponse> contract.

csharp
app.MapGet("/weather/{city}", async (string city, ISender sender) =>
{
    WeatherForecast forecast = await sender.Send(new GetWeatherQuery(city));

    return Results.Ok(forecast);
});

One request, one handler, no manual wiring. The assembly scanner registered GetWeatherHandler when it found IRequestHandler<GetWeatherQuery, WeatherForecast>.

Void Requests ​

Commands that produce no return value implement the non-generic IRequest and use IRequestHandler<TRequest> (single type parameter).

csharp
public sealed record DeleteCityCommand(string City) : IRequest;

public sealed class DeleteCityHandler : IRequestHandler<DeleteCityCommand>
{
    public Task Handle(DeleteCityCommand request, CancellationToken cancellationToken)
    {
        // perform deletion
        return Task.CompletedTask;
    }
}

Dispatch with the same Send method:

csharp
await sender.Send(new DeleteCityCommand("Springfield"));

Publish a Notification ​

Notifications deliver a message to zero or more handlers. A notification implements INotification, and each handler implements INotificationHandler<T>.

Define the notification and handlers ​

csharp
using Nerdigy.Mediator.Contracts;

public sealed record CityDeleted(string City) : INotification;

public sealed class AuditLogHandler : INotificationHandler<CityDeleted>
{
    public Task Handle(CityDeleted notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Audit: {notification.City} was deleted");

        return Task.CompletedTask;
    }
}

public sealed class CacheInvalidationHandler : INotificationHandler<CityDeleted>
{
    public Task Handle(CityDeleted notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Cache cleared for {notification.City}");

        return Task.CompletedTask;
    }
}

Publish the notification ​

Inject IPublisher (or IMediator) and call Publish. Both handlers execute -- by default, sequentially in registration order.

csharp
await publisher.Publish(new CityDeleted("Springfield"));

If no handlers are registered for a notification type, Publish completes successfully with no effect.

Minimal Console Example ​

Here is a complete, self-contained program that sends a request and publishes a notification.

csharp
using Microsoft.Extensions.DependencyInjection;
using Nerdigy.Mediator.Contracts;
using Nerdigy.Mediator.DependencyInjection;

ServiceCollection services = new();

services.AddMediator(options =>
{
    options.RegisterServicesFromAssemblyContaining<PingHandler>();
});

await using ServiceProvider provider = services.BuildServiceProvider();
IMediator mediator = provider.GetRequiredService<IMediator>();

// Request/response
string pong = await mediator.Send(new PingQuery("hello"));
Console.WriteLine(pong); // PONG: hello

// Notification
await mediator.Publish(new Pinged("hello"));

// --- Contracts and handlers ---

public sealed record PingQuery(string Message) : IRequest<string>;

public sealed class PingHandler : IRequestHandler<PingQuery, string>
{
    public Task<string> Handle(PingQuery request, CancellationToken cancellationToken)
    {
        return Task.FromResult($"PONG: {request.Message}");
    }
}

public sealed record Pinged(string Message) : INotification;

public sealed class PingedHandler : INotificationHandler<Pinged>
{
    public Task Handle(Pinged notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Notification received: {notification.Message}");

        return Task.CompletedTask;
    }
}

What's Next ​

You have requests dispatching and notifications publishing. Nerdigy.Mediator offers several more capabilities worth exploring:

  • Dependency Injection -- Assembly scanning options, service lifetimes, and notification publisher strategies.
  • Requests -- Request/response and void request patterns in depth.
  • Notifications -- Sequential vs. parallel publishing and custom publisher strategies.
  • Streaming -- Async streaming with IStreamRequest<T>, IAsyncEnumerable<T>, and cancellation semantics.
  • Pipelines and Processors -- Pre-processors, post-processors, and pipeline behaviors for cross-cutting concerns like logging and validation.
  • Exception Handling -- Typed exception handlers that recover from errors and exception actions for side-effects before rethrow.

Mediator runtime for .NET