Pipeline Behaviors¶
Pipeline Behaviors, MediatR pipeline’ında request’lerin işlenmesi sırasında çalışan middleware’lerdir. Cross-cutting concern’ler için idealdir.
Behavior Özellikleri¶
- Pre-Processing: Request handler’dan önce çalışır
- Post-Processing: Request handler’dan sonra çalışır
- Exception Handling: Exception’ları yakalayabilir
- Response Manipulation: Response’ları değiştirebilir
Yaygın Behavior Örnekleri¶
Logging Behavior¶
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();
_logger.LogInformation("Handled {RequestName}", typeof(TRequest).Name);
return response;
}
}
Validation Behavior¶
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
var context = new ValidationContext<TRequest>(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
{
throw new ValidationException(failures);
}
return await next();
}
}
Transaction Behavior¶
public class TransactionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly IUnitOfWork _unitOfWork;
public TransactionBehavior(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
await _unitOfWork.BeginTransactionAsync();
try
{
var response = await next();
await _unitOfWork.CommitTransactionAsync();
return response;
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}
}
Performance Behavior¶
public class PerformanceBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<PerformanceBehavior<TRequest, TResponse>> _logger;
private readonly Stopwatch _timer;
public PerformanceBehavior(ILogger<PerformanceBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
_timer = new Stopwatch();
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
_timer.Start();
var response = await next();
_timer.Stop();
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
if (elapsedMilliseconds > 500)
{
_logger.LogWarning(
"Long Running Request: {RequestName} ({ElapsedMilliseconds} milliseconds)",
typeof(TRequest).Name,
elapsedMilliseconds);
}
return response;
}
}
Behavior Best Practices¶
-
Behavior Sıralaması - Validation - Authorization - Logging - Transaction - Caching - Performance
-
Error Handling - Her behavior kendi exception’larını yakalamalı - Global exception handling kullanılmalı
-
Testing - Behavior’lar unit test edilmeli - Integration testler yazılmalı
-
Performance - Behavior’lar hafif olmalı - Gereksiz işlemlerden kaçınılmalı
Behavior Registration¶
services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
cfg.AddBehavior(typeof(ValidationBehavior<,>));
cfg.AddBehavior(typeof(LoggingBehavior<,>));
cfg.AddBehavior(typeof(TransactionBehavior<,>));
cfg.AddBehavior(typeof(PerformanceBehavior<,>));
});