Skip to content

Instantly share code, notes, and snippets.

@DavidRogersDev
Last active June 27, 2022 22:28
Show Gist options
  • Save DavidRogersDev/a9291deb6b333ac87434589c3e6e8a00 to your computer and use it in GitHub Desktop.
Save DavidRogersDev/a9291deb6b333ac87434589c3e6e8a00 to your computer and use it in GitHub Desktop.
Gists for Medium Article - BusinessValidationPipeline
public class BusinessValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TResponse : class
where TRequest : IValidateable
{
private readonly IValidator<TRequest> _compositeValidator;
private readonly ILogger<TRequest> _logger;
private readonly ICurrentUser _currentUser;
private readonly IActionContextAccessor _actionContextAccessor;
public BusinessValidationPipeline(
IValidator<TRequest> compositeValidator,
ILogger<TRequest> logger,
ICurrentUser currentUser,
IActionContextAccessor actionContextAccessor)
{
_compositeValidator = compositeValidator;
_logger = logger;
_currentUser = currentUser;
_actionContextAccessor = actionContextAccessor;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
_logger.TraceBeforeValidatingMessage();
var responseType = typeof(TResponse);
if (responseType.Name.StartsWith(nameof(ValidateableResponse), StringComparison.Ordinal))
{
var result = await _compositeValidator.ValidateAsync(request, cancellationToken);
if (!result.IsValid)
{
var errorsCollated = result.ToDictionary();
_logger.TraceMessageValidationFailed(errorsCollated, _currentUser?.UserName ?? GeneralPurpose.AnonymousUser);
// Add validation fail to ModelState to make it available there. Just in case it is needed.
result.AddToModelState(_actionContextAccessor.ActionContext.ModelState, string.Empty);
// Deal with type depending on whether it is the generic version of ValidateableResponse or not.
var resultType = responseType.GetGenericArguments().FirstOrDefault();
if (ReferenceEquals(resultType, null))
{
var nonGenericInvalidResponse =
Activator.CreateInstance(
responseType,
errorsCollated
) as TResponse;
return nonGenericInvalidResponse;
}
var invalidResponseType = typeof(ValidateableResponse<>).MakeGenericType(resultType);
var invalidResponse =
Activator.CreateInstance(
invalidResponseType,
null,
errorsCollated
) as TResponse;
return invalidResponse;
}
_logger.TraceMessageValidationPassed();
return await next();
}
throw new Exception($"IValidateable implementation must be a {nameof(ValidateableResponse)}.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment