Adding your own custom validation
We will be adding a custom validation extension here. A custom validation extension allows you to extend the Validate framework by adding new types of validations.
A custom validation extension consists of two parts:
- An extension method on Validator<T>
- A class which implements the abstract class TargetMemberExpression
A custom validation extension "IsLengthGreaterThan" which checks whether the given property/method/field on an object has a value whose length is greater than some user specified value.
- Writing the extension method:
// Declare an extension method on type Validator<T> which accepts an expression returning type U : IEnumerable
public static Validator<T> IsLengthGreaterThan<T,U>(this Validator<T> validator, Expression<Func<T,U>> selector, int lengthGreaterThan, string message = null) where U:IEnumerable
{
var validationMessage = message == null ? new ValidationMessage("{TargetType}.{TargetMember} had length greater than the speciefied value."): new ValidationMessage(message);
var validationExpression = new IsLengthGreaterThanTargetMemberExpression<T, U>(selector, validationMessage, lengthGreaterThan);
return validationExpression.ValidationMethod.RunAgainst(validator);
}
- Implement abstract class target member expression:
// This class is used to implement the custom validation
// The type U is constrained to implement IEnumerable
public class IsLengthGreaterThanTargetMemberExpression<T, U> : TargetMemberValidationExpression<T,U> where U:IEnumerable
{
private readonly int _lengthGreaterThan;
public IsLengthGreaterThanTargetMemberExpression(Expression<Func<T, U>> selector, ValidationMessage message, int lengthGreaterThan) : base(selector, message)
{
_lengthGreaterThan = lengthGreaterThan;
}
// Only this method needs to be implemented
public override ValidationMethod<T> GetValidationMethod()
{
// Populate tokens which you want to be replaced by values in the validation message
// The TargetMemberExpression is the Expression<Func<T, U>> selector passed in the constructor
var validationMessage = Message.Populate(targetType: GetTargetTypeName(TargetMemberExpression), targetMember: GetTargetMemberName(TargetMemberExpression));
// Get an executable method which would return the target member U from type T
var compiledSelector = TargetMemberExpression.Compile();
// Define a Func<Validator<T>, Validator<T>> which adds errors to the input validator in case the condition fails
Func<Validator<T>, Validator<T>> validation = (v) =>
{
var target = compiledSelector(v.Target);
if (target == null || target.OfType<object>().Count() <= _lengthGreaterThan)
// populate the cause property of the validation error to make the cause of the failed validation as clear as possible
v.AddError(new ValidationError(validationMessage.Populate(targetValue: target).ToString(), target,
"{{ The target member {0}.{1} did not have length greater than {2} }}".WithFormat(GetTargetTypeName(TargetMemberExpression), GetTargetMemberName(TargetMemberExpression), _lengthGreaterThan)));
return v;
};
// Return a ValidationMethod<T>
return new ValidationMethod<T>(validation, validationMessage, GetTargetTypeName(TargetMemberExpression), GetTargetMemberName(TargetMemberExpression));
}
}
var person = new Person {Name = "Some Name", EmailAddresses = new [] {"Email1", "Email2"}};
var validator = person.Validate()
.IsLengthGreaterThan(p => p.Name, 3, "Name should have more than 3 characters.")
.IsLengthGreaterThan(p => p.EmailAddresses, 1);
Assert.IsTrue(validator.IsValid);