Quick Start Guide
Get started with MediatorLite v2 in minutes.
Installation
dotnet add package MediatorLite
dotnet add package MediatorLite.SourceGeneration # Required for O(1 dispatch
⚠️ v2 Requirement: Source generation is the only dispatch mechanism. Without
MediatorLite.SourceGeneration, dispatch throws anInvalidOperationExceptionwith setup guidance on first use.
Optional (when a project only needs request/notification/validation contracts):
dotnet add package MediatorLite.Abstractions
Package Selection and Versioning
Use this as a quick rule set for new users.
| Project Type | Install |
|---|---|
| Application/API (v2) | MediatorLite + MediatorLite.SourceGeneration (both required) |
| Shared contracts library | MediatorLite.Abstractions |
How transitive install works:
- Installing
MediatorLitealso installsMediatorLite.Abstractions. - Installing only
MediatorLite.SourceGenerationdoes not install runtime contracts.
Compatibility matrix (safe combinations):
| Abstractions | MediatorLite | SourceGeneration | Supported |
|---|---|---|---|
| 2.x | 2.x | 2.x | Yes (recommended) |
| transitive | 2.x | 2.x | Yes |
| 2.x | 2.x | not installed | No (dispatch throws — no fallback) |
| 2.x | not installed | 2.x | No |
| 1.x | 2.x | 2.x | No |
| 2.x | 2.x | 1.x | No |
Versioning guideline:
- Keep all packages on the same major and minor version.
- Patch versions can differ, but lockstep is recommended for beginners.
1. Define a Request and Handler
// Define a query request
public record GetUserQuery(int Id) : IRequest<User>;
// Define the response type
public record User(int Id, string Name, string Email);
// Implement the handler
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, User>
{
private readonly IUserRepository _repository;
public GetUserQueryHandler(IUserRepository repository)
{
_repository = repository;
}
public async ValueTask<User> HandleAsync(GetUserQuery request, CancellationToken cancellationToken = default)
{
return await _repository.GetByIdAsync(request.Id, cancellationToken);
}
}
2. Register Services
Source-Generated Registration (Required for v2)
You must call AddGeneratedHandlers() before AddMediatorLite() to enable O(1) dispatch:
using MediatorLite.Generated;
services
.AddGeneratedHandlers() // Registers handlers and the generated IMediator (typed dispatch)
.AddMediatorLite(); // Optional diagnostic fallback; call order doesn't matter
AddGeneratedHandlers() registers:
- All
IRequestHandler<,>implementations - All
INotificationHandler<>implementations - All
IPipelineBehavior<,>implementations (ordered by[BehaviorOrder]) - The source-generated
IMediatorimplementation (scoped; typed switch dispatch, no reflection, no boxing)
Observability (on by default, compile-time opt-out)
Built-in logging and OpenTelemetry tracing are emitted inline by the source generator and are on by default. Opt out at compile time with assembly-level attributes:
[assembly: DisableMediatorLogging] // Generator emits no logging calls
[assembly: DisableMediatorTracing] // Generator emits no ActivitySource calls
The log level is controlled through standard Microsoft.Extensions.Logging configuration. Generated code logs at Debug under the MediatorLite.IMediator category:
services.AddLogging(b => b.AddFilter("MediatorLite.IMediator", LogLevel.Information));
For granular control, use the individual registration methods:
services
.AddGeneratedRequestHandlers() // Only request handlers
.AddGeneratedNotificationHandlers() // Only notification handlers
.AddGeneratedBehaviors() // Only pipeline behaviors
.AddMediatorLite();
Manual DI Registration (Not Supported)
⚠️ Not supported in v2: There is no reflection fallback. Without
AddGeneratedHandlers(), theIMediatorregistered byAddMediatorLite()throws anInvalidOperationExceptionwith setup guidance on first use — manual handler registrations alone cannot be dispatched.
services.AddTransient<IRequestHandler<GetUserQuery, User>, GetUserQueryHandler>();
services.AddTransient<INotificationHandler<UserCreatedNotification>, SendWelcomeEmailHandler>();
services.AddMediatorLite(); // Without AddGeneratedHandlers(), dispatch throws at first use
Excluding Types from Source Generation
Discovery is unconditional: every concrete handler in the compilation is registered. The legacy [MediatorGeneration(Skip = true)] attribute is obsolete and has no effect. To keep a handler out of registration, move it to an assembly the source generator does not run on (for example, a test-support project).
3. Send Requests
public class UserService
{
private readonly IMediator _mediator;
public UserService(IMediator mediator)
{
_mediator = mediator;
}
public async Task<User> GetUserAsync(int id, CancellationToken ct)
{
return await _mediator.SendAsync(new GetUserQuery(id), ct);
}
}
4. Commands (No Return Value)
// Command without response
public record DeleteUserCommand(int Id) : IRequest;
// Handler for void commands
public class DeleteUserCommandHandler : IRequestHandler<DeleteUserCommand>
{
public async ValueTask HandleAsync(DeleteUserCommand request, CancellationToken cancellationToken = default)
{
await _repository.DeleteAsync(request.Id, cancellationToken);
}
}
5. Notifications (Pub-Sub)
// Define notification
public record UserCreatedNotification(int UserId, string Email) : INotification;
// Multiple handlers can subscribe
public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedNotification>
{
public async ValueTask HandleAsync(UserCreatedNotification notification, CancellationToken ct)
{
await _emailService.SendWelcomeAsync(notification.Email);
}
}
public class CreateAuditLogHandler : INotificationHandler<UserCreatedNotification>
{
public async ValueTask HandleAsync(UserCreatedNotification notification, CancellationToken ct = default)
{
await _auditService.LogAsync($"User {notification.UserId} created");
}
}
// Publish notification
await _mediator.PublishAsync(new UserCreatedNotification(user.Id, user.Email));
6. Notification Execution Strategies (v2)
In v2, notification strategies are controlled via compile-time attributes, not runtime options:
// Apply strategy via compile-time attributes (v2 approach)
[NotificationExecution(NotificationExecutionStrategy.Parallel)]
[NotificationError(NotificationErrorStrategy.ContinueAndAggregate)]
public record UserCreatedNotification(int UserId, string Email) : INotification;
// Or set an assembly-wide default once:
[assembly: DefaultNotificationExecution(NotificationExecutionStrategy.Parallel)]
[assembly: DefaultNotificationError(NotificationErrorStrategy.ContinueAndAggregate)]
⚠️ v2 Change:
MediatorOptionsis gone,AddMediatorLiteno longer accepts a configure lambda, and the old[NotificationOptions]attribute has been removed. Use[NotificationExecution]/[NotificationError](or their[assembly: Default...]counterparts) instead.
| Strategy | Behavior | Error Strategy |
|---|---|---|
Sequential | Execute handlers one-by-one in order | Applies |
Parallel | Execute all handlers concurrently | Always aggregates* |
StopOnFirst | Stop after first successful handler | Applies |
*Parallel mode always aggregates exceptions because concurrent tasks cannot be stopped mid-execution.
See Notifications for detailed strategy documentation and error handling patterns.
7. Source Generator Diagnostics
The source generator exposes handler counts for diagnostics:
using MediatorLite.Generated;
Console.WriteLine($"Request handlers: {MediatorLiteRegistration.RequestHandlerCount}");
Console.WriteLine($"Notification handlers: {MediatorLiteRegistration.NotificationHandlerCount}");
Console.WriteLine($"Behaviors: {MediatorLiteRegistration.BehaviorCount}");
Next Steps
- Pipeline Behaviors - Add logging, validation, and other cross-cutting concerns
- Notifications - Configure execution strategies and ordering
- Migration from MediatR - Migrate your existing MediatR code