lmost any application you write that accepts input requires some sort of validation to meet the business requirements. One example is when you need to validate user-entered or externally acquired data before persisting it in the database. .NET provides a rich set of features for handling validation; but even so, developers sometimes find themselves writing the same type of validation “plumbing” code repeatedly. To help solve this, Enterprise Library 3.0 ships with a new Validation Application Block that abstracts this plumbing code in the form of a reusable library that you can use in any .NET application. This article introduces the Validation Application Block and provides working examples of how you can use it to write robust validation code.
Introducing the Validation Application Block
The Validation Application Block includes a comprehensive library of common validation rules that apply to primitive data types. These include rules for validating string lengths, date ranges, regular expressions and so on. You can leverage these validators directly from within your code (programmatically or in the form of declarative attributes) or from a configuration file. When the built-in validation does not satisfy your requirements, the Validation Block gives you the option of creating custom validator classes.
After you install Enterprise Library 3.0 April 2007 edition, you’ll find the Validation Application Block assemblies in the
- Microsoft.Practices.EnterpriseLibrary.Common.dll
- Microsoft.Practices.EnterpriseLibrary.Validation.dll
- Microsoft.Practices.ObjectBuilder.dll assemblies
Get Familiar with the Built-in Validators
The Validation Block ships with a number of built-in validator classes, all of which derive from a base Validator class. Table 1 provides a brief overview of the built-in validators.
Class | Description |
ValidatorComposition | Allows you to create a composite validator by combining one or many validators. You can specify either “And” or “Or” type validation through CompositionType enumeration. |
ContainsCharactersValidator | Checks a string for the presence of the characters specified in the CharacterSet property. |
DateTimeRangeValidator | Lets you check whether a supplied DateTime object falls within a specified range. |
DomainValidator | Checks whether a value matches one of a list of supplied values. |
EnumConversionValidator | Checks whether a string can be converted to an enum. |
NotNullValidator | Checks to ensure a supplied value is not null. |
ObjectCollectionValidator | Ensures that an object is a collection of a given type, and invokes validation on each element in the collection. |
ObjectValidator | Validates an object reference. |
PropertyComparisonValidator | Compares the value of a property with another property. |
RangeValidator | Checks whether a value falls within a specified range. |
RegexValidator | Checks whether a value matches the pattern specified by a regular expression. |
RelativeDateTimeValidator | Checks whether a value falls within a specified date/time range. |
StringLengthValidator | Ensures that the length of a string is within specified limits. |
TypeConversionValidator | Checks whether a string can be converted to a specific type. |
Now that you have seen an overview of the available validators, I’ll walk you through a simple example.
Simple Example Walkthrough
In this example, you will see how to use built-in validators to validate an object named Employee. To begin, create a new Visual C# Windows Forms application named ValidationExample. In the new project, add references to the Validation Block assemblies. Then add a new class named Employee to the project that contains the code in Listing 1.
Note that the Employee class properties in Listing 1 are decorated with built-in validator classes such as RangeValidator, StringLengthValidator, NotNullValidator, etc., and that the constructors of each validator accept the MessageTemplate property as a named parameter. The MessageTemplate property lets you specify the validation message that will be displayed to the user if validation fails.
After decorating the Employee class with all the validation related attributes, the next step is to execute those validation rules from a client application. For this purpose, add a button named btnValidateEmployee to the Windows form and modify its Click event to look as follows:
private void btnValidateEmployee_Click(object sender, EventArgs e) { Employee emp = new Employee(); emp.EmployeeID = 100001; emp.Name = "Dave Campell"; emp.Gender = "Male"; emp.BirthDate = DateTime.Now.AddYears(-130); emp.EmailAddress = "[email protected]"; lstValidationResults.Items.Clear(); //Validate the Employee object ValidationResults results = Validation.Validate(emp); //Inspect the ValidationResults object if (!results.IsValid) { //If not valid, loop through the ValidationResults foreach (ValidationResult result in results) { lstValidationResults.Items.Add("Key = " + result.Key); lstValidationResults.Items.Add("Message = " + result.Message); lstValidationResults.Items.Add("....."); } } }
The preceding code creates an instance of the Employee class and sets its properties to appropriate values. Specifically it intentionally sets the EmployeeID and BirthDate properties to values that violate the validation rules supplied at the time of Employee class declaration.
Then it invokes the Validation.Validate() method, passing in the Employee object as an argument, and captures the return value (of type ValidationResults collection) in a local variable:
ValidationResults results = Validation.Validate(emp);
![]() | ? |
Figure 1. Validation Output: The EmployeeID and BirthDate properties are decorated with RangeValidator and RelativeDateTimeValidator classes that result in the validation message being displayed in the ListBox. |
When the Validate method returns, the results variable contains a collection of ValidationResult objects. In the foreach loop at the end of the method, the code iterates through the returned collection to display the Key and Message values of each of the ValidationResult objects in a ListBox. Figure 1 shows the output in the downloadable sample application.
The previous example used Validation.Validate() to trigger the validation, but alternatively, you can create an Employee validator class using the ValidationFactory.CreateValidator() method and then invoke its Validate() method to get the same result:
Validator empValidator = ValidationFactory.CreateValidator (); ValidationResults results = empValidator.Validate(emp);
ValidationResult object
As shown in the preceding section, the object returned from the Validation.Validate() method is a collection object of type ValidationResults that is made up of ValidationResult objects. By checking the ValidationResults.IsValid property, you can check for any validation errors generated during the validation process. If the IsValid property returns false, you can then loop through the ValidationResults collection to examine each of the ValidationResult objects contained in the collection. The ValidationResult object exposes the properties shown in Table 2:
Property | Description |
Key | Contains the name of the member that is associated with the validator. |
Message | Describes the validation failure. |
Tag | Provides the value supplied by the user and is mainly used for categorizing the generated validation messages. |
Validator | Returns reference to the validator that performed the validation. |
Grouping Validations with Rulesets
Although the single-validation-per-property approach shown so far in this article with the Employee class works fine, there are times where you might want to have a finer level of control over the validation rules used in different situations. For example, you might want to apply one set of validation rules to an Employee instance before persisting its data in a database, but apply a different set of validation rules tailored toward user interface processing. You can use rulesets to accomplish that. A ruleset is a mechanism through which you can evaluate a set of related validation rules as a named group.
In this case, you can create two different rulesets named “DatabaseRuleset” and “UserInterfaceRuleset,” and invoke whichever is appropriate at runtime by supplying the name of the ruleset to the Validate() method. If you don’t specify a ruleset as part of the type declaration, the Validation Block uses a default “anonymous” ruleset. Here are the steps to implement rulesets in your application:
- Specify the Ruleset attribute as one of the named parameters when you declare the validator attribute. If you are using a configuration file to specify the validation behavior, set the name attribute of the
element to the appropriate value. - Supply the name of the desired ruleset as an argument to the Validate() method.
- Process the ValidationResults collection returned from the Validate() method to examine the messages generated during validation.
To illustrate, modify the EmployeeID and Name properties of the Employee class to add a ruleset named “DatabaseRuleset” (for brevity, the following code shows only the modified lines):
public class Employee { ----- ----- [RangeValidator(1,RangeBoundaryType.Inclusive,10000, RangeBoundaryType.Inclusive,MessageTemplate="Employee ID must be within 1 to 10000", Ruleset="DatabaseRuleset")] public int EmployeeID { get { return employeeID; } set { employeeID = value; } } [StringLengthValidator(50,MessageTemplate="Employee Name must be within 50 characters", Ruleset="DatabaseRuleset")] public string Name { get { return name; } set { name = value; } } ---- ----- }
Note how the preceding code uses the named parameter Ruleset (set to “DatabaseRuleset” in the preceding code) to specify the name of the ruleset. After modifying the class, the next step is to trigger the validation using the ruleset as an argument.
Add a button named btnValidateEmployeeWithDatabaseRuleset to the form and modify its Click event as follows:
private void btnValidateEmployeeWithDatabaseRuleset_Click (object sender, EventArgs e) { Employee emp = new Employee(); emp.EmployeeID = 100001; emp.Name = "Dave Campell"; emp.Gender = "Male"; emp.BirthDate = DateTime.Now.AddYears(-130); emp.EmailAddress = "[email protected]"; // Validate the Employee object ValidationResults results = Validation.Validate(emp, "DatabaseRuleset"); // Inspect the ValidationResults object if (!results.IsValid) { lstValidationResults.Items.Clear(); // If not valid, loop through // the ValidationResults foreach (ValidationResult result in results) { lstValidationResults.Items.Add( "Key = " + result.Key); lstValidationResults.Items.Add( "Message = " + result.Message); lstValidationResults.Items.Add( "....."); } } }
Note that when the listing invokes the Validate() method it passes the name of the ruleset as an argument:
ValidationResults results = Validation.Validate( emp, "DatabaseRuleset");
Because of the ruleset parameter, the Validate() method executes only the validation rules associated with the EmployeeID and Name properties. The application displays the resulting output in the list box.
At this point, it’s worth noting that you can have different attributes with different validations grouped within different rulesets on a single property, so you can arrange validation groups however you like. In addition, you can attach multiple validation rules attached to any property, letting you validate the value in multiple ways. And finally, there’s no restriction that prevents you from using any combination of attribute-based programmatic, or configuration-based validation. As you can see, the Validation Block gives you the freedom to adapt to whatever validation combination or technique is most useful.
Using Configuration-Based Validation
All the examples so far have shown the steps involved in using validation attributes directly from within code. Obviously, this approach has a drawback: Every time the validation logic changes, you must modify the code, recompile it, and redeploy it. In addition, reusing attributed validation code is not straightforward. To address these issues, the Validation Block lets you use configuration-driven validation where you specify the validation behavior in an external configuration file, such as app.config or web.config. Because the validation behavior specification is external to the application, this approach is considerably more flexible and can be ported to other applications more easily.
The simplest way to create a configuration file for use with the Validation Block is to leverage the Enterprise Library Configuration Tool (available via the Start?> Programs?> Enterprise Library 3.0 – April 2007 menu option). For the purposes of this example, consider a simple Department class for which you’ll specify validation behavior through the configuration tool. Here’s the Department class code:
using System; namespace ValidationExample { public class Department { private string name; private string description; public string Name { get { return name; } set { name = value; } } public string Description { get { return description; } set { description = value; } } } }
The Department class has two properties?Name and Description. Now add a validation rule for the Name property to validate that the value supplied is between 1 and 50 characters. Here are the steps:
- Launch the Enterprise Library configuration tool. Select File?> New Application from the menu.
- Right click on the Application Configuration node and select New?> Validation Application Block from the menu.
- Right click on the Validation Application Block node and select New?> Type from the menu. You’ll see the Type Selector dialog box.
- Click on the “Load an Assembly” button and select the name of the assembly that contains the Department class.
- You’ll see a “Type Select” dialog containing a list of classes in the assembly. Select the Department class from the list and click OK.
- Right click on the Department class and select New?> Rule Set from the menu. Change the default rule from “Rule Set” to “DepartmentDefaultRuleset.”
- Right click on the DepartmentDefaultRuleset node and select New?> Choose Members from the menu. In the Member selector dialog, select the Name property and click OK.
- Right click on the Name node and select New?> String Length Validator from the menu.
- Now set the String Length Validator properties to the following values, as shown in Figure 2:
- LowerBound : 1
- UpperBound : 50
- LowerBoundType : Inclusive
- MessageTemplate : Name should be between 1 and 50 characters
![]() | ? |
Figure 2. Configuring Validation in an Application Configuration File: You can use the Enterprise Library Configuration tool to create the configuration file and specify validation behavior for your application. |
Finally, save the configuration file as App.config in the root project directory of the Windows Forms application.
Now, if you open the App.config file in a text editor or in Visual Studio, you should see something similar to the following:
To test the configuration, you need to invoke the validation rule from code. To do that, add a new button named btnValidateDepartmentFromConfig and modify its Click event to look as follows:
private void btnValidateDepartmentFromConfig_Click (object sender, EventArgs e) { Department dept = new Department(); dept.Name = ""; dept.Description = "Production Department"; lstValidationResults.Items.Clear(); ValidationResults results = Validation.ValidateFromConfiguration(dept, "DepartmentDefaultRuleset"); //Inspect the ValidationResults object if (!results.IsValid) { //If not valid, loop through the ValidationResults foreach (ValidationResult result in results) { lstValidationResults.Items.Add("Key = " + result.Key); lstValidationResults.Items.Add("Message = " + result.Message); lstValidationResults.Items.Add("....."); } } }
Note the Validation.ValidateFromConfiguration() method specifically enables you to validate the Department object using the rules supplied in the configuration file. However, you can achieve the same results using the overloaded Validate() method:
ValidationResults results = Validation.ValidateFromConfiguration(dept, "DepartmentDefaultRuleset");
When you call the ValidateFromConfiguration() method, you can also supply the name of the rule set that identifies the group of validation rules to be applied for the Department class.
Using Self-Validation
There are situations where you might want to have complete control over the validation logic but still leverage the foundation provided by the Validation Application Block. You can gain that level of control by using the “self-validation” feature supplied with the block. To create a self-validating class, you decorate the class with the HasSelfValidation attribute and each method with the SelfValidation attribute. This validation method must return void and accept a single input parameter of type ValidationResults. Inside the method, you can add any type of validation logic and set the ValidationResults properties (see Table 2) to appropriate values.
For example, here’s an implementation of a class named Student with a CheckStudentID() method that contains custom validation logic.
using System; using Microsoft.Practices.EnterpriseLibrary.Validation; using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; namespace ValidationExample { [HasSelfValidation] public class Student { public int StudentID; [SelfValidation] public void CheckStudentID(ValidationResults results) { // Compare the StudentID against some other value if (StudentID > 100000) { ValidationResult result = new ValidationResult ("Incorrect ID", this, "StudentID", null, null); results.AddResult(result); } } } }
Note the use of HasSelfValidation and SelfValidation attributes to indicate the self-validating class behavior. Inside the CheckStudentID() method, you create a new ValidationResult object and add it to the ValidationResults collection when the StudentID value is greater than 100000.
Again, the flexibility of the Validation Application Block becomes apparent when you realize that you can freely mix and match customized self-validation with the built-in attributed, programmatic, or configuration-based validation. In other words, adding the SelfValidation attribute doesn’t cause you to lose the ability to validate other class properties with the standard validation.
This article discussed the use of the Validation Application Block to meet common validation requirements, and provided examples showing how to use the Validation Block to validate simple primitive datatype values such as string, integer, and datetime variables. In addition, you saw the steps involved in creating reusable validation rules through application configuration files. Finally, the article showed how to create objects that are capable of performing self-validation. With this tool in your development arsenal, you should now rarely (if ever) need to spend time writing validation plumbing code in your applications.