CQS Validation
Kurt Dowswell
293

Good post! Here is another possible way to handle the annotation validators globally.

We have an IValidator<T> interface like yours. All validators inherit from this.

The ValidationQueryHandlerDecorator has the following parameters:

IQueryHandler<TQuery, TResult> wrappedHandler AND

IEnumerable<IValidator<TQuery>>

This also lets you create and pass in multiple validators, keeping your business rules separate and testable(NoOrdersAfter5Validator, ValidCreditCardNumberValidator, etc). And now we have a single Decorator that can handle both levels — first the simple annotations validation. Then the possibly more complex business rules validation. Here is the Handle() method:

public TResults Handle(TQuery query){

var validationContext = new ValidationContext(query, null, null);

Validator.ValidateObject(query, validationContext, validateAllProperties: true);

List<ValidationResult> results = new List<ValidationResult>();

foreach(var validator in _validatorsPassedIn){

results.AddRange(validator.Validate(query));

}

if (results.Any())

throw new ValidationException(results);

return this._wrappedQueryHandler.Handle(query);

}

Hope this helps! Sorry for the horrible formatting, Medium doesn’t seem to have any code formatting utilities!

Another tip: I’ve moved away from controller inheritance when possible, and was doing the same as your post for awhile with the custom exception filters until I found out about the global filter collection. In Global.asax.cs, at the RegisterGlobalFilters method, you could do something like:

filters.add(new ValidationExceptionFilter());

Like what you read? Give Josh Williams a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.