Lädt...

🔧 Enhancing Request Pipelines with MediatR Behaviors


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

Introduction

MediatR is a widely used library in .NET applications that follows the mediator pattern, helping to decouple requests from handlers. While it simplifies CQRS (Command Query Responsibility Segregation), it also offers a robust feature called Pipeline Behaviors.

These behaviors enable developers to intercept requests and implement cross-cutting concerns such as logging, validation , performance tracking , transaction management , and error handling in a structured and reusable manner.

Source Code

You can find the complete source code for this tutorial at:
👉 GitHub Repository

Understanding MediatR Behaviors and the Decorator Pattern

MediatR Behaviors function as middleware, allowing developers to execute logic before and after request handlers.

The Decorator Pattern in MediatR Behaviors

MediatR Behaviors follow the Decorator Pattern, a structural design pattern that allows additional responsibilities to be dynamically added to an object without modifying its code.
Instead of modifying request handlers directly, behaviors act as layers around the handler execution, applying cross-cutting concerns transparently.

How MediatR Implements the Decorator Pattern

Each MediatR behavior wraps the handler, decorating it with additional functionality. This follows the Open-Closed Principle (OCP), as we can extend behavior without modifying the existing handler.

Example: Applying the Decorator Pattern in MediatR Behaviors

When MediatR processes a request, it applies behaviors in order, wrapping the original handler:

1️⃣ ValidationBehavior runs first and validates the request.
2️⃣ LoggingBehavior logs the request before and after execution.
3️⃣ Request Handler executes after all decorators have run.

Each behavior decorates the request handler, adding functionality without modifying it.

Why Use Behaviors?

Separation of concerns: Keep handlers focused on business logic.

Code reusability: Apply behaviors to multiple handlers.

Maintainability: Centralized logic makes updates easier.

Consistent processing: Ensure common concerns (e.g., logging, validation) are always executed.

Implementing a MediatR Behavior
To create a MediatR behavior, you implement IPipelineBehavior.

Example: Logging Behavior

using MediatR;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

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, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Handling {RequestName}", typeof(TRequest).Name);
        var response = await next(); // Call the next behavior or handler
        _logger.LogInformation("Handled {RequestName}", typeof(TRequest).Name);
        return response;
    }
}

Registering the Behavior in DI

To make behaviors work, register them in the DI container:

    builder.Services.AddMediatR(options =>
    {
        options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());

        options.AddOpenBehavior(typeof(ValidationBehaviour<,>));
        options.AddOpenBehavior(typeof(PerformanceBehaviour<,>));
        options.AddOpenBehavior(typeof(UnhandledExceptionBehaviour<,>)); 
        options.AddOpenBehavior(typeof(LoggingBehavior<,>));
    });

More Use Cases for MediatR Behaviors :

Validation

Integrate FluentValidation to validate requests before handling.

using FluentValidation;
using MediatR;

public class ValidationBehaviour<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators) : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators = validators;

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        if (_validators.Any())
        {
            var context = new ValidationContext<TRequest>(request);

            var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
            var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();

            if (failures.Any())
            {
                throw new ValidationException(failures);
            }
        }

        return await next();
    }
}

Performance Monitoring

Measure execution time using Stopwatch.

using MediatR;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

public class PerformanceBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly ILogger<PerformanceBehavior<TRequest, TResponse>> _logger;

    public PerformanceBehavior(ILogger<PerformanceBehavior<TRequest, TResponse>> logger)
    {
        _logger = logger;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var stopwatch = Stopwatch.StartNew();
        var response = await next();
        stopwatch.Stop();

        _logger.LogInformation("Request {Request} executed in {ElapsedMilliseconds}ms", typeof(TRequest).Name, stopwatch.ElapsedMilliseconds);

        return response;
    }
}

Transaction Handling

Wrap requests in a database transaction scope.

using MediatR;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;

public class TransactionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly DbContext _dbContext;

    public TransactionBehavior(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        await using var transaction = await _dbContext.Database.BeginTransactionAsync(cancellationToken);

        try
        {
            var response = await next();
            await _dbContext.SaveChangesAsync(cancellationToken);
            await transaction.CommitAsync(cancellationToken);
            return response;
        }
        catch
        {
            await transaction.RollbackAsync(cancellationToken);
            throw;
        }
    }
}

Exception Handling

Catch and log exceptions globally.

using MediatR;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

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, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        try
        {
            return await next();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Request {Request} failed with exception {Message}", typeof(TRequest).Name, ex.Message);
            throw;
        }
    }
}

