Easy.Mediator
2.1.0
dotnet add package Easy.Mediator --version 2.1.0
NuGet\Install-Package Easy.Mediator -Version 2.1.0
<PackageReference Include="Easy.Mediator" Version="2.1.0" />
<PackageVersion Include="Easy.Mediator" Version="2.1.0" />
<PackageReference Include="Easy.Mediator" />
paket add Easy.Mediator --version 2.1.0
#r "nuget: Easy.Mediator, 2.1.0"
#:package Easy.Mediator@2.1.0
#addin nuget:?package=Easy.Mediator&version=2.1.0
#tool nuget:?package=Easy.Mediator&version=2.1.0
๐ Easy.Mediator
Easy.Mediator is a lightweight, extensible, and intuitive library for implementing the Mediator pattern in .NET applications. It helps you separate concerns, reduce coupling, and keep your code clean and testable.
โจ Features
- โ Supports Requests/Responses and Notifications
- โ๏ธ Automatic handler registration via Dependency Injection
- ๐ Compatible with .NET Standard 2.1, .NET Core 2.1+ and .NET 5+ or higher
- ๐ Seamless integration with
Microsoft.Extensions.DependencyInjection - ๐ฆ Easy to use
- ๐งฉ NEW: Support for Pipeline Behaviors (interceptors like logging, validation, etc.)
- ๐ 100% Compatible with C# Records (C# 9+) even though library is C# 7.3
๐ฆ Installation
Install via the .NET CLI:
dotnet add package Easy.Mediator
๐ Basic Usage
๐ Handling Notifications
1. Define a notification
public class NewUserNotification : INotification
{
public string UserName { get; }
public string Message { get; }
public NewUserNotification(string userName, string message)
{
UserName = userName;
Message = message;
}
}
2. Implement a notification handler
public class EmailNotificationHandler : INotificationHandler<NewUserNotification>
{
public Task Handle(NewUserNotification notification, CancellationToken cancellationToken = default)
{
Console.WriteLine($"[Email] To: {notification.UserName} - {notification.Message}");
return Task.CompletedTask;
}
}
3. Publish the notification
var services = new ServiceCollection();
services.AddEasyMediator();
var provider = services.BuildServiceProvider();
var mediator = provider.GetRequiredService<IMediator>();
await mediator.Publish(new NewUserNotification("Bob", "Bem-vindo!"));
//[Email] To: Bob - Welcome to the system!
๐ฌ Handling Requests (Request/Response)
1. Define a request and its response
public class PingCommand : IRequest<PongResponse>
{
public string Message { get; }
public PingCommand(string message) => Message = message;
}
public class PongResponse
{
public string Message { get; }
public PongResponse(string message) => Message = message;
}
2. Implement a request handler
public class PingCommandHandler : IRequestHandler<PingCommand, PongResponse>
{
public Task<PongResponse> Handle(PingCommand request, CancellationToken cancellationToken = default)
{
return Task.FromResult(new PongResponse($"{request.Message} => Pong!"));
}
}
3. Send the request and receive the response
var response = await mediator.Send(new PingCommand("Ping!"));
Console.WriteLine(response.Message); // Ping! => Pong!
๐๏ธ Pipeline Behaviors (Interceptors)
Easy.Mediator supports pipeline behaviors that allow you to intercept request execution before and/or after the handler. Useful for:
- Logging
- Validation
- Caching
- Handling exceptions
- Auditing
โ Implementing a Behavior
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> HandleAsync(
TRequest request,
CancellationToken cancellationToken,
Func<TRequest, CancellationToken, Task<TResponse>> next)
{
Console.WriteLine($">>> Handling {typeof(TRequest).Name}");
var response = await next(request, cancellationToken);
Console.WriteLine($"<<< Handled {typeof(TRequest).Name}");
return response;
}
}
๐ง Registering Behaviors
Behaviors must be registered in the desired order:
services.AddEasyMediator(cfg =>
{
cfg.AddRequestHandlersFromAssembly(typeof(SomeHandler).Assembly);
cfg.AddPipelineBehavior(typeof(LoggingBehavior<,>));
cfg.AddPipelineBehavior(typeof(ValidationBehavior<,>));
});
โน๏ธ The registration order defines the execution flow โ behaviors registered first run before later ones and the final handler.
๐งช Example of a ValidationBehavior
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) =>
_validators = validators;
public async Task<TResponse> HandleAsync(
TRequest request,
CancellationToken ct,
Func<TRequest, CancellationToken, Task<TResponse>> next)
{
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(r => r.Errors)
.Where(f => f != null);
if (failures.Any())
throw new ValidationException(failures);
return await next(request, ct);
}
}
๐ License
This project is licensed under the MIT License.
You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, provided that you include the original copyright and license notice.
See the License.txt file for full details.
๐ C# Records Support
Easy.Mediator is 100% compatible with C# Records! Even though the library is compiled with C# 7.3 for maximum compatibility, your projects can freely use C# 9+ Records.
Using Records with Easy.Mediator
// โ
Your project can use C# 9+ Records
public record CreateOrderCommand(string OrderId, string ProductName) : IRequest<OrderResponse>;
public record OrderResponse(string OrderId, string ProductName, string ConfirmationId);
public record OrderCreatedNotification(string OrderId, string ProductName) : INotification;
// Handlers work seamlessly with records
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResponse>
{
public Task<OrderResponse> Handle(CreateOrderCommand request, CancellationToken ct)
{
var response = new OrderResponse(
request.OrderId,
request.ProductName,
Guid.NewGuid().ToString()
);
return Task.FromResult(response);
}
}
Compatibility Matrix
| Your Project | C# Version | .NET Target | Easy.Mediator | Status |
|---|---|---|---|---|
| Legacy | C# 7.3 | .NET Framework 4.7.2+ | โ Works | |
| Modern | C# 8.0 | .NET Core 3.0+ | โ Works | |
| Latest | C# 9.0+ | .NET 5.0+ | โ Works + Records |
Easy.Mediator is compiled for C# 7.3 and .NET Standard 2.1, ensuring maximum compatibility across all .NET versions and C# versions. Your code is free to use any newer features! ๐
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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 was computed. 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. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Easy.Mediator.Abstractions (>= 1.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.5)
- System.Threading.Channels (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
## v2.1.0 - Enhanced Architecture & Stability Release
### โจ New Features
- **Named Tuples** - Improved code readability with semantic field names (RequestType, Handler, BehaviorType) replacing Item1/Item2
- **C# Records Support** - 100% compatible with C# 9+ Records while maintaining C# 7.3 minimum compatibility
- **Error Handling Tests** - New comprehensive error handling test suite for missing handlers
- **Better Type Safety** - Explicit tuple naming reduces bugs and improves maintainability
### 🔧 Technical Improvements
- **Thread-Safe Architecture** - Switched from Channels to thread-safe Collections for better mutation control
- **No Race Conditions** - IServiceProvider moved to instance-level (not static) eliminating concurrency issues
- **31 Unit Tests** - Complete test coverage including error scenarios, records, and pipeline behaviors
- **Performance Optimized** - HashSet for behavior registry ensures O(1) duplicate prevention
### 🎯 Architecture Highlights
- โ
List<T> + Lock-based handler registries (thread-safe mutations)
- โ
HashSet<T> for pipeline behaviors (automatic duplicate prevention)
- โ
Channels reserved for audit trail (immutable streaming)
- โ
Instance-level IServiceProvider (no static state conflicts)
### 📦 Compatibility & Quality
- **C# Versions**: 7.3 minimum (13.0+ fully supported)
- **Frameworks**: .NET Framework 4.7.2+, .NET Core 2.1+, .NET 5.0-9.0+
- **Code Quality**: 100% test coverage for Mediator core
- **Documentation**: Records compatibility guide, implementation summary, enhanced README
### 🐛 Bug Fixes & Improvements
- Fixed static service provider race conditions causing flaky tests
- Improved error messages with explicit request type names
- Better duplicate behavior prevention with HashSet
- Cleaner code with named tuple unpacking
### Previous Features (v2.0.0)
- 🧩 Pipeline Behaviors (interceptors)
- 🔄 Request/Response pattern with generic handlers
- 🔔 Publish/Subscribe notifications with parallel execution
- 🎯 Automatic handler discovery via reflection
- 💉 Seamless Dependency Injection integration
- 📊 Audit trail support via Channels