Decouple senders from handlers using IRequest/IRequestHandler and optional pipeline behaviors.
// Application/Ping.cs
using MediatR;
public sealed record PingQuery(string Message) : IRequest<string>;
public sealed class PingHandler : IRequestHandler<PingQuery, string>
{
public Task<string> Handle(PingQuery request, CancellationToken ct)
=> Task.FromResult($"Pong: {request.Message}");
}
Minimal API wiring with one query and one command.
// Program.cs
using MediatR;
var builder = WebApplication.CreateBuilder(args);
// Registers all IRequestHandler<> in this assembly
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(PingHandler).Assembly));
var app = builder.Build();
app.MapGet("/ping/{msg}", async (string msg, IMediator mediator, CancellationToken ct)
=> await mediator.Send(new PingQuery(msg), ct));
// Command example
public sealed record CreateTodoCommand(string Title) : IRequest<Todo>;
public sealed record Todo(Guid Id, string Title);
public sealed class CreateTodoHandler : IRequestHandler<CreateTodoCommand, Todo>
{
public Task<Todo> Handle(CreateTodoCommand request, CancellationToken ct)
=> Task.FromResult(new Todo(Guid.NewGuid(), request.Title));
}
app.MapPost("/todos", async (CreateTodoCommand cmd, IMediator mediator, CancellationToken ct)
=> Results.Created("/todos", await mediator.Send(cmd, ct)));
app.Run();
Notes
Paste into a Minimal API project (see sandboxes/dotnet-minimal-api):