Smpl.Mediatr
2.0.0
dotnet add package Smpl.Mediatr --version 2.0.0
NuGet\Install-Package Smpl.Mediatr -Version 2.0.0
<PackageReference Include="Smpl.Mediatr" Version="2.0.0" />
<PackageVersion Include="Smpl.Mediatr" Version="2.0.0" />
<PackageReference Include="Smpl.Mediatr" />
paket add Smpl.Mediatr --version 2.0.0
#r "nuget: Smpl.Mediatr, 2.0.0"
#:package Smpl.Mediatr@2.0.0
#addin nuget:?package=Smpl.Mediatr&version=2.0.0
#tool nuget:?package=Smpl.Mediatr&version=2.0.0
Smpl.Mediatr
A lightweight mediator pattern implementation for .NET 9/10, providing a simple and clean abstraction for handling commands and queries in your applications using CQRS (Command Query Responsibility Segregation) principles.
Overview
This project implements the mediator pattern to decouple request/response handling in your application. It provides a clean separation between commands (operations that change state) and queries (operations that retrieve data), promoting clean architecture and separation of concerns.
Features
- Command Pattern: Execute operations with or without return values
- Query Pattern: Retrieve data without side effects
- Mediator Pattern: Centralized request routing and handling using dependency injection
- Automatic Handler Registration: Automatically discovers and registers all handlers from assemblies
- Async/Await Support: Full async support with cancellation tokens
- Lightweight: Minimal dependencies (only Microsoft.Extensions.DependencyInjection.Abstractions)
- .NET 9/10: Built for the latest .NET platform with nullable reference types enabled
Project Structure
base-mediatr/
├── Extension/
│ └── ServiceCollectionExtensions.cs # Automatic handler registration extension
├── Mediator/
│ ├── ICommand.cs # Command interface definitions (with and without return values)
│ ├── ICommandHandler.cs # Command handler interfaces
│ ├── IMediator.cs # Main mediator interface
│ └── IQuery.cs # Query interface and handler definitions
├── Mediator.cs # Main mediator implementation
└── base-mediatr.csproj # Project file
Pipeline behaviors and notifications (2.0.0)
services.AddMediator(typeof(Program).Assembly); // handlers (commands, queries, notifications) are scanned
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); // behaviors are NOT scanned;
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); // first registered = outermost
IPipelineBehavior<TRequest, TResult>wraps commands (both forms) and queries. A command without a result flows withTResult = Unit.INotification/INotificationHandler<T>/IPublisher.PublishAsync: handlers run sequentially, the first exception propagates.IMediator : IPublisher.- Handler exceptions propagate unwrapped (typed dispatch, no reflection invoke).
- Namespace is
Smpl.Mediatr(package idSmpl.Mediatr). The parameterlessAddMediator()is obsolete.
Releases: bump <Version>, push a tag vX.Y.Z; the publish workflow uses the repository secret NUGET_API_KEY.
Getting Started
Prerequisites
- .NET 9/10 SDK
- Microsoft.Extensions.DependencyInjection (for DI container)
Installation
Clone the repository:
git clone <repository-url>
cd base-mediatr
Build the project:
dotnet build
Basic Usage
1. Define Commands and Queries
// Command with return value
public record CreateUserCommand(string Name, string Email) : ICommand<int>;
// Command without return value
public record DeleteUserCommand(int UserId) : ICommand;
// Query
public record GetUserQuery(int UserId) : IQuery<User>;
2. Implement Handlers
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, int>
{
public async Task<int> HandleAsync(CreateUserCommand command, CancellationToken cancellationToken = default)
{
// Create user logic
return userId;
}
}
public class DeleteUserCommandHandler : ICommandHandler<DeleteUserCommand>
{
public async Task HandleAsync(DeleteUserCommand command, CancellationToken cancellationToken = default)
{
// Delete user logic
}
}
public class GetUserQueryHandler : IQueryHandler<GetUserQuery, User>
{
public async Task<User> HandleAsync(GetUserQuery query, CancellationToken cancellationToken = default)
{
// Get user logic
return user;
}
}
3. Register Services
Automatic Registration (Recommended)
The easiest way to register the mediator and all handlers is using the extension method:
using Smpl.Mediatr.Extension;
// Register mediator and automatically discover all handlers in the current assembly
services.AddMediator();
// Or specify specific assemblies to scan
services.AddMediator(typeof(CreateUserCommandHandler).Assembly, typeof(AnotherHandler).Assembly);
The AddMediator() extension method will:
- Register the
IMediatorservice - Automatically scan assemblies for all handler implementations
- Register all found handlers with the DI container
Manual Registration
If you prefer manual registration:
services.AddScoped<IMediator, Mediator>();
services.AddScoped<ICommandHandler<CreateUserCommand, int>, CreateUserCommandHandler>();
services.AddScoped<ICommandHandler<DeleteUserCommand>, DeleteUserCommandHandler>();
services.AddScoped<IQueryHandler<GetUserQuery, User>, GetUserQueryHandler>();
4. Use the Mediator
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<int> CreateUser(CreateUserCommand command)
{
return await _mediator.SendAsync(command);
}
[HttpDelete("{id}")]
public async Task DeleteUser(int id)
{
await _mediator.SendAsync(new DeleteUserCommand(id));
}
[HttpGet("{id}")]
public async Task<User> GetUser(int id)
{
return await _mediator.SendAsync(new GetUserQuery(id));
}
}
Key Interfaces
IMediator
The main interface for sending commands and queries:
Task<TResult> SendAsync<TResult>(ICommand<TResult> command, CancellationToken cancellationToken = default)Task SendAsync(ICommand command, CancellationToken cancellationToken = default)Task<TResult> SendAsync<TResult>(IQuery<TResult> query, CancellationToken cancellationToken = default)
Commands
ICommand<TResult>: Commands that return a valueICommand: Commands that don't return a valueICommandHandler<TCommand, TResult>: Handler for commands with return valuesICommandHandler<TCommand>: Handler for commands without return values
Queries
IQuery<TResult>: Query interface that returns dataIQueryHandler<TQuery, TResult>: Handler for queries
Automatic Handler Registration
The ServiceCollectionExtensions.AddMediator() method provides automatic handler discovery and registration:
Assembly Scanning
- No parameters: Scans executing, calling, entry, and all loaded assemblies
- With assemblies: Scans only the specified assemblies
Handler Discovery
The extension automatically finds and registers:
- All classes implementing
ICommandHandler<TCommand, TResult> - All classes implementing
ICommandHandler<TCommand> - All classes implementing
IQueryHandler<TQuery, TResult>
Registration Scope
All handlers are registered as Scoped services, which is appropriate for most web applications and ensures proper lifecycle management.
Architecture Benefits
- Separation of Concerns: Clear separation between business logic and controllers
- Testability: Easy to unit test handlers in isolation
- Maintainability: Changes to business logic don't affect controllers
- Scalability: Easy to add new commands and queries without modifying existing code
- CQRS: Natural implementation of Command Query Responsibility Segregation
- Convention-based: Automatic registration reduces boilerplate code
Dependencies
- Microsoft.Extensions.DependencyInjection.Abstractions (9.0.9)
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Inspired by the MediatR library
- Built with .NET 9/10 and modern C# features
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. 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 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. |
-
net10.0
-
net9.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 |
|---|---|---|
| 2.0.0 | 91 | 10/21/2026 |