Click here to Skip to main content
15,879,239 members
Articles / Programming Languages / C#

Introduction to the Validation Application Block

Rate me:
Please Sign up or sign in to vote.
3.65/5 (19 votes)
8 Apr 2007CDDL8 min read 78.1K   203   73   17
An introduction to the Validation Applicaiton Block and its usage.

Introduction

Applications generally must have some type of input to operate; whether the input comes from a user, a database, or another application isn't really important. The important point is that no matter where the input comes from, it should be validated; not doing so can lead to unexpected behavior and consequences. Though it is a must for robust applications, creating validation routines can be time consuming and tedious and not very portable; reuse often involves cut and paste techniques.

In the latest release of Microsoft Enterprise Library 3.0, a new application block has been included that will ease the pain in creating and maintaining validation rules and routines.

This article will give an introductory look at the new Validation Application Block and how it can be used with new and existing code to easily provide customizable and configurable validation routines.

Validation Application Block

The VAB is not a replacement for validation controls such as the ASP.NET RequireFieldValidator. It is designed to follow the patterns established in the other application blocks present in the Enterprise Library, and can be used with, or without, configuration file settings or by adding attributes to your class' properties, fields, or methods. It is also independent of any presentation framework like ASP.NET or WinForms.

Using the Code

Using Validator Attributes

Using an object that should be familiar to most developers, the person or customer business entity, we can see how adding a few attributes can easily provide simple validation.

C#
/// <summary>
/// Example business object class that uses 
/// attributes to define validation rules 
/// </summary> 
public class NoConfigPerson 
{ 
    [RangeValidator(1, RangeBoundaryType.Inclusive, 500, 
                   RangeBoundaryType.Inclusive)] 
    private int m_ID; 

    private string m_FirstName = null; 
    private string m_LastName; 
    private string m_Email; 
    private DateTime m_DOB; 
    private Address m_Address; 
    private DateTime m_StartDate; 
    private DateTime m_EndDate; 
    public NoConfigPerson() 
    { 
    }
#region Properties 
    public int ID 
    { 
        get { return m_ID; } 
        set { m_ID = value; } 
    } 
    [StringLengthValidator(1, 20)] 
    [NotNullValidator] 
    public string FirstName 
    { 
        get { return m_FirstName; } 
        set { m_FirstName = value; } 
    } 
    [NotNullValidator] 
    [StringLengthValidator(1, 30)] 
    public string LastName 
    { 
        get { return m_LastName; } 
        set { m_LastName = value; } 
    } 
    [NotNullValidator] 
    public string Email 
    { 
        get { return m_Email; } 
        set { m_Email = value; } 
    } 
    [RelativeDateTimeValidator(18, DateTimeUnit.Year, 25, DateTimeUnit.Year)] 
    [DateTimeRangeValidator("1960-01-01T00:00:00", 
                            "2007-01-01T00:00:00")] 
    public DateTime DOB 
    { 
        get { return m_DOB; } 
        set { m_DOB = value; } 
    } 
    [ObjectValidator] 
    public Address Address 
    { 
        get { return m_Address; } 
        set { m_Address = value; } 
    } 
    [PropertyComparisonValidator("EndDate", 
             ComparisonOperator.LessThan)] 
    [PropertyComparisonValidator("DOB", 
             ComparisonOperator.GreaterThan)] 
    public DateTime StartDate 
    { 
        get { return m_StartDate; } 
        set { m_StartDate = value; } 
    } 
    [PropertyComparisonValidator("StartDate", 
             ComparisonOperator.GreaterThan)] 
    public DateTime EndDate 
    { 
        get { return m_EndDate; } 
        set { m_EndDate = value; } 
    } 
#endregion 
}

Note that as with the m_ID field, attributes can be placed on the field or property with the same results. In the RangeValidator attribute, you will notice the RangeBoundaryType.Inclusive parameter. This parameter determines how the value will be compared, and can have three values.

  • Inclusive: The value is included in the specified range. ID = 1 is OK, ID = 0 is not.
  • Exclusive: The value is not included in the specified range. ID = 1 fails, ID = 2 passes.
  • Ignore: Ignores the value. Doesn't make sense to use a RangeValidator and be able to ignore the range, but who knows where it could be useful.
