Tuesday, 30 November 2010

Encapsulating boolean return types.

.Net 4 introduced some changes to the way DataAnnotations ValidationAttributes work. Pre .NET 4 you would check the validity of a property on a class by attributing it with a ValidationAttribute (custom or otherwise) and then calling the IsValid():bool method. This is straight forward and logical. However; this approach leaves a bit to be desired. If IsValid() returns false, what exactly is it that invalidates my property? It would be better if validation was approached differently, and this is what DataAnnotations in .NET 4 does.

In .NET 4 the use of IsValid():bool is discouraged. In fact, if you create a new custom validation attribute (extending ValidationAttribute) and do not override IsValid() before you call it, it will throw a NotImplementedException with the message "IsValid(object value) has not been implemented by this class. The preferred entry point is GetValidationResult() and classes should override IsValid(object value, ValidationContext context)."

As you can see, you're encouraged to override an overload of IsValid(). If you look closer you'll see that the overload does not return true/false, but instead returns a ValidationResult which is a wee bit more informative. Also, the overload is protected - so you can't call it externally. Instead you call the GetValidationResult() method to see what the result of validation is.

Why is this better than just returning true or false? It's because a ValidationResult carries information about why validation failed.

You might now think that this is a bit overkill for scenarios in which validation is successful. In this case, more often than not, all you care about is knowing that validation has passed and that you can carry on. The ValidationResult class facilitates this by applying the Null Object Pattern and encapsulating a static property called ValidationResult.Success against which you can compare the ValidationResult instance returned to you by GetValidationResult() (ValidationResult.Success always returns null, because a null validation result signifies successful validation).

This approach doesn't just apply to validation scenarios. Ayende Rahien talks about why and how he uses this approach here. If you have a method that returns true/false and there is no more relevant information available, fine. However, if the true/false answer masks other information then the approach of encapsulating the answer in a class is better.

No comments: