Wednesday, November 23, 2011

Castle Validator Component for Beginners

Introduction

Castle validator component is an open source validation framework that uses property attribute to specify validation rules on class. In this article, I will explain how to use it and how it works from inside out.

Castle Validator Sample

Let’s start by looking at a class that has Castle validator attributes on it.
public class User
{
 [ValidateLength(3, 10)]
 public string Name { get; set; }

 [ValidateDate("Invalid date format.")]
 public string DOB { get; set; }
 
 [ValidateEmail("Invalid email address.")]
 [ValidateLength(5, 20, RunWhen = RunWhen.Insert)]
 public string Email { get; set; }
}
There are three validator attributes used here: ValidateLengthValidateDate, and ValidateEmail.
  • ValidateLength attribute is used to specify a fixed length or length range for the property.
  • ValidateDate attribute is used to specify the property as date type.
  • ValidateEmail attribute is used to specify the property allows email format.
A castle validator attribute inherits from AbstractValidationAttribute that ultimately inherits fromAttribute and IValidatorBuilder. The IValidatorBuilder is the contract that Castle validator attribute must implement. By implementing this contract, validate attribute is able to create a corresponding validator for validation process by ValidatorRunner.
ValidateLengthAttributeClass.gif
The following code sample shows how to use ValidatorRunner validates the class that has validator attributes on it.
[TestMethod]
public void CreateUser_User_NoError()
{
 //Assign
 ValidatorRunner runner = new ValidatorRunner(new CachedValidationRegistry());

 User user = new User();
 user.Name = "Henry";
 user.DOB = "1/1/2011";
 user.Email = "henry@aaa.com";

 //Act
 bool result = runner.IsValid(user);

 //Assert
 Assert.IsTrue(result);
 ErrorSummary errorSummary = runner.GetErrorSummary(user);
 Assert.AreEqual(0, errorSummary.ErrorsCount);
}
The ValidatorRunner is the class that has knowledge of how to execute validators. All validators are fromCachedValidationRegistry. When runner is calling IsValid method, it will ask CachedValidationRegistryfor all validators of the current object. Then use Validation Performer internally to perform validation process on the object. The result is stored in ErrorSummary for later use.
IsValid.gif

Some Good Validation Features

As a useful validation framework, some features are designed for advanced scenarios. I am not going to go through the entire advanced features, just a few of them that I feel make Castle Validator distinguished with other validation framework.
RunWhen
If the validation is only legitimate for certain situations, you can use RunWhen parameter with validator attribute to specify when you want to have this validation. RunWhen is an enum that can be EverytimeInsertUpdate, and Custom. The default is Everytime. Following is a Validator attribute that has RunWhen used.
[ValidateLength(5, 20, RunWhen = RunWhen.Insert)]
public string Email { get; set; }
Let’s see how to validate it:
[TestMethod]
public void CreateUser_User_EmailErrorWhenInsert()
{
 //Assign
 ValidatorRunner runner = new ValidatorRunner(new CachedValidationRegistry());

 User user = new User();
 user.Name = "Henry";
 user.DOB = "1/1/2011";
 user.Email = "henry@aaaaaaaaaaaaaaa.com";

 //Act
 bool result = runner.IsValid(user, RunWhen.Insert);

 //Assert
 Assert.IsFalse(result);
 ErrorSummary errorSummary = runner.GetErrorSummary(user);
 Assert.AreEqual(1, errorSummary.ErrorsCount);
 Assert.AreEqual("Field must be between 5 and 20 characters long", 
   errorSummary.ErrorMessages[0]);
}
Execution Order
When there are multiple validator attributes applied on a property, you can use execution order on validator attribute to specify which one executes first.
[ValidateNonEmpty(FriendlyName="Country Field", ExecutionOrder=1)]
[ValidateLength(3, 10, "Invalid country length.", ExecutionOrder=2)]
public string Country { get; set; }
ValidateSelf
If you have a pretty complicated validation logic and don’t want to create a custom validator attribute for it because it’s used once in that particular class only, you can create a method to do validation and use ValidateSelfattribute to let Castle validator component find it.
[ValidateSelf()]
public void Validate(ErrorSummary errorSummary)
{
 if ((Street == null || !Street.Equals
  ("Main Street", StringComparison.InvariantCultureIgnoreCase)) && 
  (City != null && City.Equals("Big City")))
  errorSummary.RegisterErrorMessage("Street", 
   "Street name must be Main Street when City is Big City.");

}

Custom Validation

For situations in which you want to have specific business logic validation and also want to apply it to multiple places, custom validation is the best option. To create a custom validation, you need to create a custom validator attribute and custom validator. The validator attribute is used to decorate property and the validator is the one doing validation.
ValidateCityAttribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | 
 AttributeTargets.ReturnValue, AllowMultiple = true), CLSCompliant(false)]
[Serializable]
class ValidateCityAttribute : AbstractValidationAttribute
{
 private readonly IValidator _validator;

 public ValidateCityAttribute()
 {
  _validator = new CityValidator();
 }

 public ValidateCityAttribute(string errorMessage)
  : base(errorMessage)
 {
  _validator = new CityValidator();
 }

 public override IValidator Build()
 {
  base.ConfigureValidatorMessage(_validator);
  return _validator;
 }
}
CityValidator
[Serializable]
public class CityValidator : AbstractValidator
{
 public override bool SupportsBrowserValidation
 {
  get
  {
   return false;
  }
 }

 public CityValidator()
 {
 }

 public override bool IsValid(object instance, object fieldValue)
 {
  if (fieldValue == null)
  {
   return true;
  }
  if (fieldValue != "Big City" && fieldValue != "Small City")
  {
   return false;
  }
  else
  {
   return true;
  }
 }

 protected override string BuildErrorMessage()
 {
  return "City must be Big City or Small City.";
 }
}
Let’s also take a look at the class diagram:
CustomValidation.gif

Summary

Castle validator component is a very simple and flexible validation framework. It can be used in all kinds of applications: Web, Windows, Console, etc. Even better, this validation framework allows customization easily and deeply to fit your exact need.

Using the Code

The code is developed in Visual Studio 2010. Castle validator component (Castle.Components.Validator.dll) 4.0.30319 is used with the code.

No comments:

Post a Comment