C#
[DateTimeRangeValidator("1960-01-01T00:00:00", "2007-01-01T00:00:00")] 

DateTimeRangeValidator can be used to validate a date range by specifying the upper and lower range as either DataTime objects or as strings. If you use a string to specify the date, it must be in UTC format.

C#
[RelativeDateTimeValidator(18, DateTimeUnit.Year)]
[RelativeDateTimeValidator(18, DateTimeUnit.Year, 25, DateTimeUnit.Year)] 

RelativeDateTimeValidator can be used to validate a date or date range relative to the current date. In the first case, the above person will be evaluated to check if they are at least 18 years old. The second will evaluate if the person is between 18 and 25 years old.

C#
[ObjectValidator]
public Address Address 
{ 
    get { return m_Address; } 
    set { m_Address = value; } 
}

The ObjectValidator attribute is used to have the object, in this case Address, validated using the attributes or rules specified for it when the parent object is validated.

And and Or

You'll notice that multiple validation attributes can be applied also. The default operation when comparing multiple attributes is AND. The order that they are evaluated and reported is the reverse of the order in which they are applied.

C#
[StringLengthValidator(1, 20)] 
[NotNullValidator] 
public string FirstName 
{ 
    get { return m_FirstName; } 
    set { m_FirstName = value; } 
}

The above will cause the FirstName property to report the following:

  • "The value cannot be null."
  • "The length of the value must fall within the range "1" (Inclusive) - "20" (Inclusive)."
C#
[NotNullValidator] 
[StringLengthValidator(1, 30)] 
public string LastName 
{ 
    get { return m_LastName; } 
    set { m_LastName = value; } 
}

While this will cause the LastName property to report the following:

  • "The length of the value must fall within the range "1" (Inclusive) - "30" (Inclusive)."
  • "The value cannot be null."

The default evaluation of multiple attributes is an AND operation. Using the attribute ValidatorComposition(CompositionType.Or) will change this. In the below example, the validation will succeed if the StartDate is less than EndDate OR greater than DOB.

C#
[ValidatorComposition(CompositionType.Or)] 
[PropertyComparisonValidator("EndDate", ComparisonOperator.LessThan)] 
[PropertyComparisonValidator("DOB", ComparisonOperator.GreaterThan)] 
public DateTime StartDate 
{ 
    get { return m_StartDate; } 
    set { m_StartDate = value; } 
}

Named Parameters

All validation attributes also have a set of optional parameters that may be specified as Named Parameters.

  • Negated: Setting this to true will reverse the validation result.
  • C#
    [RangeValidator(1, RangeBoundaryType.Inclusive, 500, 
              RangeBoundaryType.Inclusive, Negated=true)]
    ID = 0; 
    • "The value must not fall within the range "1" (Ignore) - "500" (Inclusive)."
    C#
    [RangeValidator(1, RangeBoundaryType.Inclusive, 500, 
              RangeBoundaryType.Inclusive)]
    ID = 1
    • "The value must fall within the range "1" (Inclusive) - "500" (Inclusive)."
  • Tag: Used to specify a string that will appear in the Tag property of the ValidationResult. Useful for a logging or reporting situation.
  • MessageTemplate: Used to specify the message that is displayed when validation fails.
  • MessageTemplateResourceName: Used to specify the resource identifier to be used for the display message.
  • MessageTemplateResourceTypeName: Used in conjunction with the above parameter to indicate the resource assembly and type where the identifier can be found.

As you can see, using these attributes certainly makes performing validation much easier. Now let's move on to an example where reuse can be applied by placing the validation in the configuration file.

RuleSets

RuleSets are used to indicate what set of rules the validation engine should validate against. You specify the ruleset when using the Facade pattern for creating a validator.

C#
static void UseFacade<T>(string RuleSet) where T : new() 
{ 
    T obj = new T(); 
    Validator<T> validator = ValidationFactory.CreateValidator<T>(RuleSet); 
    CheckResults(validator.Validate(obj)); 
}

This will perform validation against the default ruleset, and will produce the same results we have seen previously, because no ruleset has been applied to the attributes.

C#
UseFacade<BusinessObjects.NoConfigPerson>(""); 

