Microsoft provides the Data Annotations library in the assembly System.ComponentModel allowing to mark class properties with validation attributes in real Aspect Oritented Programming (AOP) manner. As it is quite popular we are going to base our design on that library as well.
We extend our existing IEntity interface with the following methods:
ValidationResult is member of the Data Annotations namespace and represents a container for the results of a validation request.
Unit Tests
In the spirit of Test Driven Development (TDD) we describe the unit tests before starting the implementation. The unit tests reflect the expected behavior in detail and assert a high level of quality, especially if combined with Continous Integration (CI).
Before diving into the unit tests, let us first define a fake entity class that depicts how we are going to use the Data Annotations. Please note that only public properties participate in change tracking and therefore non-public properties are not validated.
Entity
The Validate method iterates through run-time reflection over all available properties marked with a ValidationAttribute. Each property is then validated by calling IsPropertyValid, which in turn relies on GetValidationResults. As you probably also noticed, we could improve the implementation of Validate by inspecting the class properties only once per instance, e.g. in the class constructor, or even better only once per type, e.g. in the static class constructor.
The main method for validation is GetValidationResults that uses Validator.TryValidateProperty from the Data Annotations namespace to validate a property value according to its property attribute. Of course we must use property attributes that extend the base class ValidationAttribute.
Again we add some convenience methods for child classes to pass Lambda Expressions instead of property names.
Review and Outlook
Wow, that was quite easy and fun to implement! In future we could extend the definition to let also non-public properties participate in change tracking and validation.
Even though the approach of annotating class properties with validators seems quite interesting on the first sight, we will show in a future article that in practice it lacks important flexibility. Herefore we will introduce the fluent validation API which fills this gap perfectly.
In the next article we will design a composite entity, a container for other entities, that also implements our self-tracking features. Afterwards we will finally present how to use all those bits and bytes in user interface development.