Validating a validator

If you take your precious free time and build a validation framework from the ground up you want the architecture to be perfect, or as close to perfection as possible. So you are bound to run into some problem on the way there. The problem that I was struggling with a while ago was the question hot to validate the validator?

The problem became painfully obvious when I wrote the first System.String validator that required some external configuration. To illustrate my little dilemma here is the validator code.

namespace valy.Validators.String
 public class Regex : BaseValidator<string>
   public Regex(IEnumerable<IValidator<string>> validationParts) : base(validationParts){}
   public Regex(IValidatorConfiguration configuration) : base(configuration){}

   protected override IValidationResult DoValidate(string objectToValidate)
     if (reg.IsMatch(objectToValidate, RegularExpression))
       return Pass();
     return Fail(ValidationFailReasons.Invalid);

   protected override void CheckParameters()
     Require(this, v => v.RegularExpression, s => !string.IsNullOrEmpty(s));

   public string RegularExpression { get; set; }

For now try to ignore the existence of the CheckParameters method.

In this special case I have to validate that a valid non-empty regular expression is given to the validator. You could do it in the validation body with a simple if statement, but then it would be in the responsibility of every validator to do it’s internal validation and communicate possible violations to the external world. This would lead to a incosistent framework in no time at all. What I wanted was a way by which the validator could define a set of rules that had to be met before the actual validation could happen.

This does not give us complete protection. In the above example the object to validate can still be null. But that is not a problem of the validator anymore.

But lets return to the method that you should have ignored until now. The CheckParameters methods purpose in live is to define a set of rules that ensure that the internal state of the validator is consistent. This means that if the method “passes” the validator is ready to use and will not throw any bogus exception if invoked.

The core of this internal validation scheme is the Require method implemented in the base validator. Using this simple building block you can put together relatively complex validation schemes like show in the next code sample.

protected override void CheckParameters()
 Require(this, v => v.MaxLenght, i => i > MinLenght);
 Require(this, v => v.MinLenght, i => i < MaxLenght);
 Require(this, v => v.MaxLenght, i => i > 0);

The complexity of the example above will not win you and noble prizes but will ensure that your range validator can not be given an invalid range. And this works for me(until now)!

This is not perfect and the thing that is annoying me the most is the fact that the current object has to be passed as a parameter to the function. This is necessary because the Require method is defined on the parent class and therefore has to somehow link to the actual overriding class. But this is just a minor hiccup. There are other potential problems that are not causing me any headaches yet, so they will get fixed when they start to hurt.

All in all this scheme works and I am quite happy with it. The last part I would like to show you is the implementation of the Require method.

protected void Require<TValidator, TParam>(TValidator validator, Expression<Func<TValidator, TParam>> property, Expression<Predicate<TParam>> predicate)
 var propertyValue = property.Compile().Invoke(validator);
 if (!predicate.Compile().Invoke(propertyValue))
   throw new ValidatorInitializationException(
             string.Format("Validation inicialization failed on predicate : {0} for member : {1}",
             predicate.Body, property.Body),

As you can see the implementation is nothing special. Just two functions that the evaluated at runtime and if predicate fails a ValidationInitializationException is thrown. Really there is nothing more to say about this topic.

Hope that this helped someone out there and if someone out there seas something wrong with this write a comment and tell me about it.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s