This will perform validation against the Rule1 ruleset, and will produce a successful validation since validator attributes for this ruleset have not been supplied.

C#
UseFacade<BusinessObjects.NoConfigPerson>("Rule1"); 

This shows how two rulesets can be specified and used on the same property:

C#
[RelativeDateTimeValidator(18, DateTimeUnit.Year, 25, 
                               DateTimeUnit.Year, Ruleset = "18to25")] 
[RelativeDateTimeValidator(18, DateTimeUnit.Year, Ruleset = "Over18")] 
[DateTimeRangeValidator("1960-01-01T00:00:00", "2007-01-01T00:00:00")] 
public DateTime DOB 
{ 
    get { return m_DOB; } 
    set { m_DOB = value; } 
} 
UseRuleSet("Over18", new DateTime(1980, 1, 1)); 
UseRuleSet("18to25", new DateTime(1980, 1, 1)); 

Using the above code, the first validation will succeed, while the second will fail.

Reusable Validation

By using attributes on your fields or properties, it certainly eases the burden of creating validation routines; however, it doesn't really provide for reusability as you must apply the attributes to new classes. By using a configuration file, we can specify validation rules that can be applied and reused across classes and applications. You can also apply validation to existing objects without the need to modify the code by adding attributes.

As with any Enterprise Library enabled application, life begins with the configuration file and the Enterprise Library Configuration Tool. We won't cover the in depth usage of this tool as it is outside the scope of this article, but will rather concentrate on the VAB sections.

Screenshot - Image1.jpg

After opening or creating a configuration file for your application and adding a Validation Application Block node, you can add assemblies to setup validation rules by bringing up the context menu and selecting New -> Type. Click the Load an Assembly button, and navigate to the proper location. In this example, we will select the Person object in the BusinessObjects assembly to work with.

After clicking the OK button, the Person object will appear in the tree under the Validation node. You create a new ruleset for this object by right-clicking on it in the tree and selecting New -> Rule Set. This will create a ruleset named "Rule Set" (how clever); we will change this to "DefaultRule". Right-clicking on the DefaultRule node and selecting New -> Choose Members will display this dialog:

Screenshot - Image2.jpg

Here, we can choose what properties, methods, or public fields we want to create rules for. For this example, we will select all properties.

Screenshot - Image4.jpg

We can then add the same validation rules we used in the attributes example by right-clicking the desired node and selecting New -> [Appropriate Validator]. In the case of the ID property, we select New -> Range Validator. You can then set the ranges, and any other properties as appropriate. In this case, we will add a lower range of 1 and an upper range of 500.

Screenshot - Image5.jpg

You can see here how the app.config file has been modified to include the validation rules for the properties we have set.

Image 5

C#
/// <summary> 
/// Example business object class 
/// </summary> 
public class Person 
{ 
    private int m_ID; 
    private string m_FirstName = null; 
    private string m_LastName; 
    private string m_Email; 
    private DateTime m_DOB; 
    private Address m_Address; 
    private DateTime m_StartDate; 
    private DateTime m_EndDate; 
    public Person() 
    { 
    } 
#region Properties 
    public int ID 
    { 
        get { return m_ID; } 
        set { m_ID = value; } 
    } 
    public string FirstName 
    { 
        get { return m_FirstName; } 
        set { m_FirstName = value; } 
    } 
    public string LastName 
    { 
        get { return m_LastName; } 
        set { m_LastName = value; } 
    } 
    public string Email 
    { 
        get { return m_Email; } 
        set { m_Email = value; } 
    } 
    public DateTime DOB 
    { 
        get { return m_DOB; } 
        set { m_DOB = value; } 
    } 
    public Address Address 
    { 
        get { return m_Address; } 
        set { m_Address = value; } 
    } 
    public DateTime StartDate 
    { 
        get { return m_StartDate; } 
        set { m_StartDate = value; } 
    } 
    public DateTime EndDate 
    { 
    get { return m_EndDate; } 
    set     { m_EndDate = value; } 
    } 
#endregion 
}

As you can see in this class, no attributes have been applied, yet when we run this code, validation is performed and we get the same messages as above.

C#
UseRuleSet<BusinessObjects.Person>("", new BusinessObjects.Person());

