Working with Custom Validators using Enterprise Library 3
 
Published: 13 Apr 2007
Abstract
Enterprise Library 3 contains the ability to validate a business object and this validation occurs through a standard range of validators. The Framework also provides the ability to create custom validators, which is where this article will continue on to discuss the additional features of the Validation Block.
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 33533/ 76

Introduction

Previously, I wrote about Enterprise Library 3 validation in ASP.NET pages, using the existing Validator classes defined in the Microsoft.Practices.EnterpriseLibrary.Validation namespace.  Through inheritance, it is easy to create your own custom validation and we will look at a few examples of this and see how you can create your own in your applications.

Validation Overview

For this to work using the attribute approach as discussed previously (where each validator is defined as an attribute of the property it will validate), the validator entity requires two classes. First, it requires the interface of the actual validator itself, which inherits from the several base classes available. Secondly, it requires the creation of an attribute class (inherited from ValidatorAttribute), which handles exposing properties through the constructor and the creation of the validator class.  To create a custom validator, there are several classes you can inherit from, and the ones I will illustrate are the Validator, ValueValidator, and DomainValidator classes.  These are all generic implementations, but there are non-generic versions as well.

Validators use the DoValidate method to perform validation, which passes a reference to the value being evaluated, the source control, a key representing the property being validated, and a collection object that every validation result can be added to. With the generic implementation, the object being validated is of the correct generic type, making this method more type-specific.  In addition to overriding the DoValidate method, a default message property is usually required.  The base Validator class has a single method, whereas the ValueValidator has a non-negated and a negated version of the default message. The DomainValidator class handles these methods so you do not need to override it; you just need to provide the data at constructor time.

EmailAddressDomainValidator Example

The first example I created was an email address domain validator, which will inherit from the Validator base class.  For instance, only so many domains may be considered as acceptable for use in our application, such as .com, .net, .gov, .mil, etc.  Any other type of email addresses will require that the user use an alternative email address.  While this may not be a valid example, it does bring up the possibility that we can perform advanced validation capabilities within the domain.  The default constructor defines several domains considered by default, but an overload allows the developer to specify the domains they want to use.  The constructors are defined as follows:

Listing 1

public EmailDomainValidator()
      : base(nullnull)
{
      _domains = new List<string>(new string[] { ".com"".net"".gov"".org"".edu", ".mil" });
}
 
public EmailDomainValidator(params string[] acceptedDomains)
      : base(nullnull)
{
      _domains = new List<string>(acceptedDomains);
}

 

The DoValidate method is where the real work begins.  Based upon the list of accepted domains, the validator will capture the last four characters of the domain and process it.  If the domain is not in the list, then a message is added to the list of validation results.

Listing 2

protected override void DoValidate(string objectToValidate, object
  currentTarget, string key, ValidationResults validationResults)
{
  if (!string.IsNullOrEmpty(objectToValidate) && objectToValidate.Length >= 6)
  {
    string extension = 
 objectToValidate.Substring(objectToValidate.LastIndexOf
      ('.'));
 
    if (!_domains.Contains(extension))
      validationResults.AddResult(new ValidationResult(@
        "The email address domain is not acceptable ", 
 currentTarget, key, null,
        this));
  }
  else
    validationResults.AddResult(new ValidationResult(@
      "The email address must be longer ", 
 currentTarget,key, nullthis));
}

The objectToValidate parameter stores the actual value being validated, which is the parameter that is used to perform the validation work.  The total email address must be six characters, assuming two characters for the "at" sign and period, two characters for the domain, and at least one for the domain and address.  If it meets this, the extension is processed from the last decimal point, and if the domain is not valid, a new result is added to the collection with all of the error information.

The validation result collection takes a ValidationResult object.  This object's constructor defines parameters that are the message to display, the current target, the key, a tag (if provided), and the validator that is validating it.  Note in this example, currentTarget is the PropertyProxyValidator object performing the validation, since we are in an ASP.NET context, and the key value is the name of the business property being validated.

The new property definition for Email is shown below.  I used the overload for the EmailDomainValidator attribute constructor to illustrate how it will appear in the property definition.

Listing 3

[StringLengthValidator(7, RangeBoundaryType.Inclusive, 150,
  RangeBoundaryType.Inclusive,
  "The email address must be between 7 and 150 characters", 
 Ruleset = "primary")
  , ContainsCharactersValidator("@.", ContainsCharacters.All,
  "The email must have an @ and at least one period", 
 Ruleset = "primary"),
  EmailDomainValidator(".com", ".net"".edu"".gov"".biz", ".tv", 
 Ruleset =
  "primary")]
public string Email
{
  get
  {
    return _email;
  }
  set
  {
    _email = value;
  }
}
ValidZipCodeValidator Example

For the next example, the DomainValidator base class validates items that exist in a list, passed to it through the object's constructor.  The list has to be defined statically so that it can be available at the time of construction of the object.  This list is available at the time of validation, so if the item does not exist in the list, then the validation fails.  This occurs in the DoValidate method for us, and no coding effort is needed.

First, we work with a static array of zip codes, which will be used to validate the data. Assume that in the form we only have a limited list of zip codes that are allowed; so only users in that zip code can work with the form. If they enter in a value outside of the zip code array, an error is generated.  Although the form could use a drop down box, for the sake of the example, we are using a textbox, which means the zip code property is a free-form text property and we need proper validation setup.  We begin with the list of values.

Listing 4

private static List<string> ZIPS = 
 new List<string>(new string[] { "15601""15602", 
 "15603""15604", "15605""15607""15607""15608",
 "15609" });

This list gets passed through the constructor as shown below.  Because the list is static, it is available at construction.

Listing 5

public ValidZipCodeValidator() : base(ZIPS) { }

