Round #703

10 May 2026, 15:08· csharp· Meta-Llama-3.3-70B-Instruct· r13-clean-hints
Complexity
band 1
ZP / UO / OBS
13 / 0 / 0
Journey weight
872
Lightshift ΔL
-0.08

Prompt

Write a custom IPipelineBehavior<TRequest, TResponse> for MediatR that captures FluentValidation errors and re-throws as a structured ValidationException.

Zero-point index (what the engine surfaced)

ZPI-ZPI-0002 | 2026-05-10T15:08:38Z Write a custom IPipelineBehavior<TRequest, TResponse> for MediatR that captures FluentValidation errors and re-throws as a structured ValidationException. ZERO POINT — 13 entries (structural invariants) ● hsl(176.24, 100%, 32%) INotificationHandler<TNotification> [contract] ● hsl(351.03, 100%, 32%) IRequestHandler<TRequest, TResponse> [contract] ● hsl(256.59, 100%, 32%) INotification [contract] ● hsl(276.16, 100%, 32%) IRequest<TResponse> [contract] ● hsl(237.58, 100%, 32%) IPipelineBehavior<TRequest, TResponse> [contract] ● hsl(290.83, 100%, 32%) IRequestPreProcessor<TRequest> [contract] ● hsl(314.89, 100%, 32%) IRequestPostProcessor<TRequest, TResponse> [contract] ● hsl(159.80, 100%, 33%) IValidator<T> [contract] ● hsl(240.67, 100%, 33%) AbstractValidator<T> [contract] ● hsl(175.80, 100%, 33%) IValidatableObject [contract] ● hsl(328.68, 100%, 33%) IValidatorFactory [contract] ● hsl(145.31, 100%, 33%) ValidationResult [contract] ● hsl(39.29, 100%, 33%) ValidationContext<T> [contract] UNOBSERVED — 0 entries (implied truths) OBSERVED — 0 entries (explicit in request) COMPLEXITY │ 13 entries │ spread: 0° │ band: MEDIUM

Baseline response (no ZPI)

Here's an example implementation of a custom `IPipelineBehavior<TRequest, TResponse>` for MediatR that captures FluentValidation errors and re-throws as a structured `ValidationException`: ```csharp using MediatR; using System; using System.Threading; using System.Threading.Tasks; using FluentValidation; public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { private readonly IValidator<TRequest> _validator; public ValidationBehavior(IValidator<TRequest> validator) { _validator = validator ?? throw new ArgumentNullException(nameof(validator)); } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { if (_validator != null) { var validationResult = await _validator.ValidateAsync(request, cancellationToken); if (!validationResult.IsValid) { var errors = validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToList()); throw new ValidationException(errors); } } var response = await next(); return response; } } public class ValidationException : Exception { public Dictionary<string, List<string>> Errors { get; } public ValidationException(Dictionary<string, List<string>> errors) { Errors = errors; } public override string ToString() { return $"Validation failed: {string.Join(", ", Errors.Select(kvp => $"{kvp.Key}: {string.Join(", ", kvp.Value)}"))}"; } } ``` To use this behavior, you'll need to register it in the DI container. Here's an example using the `Microsoft.Extensions.DependencyInjection` package: ```csharp services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); ``` You'll also need to register your validators: ```csharp services.AddTransient<IValidator<MyRequest>, MyRequestValidator>(); ``` With this setup, any requests that are invalid according to the validator will throw a `ValidationException` with a dictionary of error messages. Example usage: ```csharp public class MyRequest { public string Name { get; set; } public int Age { get; set; } } public class MyRequestValidator : AbstractValidator<MyRequest> { public MyRequestValidator() { RuleFor(r => r.Name).NotEmpty(); RuleFor(r => r.Age).GreaterThan(0); } } public class MyRequestHandler : IRequestHandler<MyRequest, MyResponse> { public async Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken) { // Handle the request return new MyResponse(); } } public class MyResponse { // Response properties } ``` In this example, if you send a `MyRequest` with an empty `Name` or a non-positive `Age`, the `ValidationBehavior` will catch the validation error and throw a `ValidationException` with a dictionary containing the error messages.

Enhanced response (with ZPI)

Here's a custom implementation of `IPipelineBehavior<TRequest, TResponse>` that captures FluentValidation errors and re-throws them as a structured `ValidationException`: ```csharp using MediatR; using MediatR.Pipeline; using FluentValidation; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { private readonly IValidatorFactory _validatorFactory; public ValidationBehavior(IValidatorFactory validatorFactory) { _validatorFactory = validatorFactory; } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { var validator = _validatorFactory.GetValidator(request); if (validator != null) { var validationResult = await validator.ValidateAsync(request, cancellationToken); if (!validationResult.IsValid) { var errors = validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToList()); throw new ValidationException(errors); } } return await next(); } } public class ValidationException : Exception { public Dictionary<string, List<string>> Errors { get; } public ValidationException(Dictionary<string, List<string>> errors) { Errors = errors; } public override string ToString() { return $"Validation failed: {string.Join(", ", Errors.Select(kvp => $"{kvp.Key}: {string.Join(", ", kvp.Value)}"))}"; } } ``` In this implementation, the `ValidationBehavior` class takes an instance of `IValidatorFactory` in its constructor, which is used to retrieve the validator for the incoming request. If a validator is found, it's used to validate the request asynchronously. If the validation fails, a `ValidationException` is thrown with a dictionary of errors, where each key is a property name and the value is a list of error messages. The `ValidationException` class is a custom exception that holds the validation errors and provides a meaningful `ToString` implementation for easy debugging. To register this behavior in the MediatR pipeline, you can use the following code in your DI setup: ```csharp services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); ``` This will enable the validation behavior for all requests handled by MediatR. Make sure to also register your validators with the `IValidatorFactory` instance, for example: ```csharp services.AddTransient<IValidator<MyRequest>, MyRequestValidator>(); ``` With this setup, any validation errors will be caught and re-thrown as a structured `ValidationException`, making it easier to handle and display validation errors in your application.

Grader chain

Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.

V1samba/Meta-Llama-3.3-70B-Instruct
Better
V4qwen2.5-coder-3b-instruct
Better
V5openrouter/qwen/qwen3-235b-a22b-2507
Better
V7qwen2.5-coder-7b-instruct
Same
V8openai/gpt-4.1
Same
V9anthropic/claude-opus-4-7
Worse
V12openai/gpt-4o
Better

Comments

Our grader said what it said. What do you say? Comment as a guest below.

No comments yet. Be the first to say what you make of this round.

← back to all rounds