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));
        }
    }
  • Using the validation:
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);

Last edited Jan 12, 2011 at 9:44 AM by ashishsharmaait, version 3

Comments

No comments yet.