BLOG

Validation - part 2

This is part 2 of the series, you can find the other parts here:

Validation

The second idea to validation is to design an interface that specifies validation:

public interface IValidatable<T>{
    ?? Validate();
}

What return type should the validate method have? The first thing that comes to mind is a boolean indicating whether the validation was successful or not. In something I like to call happy-case-thinking that would definitely suffice, but when the validation wasn’t successful, what kind of information would I need? Obviously the error messages are the most important data to get, so we’ll go with a list of strings.

The effect of this interface is that each and every class in our model will implement this interface. We could make it a bit safer to only allow classes to validate themselves, by adding a generic type constraint:

public interface IValidatable<T>
    where T : IValidatable<T>
{
    List<string> Validate();
}

If the list of validation errors is empty, the validation succeeded.

Integration with attributes

Of course we can implement a helper function that checks for the attributes from part 1, so we can call it from our Validate method.

public static class Validator {
    public static List<string> Validate<T>(T item) {
        if(item is IValidatable<T> validatable)
            return validatable.Validate(item);
        else
            return ValidateAttributes(item);
    }
    public static List<string> ValidateAttributes<T>(T item){
        // ...
    }
}

Still, it feels like doing the same things in two different ways. Also, we rely on the implementation of IValidatable<T> to call ValidateAttributes which is error-prone. We could implement the automatic checking of the attributes, but then there is absolutely no way to circumvent this anymore.

Conclusion so far

The basic idea of validation for data objects was described in these two parts. Of course you can have a lot of extensions on this model, for instance having custom validation methods which are automatically called. But this is basically how validation is done in main stream object oriented programming languages.