DefaultRule has been set as the Default Rule to be applied to the Person object in the config file. The same could be accomplished by this:

C#
UseRuleSet<BusinessObjects.Person>("DefaultRule", 
            new BusinessObjects.Person());

Now the validation rules that have been created for the Person object can be carried along to other applications that use the object, or different rules can be applied to the same object in multiple applications.

Self Validation

What if you had an object that needed some complex validation logic, something beyond the simple validators provided, or you wanted to make use of existing validation routines? As with all of the Enterprise Library Application Blocks, you can customize it and write your own validator. This method is outside the scope of this article. Another way would be to use the self validation capabilities present.

By adding the HasSelfValidation attribute to your class, you are indicating to the VAB that you are providing a validation method. This method must have the SelfValidate attribute, must return void, and must have a single input parameter of type ValidationResults. It is up to the developer to perform any validation and set the ValidationResults appropriately before exiting. As shown in this code snippet, more than one validation can be provided and will be evaluated.

C#
[HasSelfValidation] 
public class SelfValidateAddress 
[SelfValidation] 
public void CheckState(ValidationResults results) 
{ 
    // Perform custom validation here 
    ValidationResult result = 
         new ValidationResult("State not correct", 
                              this, null, null, null); 
    results.AddResult(result); 
} 
[SelfValidation] 
public void CheckZipCode(ValidationResults results) 
{ 
    // Perform custom validation here 
    ValidationResult result = 
    new ValidationResult("ZipCode does not match City", 
                         this, null, null, null); 
    results.AddResult(result); 
} 

Conclusion

This article has attempted to present an introduction and basic understanding of the new Validation Application Block that is available in Enterprise Library 3.0. Hopefully, this has given you a glimpse at how it can be used to build robust applications that can easily contain validation and be used in multiple applications, both new and existing ones.

References

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)



Comments and Discussions

 
QuestionValidation for CC,Bcc in Email sending Pin
pullareddy S31-Oct-13 19:42
pullareddy S31-Oct-13 19:42 
GeneralDatabound ComboBox validation Pin
spankyleo1237-Feb-10 22:52
spankyleo1237-Feb-10 22:52 
GeneralRe: Databound ComboBox validation Pin
Not Active8-Feb-10 2:16
mentorNot Active8-Feb-10 2:16 
GeneralBind Validation to a textbox Pin
jake_3.528-Jan-10 0:55
jake_3.528-Jan-10 0:55 
GeneralRe: Bind Validation to a textbox Pin
Not Active28-Jan-10 1:32
mentorNot Active28-Jan-10 1:32 
GeneralImplementing VAB for a Master Code Check Pin
kssaran7-Apr-08 5:35
kssaran7-Apr-08 5:35 
GeneralRe: Implementing VAB for a Master Code Check Pin
Not Active7-Apr-08 6:09
mentorNot Active7-Apr-08 6:09 
QuestionJPEG? Pin
msoftmvp10-Sep-07 13:23
msoftmvp10-Sep-07 13:23 
AnswerRe: JPEG? Pin
Christian Graus10-Sep-07 14:31
protectorChristian Graus10-Sep-07 14:31 
Questionconfiguration Pin
Taras6116-Aug-07 20:41
Taras6116-Aug-07 20:41 
AnswerRe: configuration Pin
Not Active7-Aug-07 1:46
mentorNot Active7-Aug-07 1:46 
QuestionValidation of Colection Pin
Taras61118-Jul-07 23:06
Taras61118-Jul-07 23:06 
AnswerRe: Validation of Colection Pin
Not Active19-Jul-07 2:06
mentorNot Active19-Jul-07 2:06 
GeneralValidation over inheritance hierarchy Pin
hitch7725-Apr-07 5:06
hitch7725-Apr-07 5:06 
GeneralRe: Validation over inheritance hierarchy Pin
Not Active25-Apr-07 5:47
mentorNot Active25-Apr-07 5:47 
GeneralDownload link [modified] Pin
dwatkins@dirq.net9-Apr-07 5:39
dwatkins@dirq.net9-Apr-07 5:39 
GeneralRe: Download link Pin
Not Active9-Apr-07 6:12
mentorNot Active9-Apr-07 6:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.