Validation Application Block: Revealed!

I'm sitting at home, still slowly digesting turkey, watching the snow fall outside the window, and trying to deal with the prospect of the imminent full work week. But while the 4 day weekend has definitely been a welcome break, the next week should actually be pretty interesting, with almost all of the Enterprise Library team in the same city for a change (alas, it's Redmond :-) and the project starting to take shape.

Probably the most interesting new inclusion in the v3 release will be the Validation Application Block. We're really looking forward to sharing some early drops with you soon, but as we're not quite ready to do this, I wanted to briefly share a few of our plans and give you the chance to provide some feedback. Naturally, all the usual standard disclaimers apply (side effects may include headaches and nose bleeds etc), and there's a good chance that the details will change before the release, especially if you ask us to change them :-).

Here are a few of the key scenarios we plan to support with the new block:

Defining Validation Rules

The Validation Application Block will include a comprehensive library of common validation rules that apply to primitive data types. For example, we'll include rules like string length, numeric range, date range, regular expressions and so on. However your applications will typically deal with more complex objects such as Customers or Orders (yes, here at Microsoft we assume every application is based on Northwind ;-), so while the built-in Validators should be great building blocks, you'll need to do some additional work to specify how these primitive rules apply to more complex objects. We plan on letting you do this in two primary ways: in configuration (which is ideal if you want the rules to be easily changed after deployment), or in code (which allows better encapsulation of rules and ensures the behavior won't change unless the code does).

Defining Validation Rules using configuration

As with every other Enterprise Library block, we would expect most people to use the Enterprise Library Configuration console to define externalized validation rules. The following pseudo-configuration shows how you might be able to build validation rules using the tool:

  • Validation Application Block
    • System.String
      • Rules
        • EmailAddress
          • Validators
            • RegExValidator (Pattern:xxxxx)
        • ShortString
          • Validators
            • NotNullValidator
            • StringLengthValidator (Min=1, Max=5)
    • GlobalBank.Customer
      • Rules
        • ValidCustomer (Default=true)
          • Validators
            • PropertyValueValidator (Property: Name, Type: System.String)
              • Validators
                • NotNullValidator
                • StringLengthValidator (Min=1, Max=50)
            • PropertyValueValidator (Property: DateJoined, Type=System.DateTime)
              • Validators
                • RelativeDateValidator (Kind=Before, OffsetFromNow=0)
        • GoldCustomer
          • Validators
            • PropertyValueValidator (Property: DateJoined, Type=System.DateTime)
              • Validators
                • RelativeDateValidator (Kind=Before, OffsetFromNow=5, Units=Years)

Some interesting things to note here are that validation rules and individual validators are specified for a specific type - this should let us effectively filter the type browser dialog in the tool. As you'll see later, we'll also have a generic-based API to provide similar benefits at the code level. Also note how you can supply multiple validators for a single rule, either using simple Boolean AND logic, or by attaching validators to properties (or potentially fields or methods) of the target object type. Finally, note how it is possible to define multiple different rules for the same type, letting you do interesting things like differentiate between multiple types of "validity".

Defining Validation Rules using attributes

Another interesting scenario for validation rules is to declare them within the objects that are being validated. This will be supported using attributes, as the following example shows: 

 public class Customer
{
    // Using fields instead of properties for brevity
     [NotNullValidator]


    [StringLengthValidator(1, 50)]
    public string Name;
 
    [RelativeDateValidator(RelativeDateKind.Before, OffSetFromNow=0)]
    public DateTime DateJoined;
 }

In the previous example, the class has only a single, anonymous rule set defined. However as you saw from the configuration-based example, it may be interesting to have multiple validation rule sets for a single class. We're planning on allowing this too, by letting you specify which rule set each validation attribute belongs to:

 public class Customer
{
    // Using fields instead of properties for brevity
     [NotNullValidator("ValidCustomer")]


    [StringLengthValidator("ValidCustomer", 1, 50)]
    public string Name;
 
    [RelativeDateValidator("ValidCustomer", RelativeDateKind.Before, OffSetFromNow=0)]
    [RelativeDateValidator("GoldCustomer", RelativeDateKind.Before, OffSetFromNow=5, Units=DateInterval.Year)]

    public DateTime DateJoined;
 }

Defining Validation Rules using code

Of course, the attribute-based approach is only viable if you own the code for the type being validated. In some cases this won't be an option - maybe the class was written by another team, maybe you only have access to the binary assembly (such as for a .NET Framework class), or maybe the class was generated by a tool like wsdl.exe. In this case, you'll be able to build up single or composite validators using code, for example:

 IValidator<string> emailAddressValidator = new RegExValidator("xxxx");
IValidator<string> shortStringValidator = new AndCompositeValidator<string>(
    new NotNullValidator<string>(), new StringLengthValidator(1, 5));

Validating objects

Regardless of which of the above approaches you use to specify your validation rules, they won't be of a lot of use unless you actually plan on validating some objects. We also plan on providing a few ways of doing this:

Validating objects from custom code

The most flexible way of validating objects will by writing code directly against the block's API. First, you'll need to get a reference to the appropriate Validator object. You'll already have this if you defined the validation rules in code, but if you used configuration or attributes you'll need to use a factory:

 IValidator<string> emailAddressValidator = ValidationFactory.CreateValidator<string>("EmailAddressValidator");
IValidator<Customer> customerValidator = ValidationFactory.CreateValidator<Customer>(); // assumes default rule
IValidator<Customer> goldCustomerValidator = ValidationFactory.CreateValidator<Customer>("GoldCustomer");

Once you have your validator, you're ready to do some validation! We're expecting this code to look something like this:

 ValidationResults results = customerValidator.Validate(myCustomer);

...where ValidationResults is a collection of ValidationResult objects, each of which will report an individual violation, complete with references to the validator, the object and property causing the failure, and error messages.

Alternatively, if you aren't doing anything wacky with polymorphism, we'd like to provide a façade which lets you create the validator and do the validation in one go:

 ValidationResults results = Validation.Validate(myCustomer);

Integrating Validation into your application

While the approach above should work in almost any case, it isn't optimized for use for any specific technologies or layers. To make it easier to integrate validation into your application, we're looking to build some "adapters" that will plug the validation engine cleanly into different technologies. We haven't decided exactly which technologies will be included, but we are looking at ASP.NET (server-side and AJAX), Windows Forms, Windows Presentation Foundation and Windows Communication Foundation. We'll give more details on this as we work them out, but feel free to provide any suggestions or feedback in the meantime.

Creating your own Validators

While we have a pretty sizable list of primitive Validators we plan to include in the block, its still pretty likely that you will have some interesting requirements that can't be met by stringing together our original validators. To get around this, you'll want to create your own Validator classes. These could validate primitive objects in new and interesting ways, or you could build individual validators that can deal with more complex types (Customers and the like).

Finally, one additional scenario we are considering is letting you "in-line" the validation logic within the classes themselves:

 public class TemperatureRange
{ 
    private int min; 
    private int max; 


    [SelfValidation]   
    // if you want to mean this is only specific to a certain rulename you do [SelfValidation("Gold")] 
    public ValidationResult IsValid() 
    { 
         ValidationResult retval = (max < min) ? new ValidationResult.Failed("Max has to be larger than min") : null;

         return retval;
    } 
}  

I hope this gives you an idea on what we have planned. It's not too late for us to tweak things, so if you have any important scenarios that you don't think will be covered in anything I've described above, you know how to find me!