MediatR is a popular open-source library in C# that provides a simple and elegant way to implement the Mediator pattern and build pipelines in your application. The library helps decouple components by promoting loose coupling and encapsulation of requests and handlers.
With MediatR, you define requests as plain C# classes representing a unit of work or a command, and you create handlers to process these requests. The library then handles the routing and execution of the requests to their corresponding handlers.
One of the key features of MediatR is its support for pipelines. A pipeline in MediatR is a sequence of behavior or middleware that can be applied before and after the execution of request handlers. Each behavior in the pipeline can intercept and modify the request, perform additional processing, or handle cross-cutting concerns such as validation, logging, caching, or authorization.
Following code example shows a simple Logging and Exception handling pipeline for Medatr requests.
{
public string Username { get; set; }
public string Email { get; set; }
}
public class AddUserCommandHandler : IRequestHandler<AddUserCommand, Unit>
{
private readonly IUserService _userService;
public AddUserCommandHandler(IUserService userService)
{
_userService = userService;
}
public Task<Unit> Handle(AddUserCommand request, CancellationToken cancellationToken)
{
_userService.AddUser(request.Username, request.Email);
return Task.FromResult(Unit.Value);
}
}
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
_logger.LogInformation($"Handling {typeof(TRequest).Name}");
var response = await next();
_logger.LogInformation($"Finished handling {typeof(TRequest).Name}");
return response;
}
}
public class ExceptionHandlingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<ExceptionHandlingBehavior<TRequest, TResponse>> _logger;
public ExceptionHandlingBehavior(ILogger<ExceptionHandlingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
try
{
return await next();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception occurred while handling {typeof(TRequest).Name}");
throw;
}
}
}
Next step is to configure the pipeline in startup class.
var services = new ServiceCollection();
services.AddMediatR(typeof(Startup));
// Register any other dependencies your handlers might require
services.AddTransient<IUserService, UserService>();
// Configure pipeline behaviors
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ExceptionHandlingBehavior<,>));
By utilizing MediatR's pipeline capabilities, you can easily insert and compose multiple behaviors in a flexible manner, providing a powerful way to modify and extend the behavior of your application's requests without tightly coupling them to specific implementations.
The pipeline in MediatR is typically implemented using a combination of decorators and the Decorator Pattern. Decorators wrap and modify the behavior of the underlying handler, allowing you to add or modify functionality at various points in the pipeline.
In summary, MediatR is a versatile library that simplifies the implementation of the Mediator pattern and facilitates the creation of pipelines in C# applications. It promotes decoupling, modularity, and extensibility, making it easier to handle requests, apply behaviors, and build scalable and maintainable applications.