DoValidate does not need to be overridden because it validates the list properly, but it could be if desired.  The new zip code property definition appears below.  The validator attribute takes no constructors, so no additional properties, except for the optional ones, need to be provided.

Listing 6

[RegexValidator(@"\d{5}""The zip code entered is not five digits", Ruleset =
  "primary"), ValidZipCodeValidator(Ruleset = "primary")]
public string ZipCode
{
  get
  {
    return _zipCode;
  }
  set
  {
    _zipCode = value;
  }
}
AuthorizationCodeValidator Example

The authorization code validator inherits from another possible base class, called ValueValidator.  This class is also a generic implementation and provides two message properties to override: one to work when the property is non-negated and one for negated forms.  These properties are shown below.

Listing 7

protected override string DefaultNegatedMessageTemplate
{
  get
  {
    return @"The authorization code cannot appear with the first letter of
      'A'"; }
  }
 
protected override string DefaultNonNegatedMessageTemplate
{
  get
  {
    return "The authorization code must begin with the first letter of 'A'"; }
  }

The class also overrides DoValidate, which uses a custom class to determine whether the validation is successful.  This class could check against an authorization store to ensure an entry exists in the database, XML file, or some other store.  In this example, it just checks to see if the authorization code starts with the letter "A."  If it does, the code is considered valid.  Another validator (StringLengthValidator) is used to ensure that the authorization code is of the correct length.

Listing 8

protected override void DoValidate(string objectToValidate, 
 object currentTarget, string key, 
 ValidationResults validationResults)
{
      return AuthorizationCodeRepository.IsValid(objectToValidate);
}

A new property on the User object has been created, called AuthorizationCode.  It has the new property accessors defined as the following.

Listing 9

[
StringLengthValidator(5, RangeBoundaryType.Inclusive, 
 7, RangeBoundaryType.Inclusive, 
 "The code is outside the range of valid values", 
 Ruleset="primary"),
AuthorizationCodeValidator(Ruleset="primary")
]
public string AuthorizationCode
{
      get { return _authorizationCode; }
      set { _authorizationCode = value; }
}

The authorization code validator attribute takes no parameters in its constructor, so only the optional parameters need be assigned here. This could be expanded to include a connection string name or the name of an XML file to parse; however, this information is more appropriately extracted from the configuration file or a custom configuration section.

Other Considerations

In the custom code I created, I disregarded most of the base class constructors, which pass in a message template that overrides the default, a tag, or a negated property determining whether it is looking at the opposite results.  I disregarded it because it I had no need for it in this example, but if I was building my own custom validation for a framework or library, I would have included additional constructor overloads to incorporate this information.

Updated ASP.NET Page

The updated ASP.NET page now looks like it does below.  I only included the two new properties.  Note that it looks the same as the other properties from the previous example.

Listing 10

<tr>
<td>Zip Code</td>
<td>
  <asp:TextBox ID="txtZipCode" runat="server" />
  <el:PropertyProxyValidator ID="ppvZipCode" 
   runat="server"  
   SourceTypeName="Mains.Examples.User,App_Code"
   PropertyName="ZipCode" RulesetName="primary" 
   ControlToValidate="txtZipCode">
   *</el:PropertyProxyValidator>
</td>
</tr>
<tr>
<td>Authorization Code</td>
<td>
  <asp:TextBox ID="txtAuthorizationCode" runat="server" />
  <el:PropertyProxyValidator ID="ppvAuthorizationCode" 
   runat="server" 
   SourceTypeName="Mains.Examples.User,App_Code"
   PropertyName="AuthorizationCode" RulesetName="primary" 
   ControlToValidate="txtAuthorizationCode">
   *</el:PropertyProxyValidator>
</td>
</tr>

When performing the validation on an empty form, the following error messages are rendered in the browser through the validation summary control.

·         The name must have at least a space between the names.
The name must be between 3 and 150 characters long.

·         The phone number must be between 7 and 10 characters long.
The phone number is not a valid phone number; it can be numbers only.

·         The email must have at least an @ sign and at least one decimal.
The domain of the email address was not determined.
The email address must be between 7 and 150 characters long.

·         The zip code does not exist in the allowable list of zip code values; it must be in a range of 15601-15609.
The zip code entered is not five digits.

·         The authorization code must begin with the first letter of "A."
The authorization code is outside the range of valid values.

All of the new validations work, and as you enter in data, it performs the validation correctly.  This is to illustrate the power of the validation framework in Enterprise Library 3.

Note: This code was created from the Community Technology Preview (CTP) version of Enterprise Library 3.  It may vary from the final release of the product.  Although this version of the Framework is very close to the final, it may vary slightly from the final version.

Conclusion

In this article you have learned how to perform custom validation using Enterprise Library 3.



User Comments

Title: mmm Reply   
Name: Brian
Date: 2008-06-23 10:46:18 AM
Comment:
Validator doesn't inherit from that class, correct. You have to create an accompying attribute that works along side of it. Create another class that inherits from ValidatorAttribute (the attribute class). It has a method you need to override that uses your validator to validate the data. Sorry, I didn't include it in the article.
Title: mmm   
Name: John
Date: 2008-06-21 11:58:45 PM
Comment:
Base class Validator don't inherit System.Attribute. How can i use it in this way:
[StringLengthValidator(7, RangeBoundaryType.Inclusive, 150,
RangeBoundaryType.Inclusive,
"The email address must be between 7 and 150 characters",
Ruleset = "primary")
, ContainsCharactersValidator("@.", ContainsCharacters.All,
"The email must have an @ and at least one period",
Ruleset = "primary"),
EmailDomainValidator(".com", ".net", ".edu", ".gov", ".biz", ".tv",
Ruleset =
"primary")]
????

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-11-29 1:01:06 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search