Round #226
07 May 2026, 15:26· csharp· meta/llama-3.3-70b-instruct· r12-real-contracts
- Complexity
- band 1
- ZP / UO / OBS
- 13 / 5 / 1
- Journey weight
- 1495
- Lightshift ΔL
- —
Prompt
Write a validation pipeline behaviour in MediatR that collects all errors
Zero-point index (what the engine surfaced)
ZPI-ZPI-0019 | 2026-05-07T15:26:30Z
Write a validation pipeline behaviour in MediatR that collects all errors
ZERO POINT — 13 entries (structural invariants)
● hsl(176.24, 100%, 18%) INotificationHandler<TNotification> [contract]
● hsl(351.03, 100%, 18%) IRequestHandler<TRequest, TResponse> [contract]
● hsl(256.59, 100%, 18%) INotification [contract]
● hsl(276.16, 100%, 18%) IRequest<TResponse> [contract]
● hsl(237.58, 100%, 18%) IPipelineBehavior<TRequest, TResponse> [contract]
● hsl(290.83, 100%, 18%) IRequestPreProcessor<TRequest> [contract]
● hsl(314.89, 100%, 18%) IRequestPostProcessor<TRequest, TResponse> [contract]
● hsl(159.80, 100%, 21%) IValidator<T> [contract]
● hsl(240.67, 100%, 21%) AbstractValidator<T> [contract]
● hsl(175.80, 100%, 21%) IValidatableObject [contract]
● hsl(328.68, 100%, 21%) IValidatorFactory [contract]
● hsl(145.31, 100%, 21%) ValidationResult [contract]
● hsl(39.29, 100%, 21%) ValidationContext<T> [contract]
UNOBSERVED — 5 entries (implied truths)
○ hsl(27.69, 100%, 50%) async [feature]
○ hsl(3.75, 100%, 21%) CancellationToken [feature]
○ hsl(31.38, 100%, 40%) Scoped registration [function]
○ hsl(83.08, 100%, 26%) ILogger of T injection [function]
○ hsl(156.92, 100%, 10%) Model state inspection [function]
OBSERVED — 1 entries (explicit in request)
◉ hsl(45.00, 100%, 5%) Pipeline behaviour [function]
COMPLEXITY │ 19 entries │ spread: 153° │ band: MEDIUM
Baseline response (no ZPI)
**Validation Pipeline Behaviour in MediatR**
=============================================
Below is an example of a validation pipeline behaviour in MediatR that collects all errors.
```csharp
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationPipelineBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var validationResults = _validators
.Select(validator => validator.Validate(request))
.SelectMany(result => result.Errors)
.ToList();
if (validationResults.Any())
{
throw new ValidationException(validationResults);
}
return await next();
}
}
public class ValidationException : Exception
{
public List<ValidationFailure> Errors { get; }
public ValidationException(List<ValidationFailure> errors)
{
Errors = errors;
}
}
public class ValidationFailure
{
public string ErrorMessage { get; set; }
public string PropertyName { get; set; }
}
```
**Using the Validation Pipeline Behaviour**
------------------------------------------
To use the validation pipeline behaviour, you need to register it in the DI container and add the validators for your requests.
```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationPipelineBehavior<,>));
services.AddTransient<IValidator<MyRequest>, MyRequestValidator>();
services.AddMediatR(typeof(Startup));
}
public class MyRequestValidator : AbstractValidator<MyRequest>
{
public MyRequestValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required");
RuleFor(x => x.Age).GreaterThan(0).WithMessage("Age must be greater than 0");
}
}
public class MyRequest : IRequest<MyResponse>
{
public string Name { get; set; }
public int Age { get; set; }
}
public class MyResponse
{
public string Message { get; set; }
}
```
**Handling Validation Exceptions**
---------------------------------
To handle validation exceptions, you can create a middleware that catches the `ValidationException` and returns a response with the validation errors.
```csharp
public class ValidationExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
public ValidationExceptionHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (ValidationException ex)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
errors = ex.Errors.Select(e => new { e.ErrorMessage, e.PropertyName })
}));
}
}
}
```
You can add this middleware to the pipeline in the `Startup.cs` file.
```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseMiddleware<ValidationExceptionHandlerMiddleware>();
// ...
}
```
Enhanced response (with ZPI)
```csharp
using MediatR;
using MediatR.Pipeline;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<ValidationPipelineBehavior<TRequest, TResponse>> _logger;
private readonly IValidatorFactory _validatorFactory;
public ValidationPipelineBehavior(ILogger<ValidationPipelineBehavior<TRequest, TResponse>> logger, IValidatorFactory validatorFactory)
{
_logger = logger;
_validatorFactory = validatorFactory;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var validator = _validatorFactory.GetValidator(request.GetType());
if (validator != null)
{
var validationResults = validator.Validate(request);
if (!validationResults.IsValid)
{
var errors = validationResults.Errors.Select(e => new { Property = e.PropertyName, Error = e.ErrorMessage });
_logger.LogError($"Validation failed for {request.GetType().Name}. Errors: {string.Join(", ", errors.Select(e => $"{e.Property}: {e.Error}"))}");
throw new ValidationException(validationResults);
}
}
return await next();
}
}
public class ValidationException : Exception
{
public ValidationException(ValidationResult validationResult)
{
ValidationResult = validationResult;
}
public ValidationResult ValidationResult { get; }
}
public class ValidationPipelineBehaviorRegistration
{
public static void Register(IServiceCollection services)
{
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationPipelineBehavior<,>));
}
}
```
This implementation provides a validation pipeline behavior for MediatR that collects all errors. It uses the `IValidatorFactory` to get a validator for the request type, and then validates the request using the `Validate` method. If the validation fails, it logs the errors and throws a `ValidationException`.
The `ValidationPipelineBehavior` class is registered as a transient service in the `ValidationPipelineBehaviorRegistration` class, which can be used to register the behavior in the DI container.
The `ValidationException` class is used to wrap the `ValidationResult` object, which contains the validation errors. This exception can be caught and handled by the application to display the validation errors to the user.
Note that this implementation assumes that the request type has a validator registered in the `IValidatorFactory`. If no validator is registered, the behavior will simply pass the request to the next handler in the pipeline.
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.
V1claude-haiku-4-5-20251001
BetterV4qwen2.5-coder-7b-instruct
BetterComments
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.