**
Final Thoughts : **

MediatR Behaviors, leveraging the Decorator Pattern, provide an elegant solution for handling cross-cutting concerns in .NET applications. By combining multiple behaviors, developers can ensure cleaner, more maintainable, and efficient request processing

References

MediatR GitHub Repository

FluentValidation Documentation

Microsoft Dependency Injection

Decorator pattern

Decorator pattern

...

🔧 Enhancing Request Pipelines with MediatR Behaviors


📈 68.62 Punkte
🔧 Programmierung

🔧 MediatR Response: Should the Request Handler Return Exceptions?


📈 27.92 Punkte
🔧 Programmierung

🔧 Request -&gt; Handler -&gt; SubPub Pattern with MediatR or Raw F# code


📈 27.92 Punkte
🔧 Programmierung

🔧 Reinventando a Roda: Criando seu próprio MediatR - Parte 1


📈 23.74 Punkte
🔧 Programmierung

🔧 MediatR Simple Alternative


📈 23.74 Punkte
🔧 Programmierung

🔧 Developing your easy alternative to the MediatR library in C#


📈 23.74 Punkte
🔧 Programmierung

🔧 CQRS (Command Query Responsibility Segregation) και MediatR Pattern στη C#


📈 23.74 Punkte
🔧 Programmierung

🔧 Let's Build: CQRS and MediatR Pattern using ASP.Net WEB API


📈 23.74 Punkte
🔧 Programmierung

🔧 Setting Up MediatR in a Minimal API


📈 23.74 Punkte
🔧 Programmierung

🔧 Mediator and CQRS with MediatR


📈 23.74 Punkte
🔧 Programmierung

🔧 Decoupling Communication with MediatR


📈 23.74 Punkte
🔧 Programmierung

🔧 Desacoplamento de Comunicação com MediatR


📈 23.74 Punkte
🔧 Programmierung

🔧 Implementing CQRS with MediatR in .NET 8: A Complete Guide


📈 23.74 Punkte
🔧 Programmierung

🔧 MediatR with .NET 6.0


📈 23.74 Punkte
🔧 Programmierung

🔧 🚀How to Use MediatR Pipeline Behavior in .NET 8


📈 23.74 Punkte
🔧 Programmierung

🔧 How To Setup CQRS with MediatR in .NET 5.0 and database MariaDB?


📈 23.74 Punkte
🔧 Programmierung

🔧 Mastering CQRS Design Pattern with MediatR in C# .NET


📈 23.74 Punkte
🔧 Programmierung

🔧 CQRS (Command Query Responsibility Segregation) και MediatR Pattern στη C#


📈 23.74 Punkte
🔧 Programmierung

🔧 LiteBus: A Free and Ambitious Alternative to MediatR for .NET Applications


📈 23.74 Punkte
🔧 Programmierung

🔧 Reinventando a Roda: Criando seu próprio MediatR - Parte 2


📈 23.74 Punkte
🔧 Programmierung

🔧 Enhancing SDLC with Security: A Guide to SSDL and CI/CD Pipelines


📈 21.61 Punkte
🔧 Programmierung

🔧 Docker in DevOps Workflows: Enhancing CI/CD Pipelines


📈 21.61 Punkte
🔧 Programmierung

🔧 Beyond Static Pipelines: Enhancing AI Agents With LlamaIndex


📈 21.61 Punkte
🔧 Programmierung

🎥 A CISO's Perspective on AI, Appsec, and Changing Behaviors - Paul Davis - ASW #293


📈 19.08 Punkte
🎥 IT Security Video

📰 5 innovation behaviors that business leaders must adopt in the next normal


📈 19.08 Punkte
📰 IT Nachrichten

📰 Are Your Employees Thinking Critically About Their Online Behaviors?


📈 19.08 Punkte
📰 IT Security Nachrichten

🎥 A CISO's Perspective on AI, Appsec, and Changing Behaviors - Paul Davis - ASW #293


📈 19.08 Punkte
🎥 IT Security Video

📰 Facebook etiquette: Behaviors you should avoid


📈 19.08 Punkte
📰 IT Security Nachrichten

📰 CI Fuzz CLI: Open-source tool to test Java apps for unexpected behaviors


📈 19.08 Punkte
📰 IT Security Nachrichten

📰 What is the New “Block Suspicious Behaviors” Feature in Windows 10?


📈 19.08 Punkte
📰 IT Security Nachrichten