Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#

Write a Custom Validator for VAB Using the Application Block Software Factory

Rate me:
Please Sign up or sign in to vote.
4.40/5 (12 votes)
26 Jul 2007CPOL12 min read 44.9K   224   23   9
This article is a step by step guide on how to write a custom validator using Application Block Software Factory and integrate it in the configuration editor of Enterprise Library.

Screenshot - VABCustomValidator.jpg

Introduction

This article is a step by step guide on how to write a custom validator using the Application Block Software Factory and integrate it in the configuration editor of Enterprise Library.

When you install Enterprise Library 3.1, you not only get library code and documentation, you also get the Application Block Software Factory. This factory allows you to create a new application block that can sit above the Enterprise Library code services such as configuration and ObjectBuilder. This Software Factory can also help you to create typed and untyped providers for the library such as new TraceListener for the Logging Application Block, Call Handler for the Policy Injection Application Block, Custom Validator - Validation Application Block, etc.

Create a New Provider Library

  1. In Visual Studio, create a new project of type Guidance Packages -> Application Block Software Factory -> Provider Library.
  2. Screenshot - VABCustomValidator0.jpg

  3. This will start a wizard that will request some details from you:
    • Name: The name of the solution and the name of the project that will contain the validator. I named it Bursteg.ProvidersLibrary, and we'll come across this name later in this article.
    • Description: A description that will be placed in the AssemblyInfo.cs of the providers library.
    • Author: Your name.
    • Namespace: Namespace of your choice.

    Screenshot - VABCustomValidator1.jpg

  4. Click Finish and Visual Studio will generate the solution structure for you. The solution contains the Bursteg.ProvidersLibrary project in which the validator will be created, and another project called Bursteg.ProvidersLibrary.Configuration.Design that will contain the visual elements needed in order to integrate this validator into the Enterprise Library Configuration Editor. If in step 1 you created a project with tests, then the solution will also contain two unit test projects.
  5. Screenshot - VABCustomValidator2.jpg

Create the Custom Validator

Generate Initial Code for the Custom Validator

  1. Right click the providers project (Bursteg.ProvidersLibrary) and select Applciation Block Software Factory -> Validation Application Block -> New Validator (Typed).
  2. Screenshot - VABCustomValidator3.jpg

  3. This will start another wizard that will request the name of the validator. For demo proposes, I will create a Person ID Validator that will validate an ID number according to a configured type. I named the validator: PersonIDValidator.
  4. Screenshot - VABCustomValidator4.jpg

  5. Click Finish and Visual Studio will add the necessary references and generate the initial source code for this validator. Notice that Bursteg.ProvidersLibrary has three new files for it:

    • PersonIDValidator.cs contains the code for the validator.
    • PersonIDValidatorAttribute.cs contains the code for the validator attribute in case you want to apply it with an attribute.
    • PersonIDValidatorData.cs in the Configuration folder contains the code for the configuration element of the validator - the class that holds its data that was configured in the validator.

    Screenshot - VABCustomValidator5.jpg

Add Support for Properties

You should follow this step only if your validator receives any parameters. In this guide, the validator will receive a country code parameter and will validate the Person ID with the logic according to his country.

  1. Open PersonIDValidator.cs and add the country code property.
  2. C#
    [ConfigurationElementType(typeof(PersonIDValidatorData))]
    public class PersonIDValidator : ValueValidator
    {
        private int countryCode; 
    
        public int CountryCode
        {
            get { return countryCode; }
            set { countryCode = value; }
        }
        ...
    }
  3. Scroll down in this file and notice this commented code block:
  4. C#
    // TODO: Decide whether a constructor with discrete
    // arguments is necessary for the PersonIDValidator.
    //public PersonIDValidator(object param1, object param2,
    //       string messageTemplate, bool negated)
    //    : base(messageTemplate, null, negated)
    //{
    //    this.var1 = param1;
    //    this.var2 = param2;
    //}

    Uncomment it and create an appropriate constructor that receives the relevant parameters for your validator. In this guide, I will receive the country code parameter. The constructor should look like that:

    C#
    public PersonIDValidator(int coutry, string messageTemplate, bool negated)
        : base(messageTemplate, null, negated)
    {
        this.countryCode = coutry;
    }

    This constructor will be useful when you can initialize the validator by code, or by attribute.

    If the country code is a mandatory parameter (it is in my case) then you should make sure it exists in all the overloads of the constructor. Finally, this is how the constructors should look like:

    C#
    public PersonIDValidator(int country)
        : this(country, null, false)
    { }
     
    public PersonIDValidator(PersonIDValidatorData configuration)
        : this(configuration.CountryCode, configuration.MessageTemplate, 
               configuration.Negated)
    {
    }
     
    public PersonIDValidator(int country, bool negated)
        : this(country, null, negated)
    { }
     
    public PersonIDValidator(int country, string messageTemplate)
        : this(country, messageTemplate, false)
    { }
     
    
    public PersonIDValidator(int coutry, string messageTemplate, bool negated)
        : base(messageTemplate, null, negated)
    {
        this.countryCode = coutry;
    }
  5. Open PersonIDValidatorData.cs (under the Configuration folder) and locate the following commented line:
  6. C#
    // TODO: Add the configuration properties for PersonIDValidatorData.
    // The snippet for creating configuration properties would be useful.

    Add the country code property to this class using the code snippet for creating configuration properties provided with the Application Block Software Factory. (To insert a snippet, click Ctrl + K + X and select the snippet you want. If you want to use this one directly, you can type its shortcut configproperty and click Tab twice).

    Screenshot - VABCustomValidator6.jpg

    Edit the snippet fields with the property's name, configuration name, and type.

    C#
    private const string CountryCodePropertyName = "CountryCode";
    
    [ConfigurationProperty(CountryCodePropertyName)]
    public int CountryCode
    {
        get { return (int)this[CountryCodePropertyName]; }
        set { this[CountryCodePropertyName] = value; }
    }
  7. Go back to PersonIDValidator.cs and modify the constructor that receives PersonIDValidatorData as parameter. This constructor is called when the validator is configured in the configuration file. Simply assign the values from the configuration properties to the validator properties.
  8. C#
    public PersonIDValidator(PersonIDValidatorData configuration)
        : this(configuration.CountryCode, configuration.MessageTemplate, 
               configuration.Negated)
    {
    }

    Notice the first parameter that this constructor overload receives.

  9. Open PersonIDValidatorAttribute.cs and add a data member for the country code.
  10. C#
    private int coutryCode;

    Locate the following commented code:

    C#
    // TODO: Decide whether a constructor with discrete
    // arguments is necessary for the PersonIDValidatorAttribute.
    //public PersonIDValidatorAttribute(object param1, object param2)
    //{
    //    this.var1 = param1;
    //    this.var2 = param2;
    //}

    Uncomment this code and create the constructor with the country code parameter:

    C#
    public PersonIDValidatorAttribute(int country)
    {
        this.coutryCode = country;
    }

    If the country code is a mandatory parameter (it is in my case) then you should remove the default constructor.

  11. In the same file, edit the DoCreateValidator method. This method creates the instance of the validator based on the parameters of the attribute. Make sure to use the constructor overload that receives the country code.

    C#
    protected override Validator DoCreateValidator(Type targetType)
    {
        return new PersonIDValidator(this.coutryCode, MessageTemplate, Negated);
    }

Implement the Validation Logic

  1. Go back to PersonIDValidator.cs and locate the DoValidate method at the bottom of the class. This method will contain the validation logic, but first you must know what each of the parameters mean. From the parameters description above the method:
    • objectToValidate - The object to validate.
    • currentTarget - The object on behalf of which the validation is performed.
    • key - The key that identifies the source of objectToValidate.
    • validationResults - The validation results to which the outcome of the validation should be stored.

    For example: If this validator receives a string to validate (person ID is string in this example):

    C#
    PersonIDValidator validator = new PersonIDValidator(3);
    ValidationResults results = validator.Validate("123");

    Than objectToValidate will be a string containing "123", same as the currentTarget. key will be null.

    If the validator receives an object and validates only a property of that object:

    C#
    Person p = new Person();
    p.PersonID = "123";
    ValidationResults results = Validation.Validate<Person>(p);

    Then objectToValidate will be "123", currentTarget will be the person instance, and key will be "PersonID".

    Now that you understand what each parameter does, we can go on and implement the validation logic. The validation logic in this case is very simple, since this is not the main purpose of this guide. The validator checks the input string and makes sure it ends with the country code.

    C#
    protected override void DoValidate(object objectToValidate, object currentTarget, 
              string key, ValidationResults validationResults)
    {
        // Get the object to validate
        string id = (string)objectToValidate;
     
        // Check if it is valid
        string end = countryCode.ToString().Trim();
        bool isValid = id.EndsWith(end);
     
        // If the negated property is false,
        // and the id is not valid, log a validation error
        if (isValid == Negated)
        {
            LogValidationResult(validationResults, 
                   this.MessageTemplate, currentTarget, key);
        }
    }

Change the Message Templates of the Custom Validator

  1. When you log a validation error, you also add a message that contains the error description. By default the validator uses two message templates - one for the default validation error and one for the negated error. To change the validation error messages, expand the provider library project (Bursteg.ProvidersLibrary) and double click the Resources.resx file under the properties folder.
  2. Screenshot - VABCustomValidator13.jpg

    This will open the Resource Editor and let you edit the messages. Notice that there are two message templates - PersonIDValidatorNegatedDefaultMessage (for the negated template) and PersonIDValidatorNonNegatedDefaultMessage (for the non-negated template).

    Screenshot - VABCustomValidator14.jpg

Add Design-Time support for the Custom Validator

When you develop a Custom Validator for your project, you'll probably want to add design-time support that will enable developers to configure the new validator from the Enterprise Library Configuration Tool just as simple as they would do for every other out-of-the-box validator.

Creating the Design-Time providers node is a recipe that is similar to all Enterprise Library providers and not something specific for each provider type.

  1. Add references to the Validation Application Block assemblies (Microsoft.Practices.EnterpriseLibrary.Validation and Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.Design).
  2. Right click the design-time support project (Bursteg.ProvidersLibrary.Configuration.Design) and choose Application Block Software Factory -> Create Design-Time Provider Node.

    Screenshot - VABCustomValidator15.jpg

  3. This will start a new wizard that will request some detail about the provider:
    • Node Name is the name of the class that will be generated and will represent the node in the configuration tree for this validator. In this guide, it is PersonIDValidatorNode.
    • Runtime Configuration Type is the type that holds the configuration data for this validator, and it was already created for us earlier. In this guide, it is PersonIDValidatorData in the Bursteg.ProvidersLibrary assembly.
    • Base Design Mode is the class to inherit from when generating the configuration type. If you noticed, when we edited the validator class, it inherited from a class called ValueValidator, which has a Design-Time configuration type called ValueValidatorNode in the Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.Design assembly. You should pick this type as the Base Design Mode.
    • Parent UI Node is the parent node in the configuration tree. The validator will always have a parent of the following nodes:
      • SelfNode - When we want to validate an object (the whole object).
      • PropertyNode - When we want to validate a property of a type.
      • MethodNode - When we want to validate method parameters of a type.
      • FieldNode - When we want to validate a field of a type.

    You would probably want to support all of the above options, but the wizard supports only a single selection. You can select SelfNode for now, and we will add the others by code.

    Cardinality indicates whether this validator can be applied more than once. For example - if you want to have two PersonIDValidators validating a single property. For this guide, it is Single.

    Screenshot - VABCustomValidator16.jpg

  4. Click Finish and Visual Studio will generate the code for the design-time configuration support. Notice that Bursteg.ProvidersLibrary.Configuration.Design has four new files for it:
    • PersonIDValidatorNode.cs contains the class that represents the node in the configuration.
    • CommandRegistrar.cs, ConfigurationDesignManager.cs, and NodeMapRegistrar.cs contain the code that registers the new configuration node to the configuration tree in the location we have selected earlier in the wizard.

    Screenshot - VABCustomValidator17.jpg

  5. In step 14, we selected to place the validator node under SelfNode. If you want to add support for other node types, expand the CommandRegistrar.cs file node and open the CommandRegistrar.PersonIDValidatorNode.cs file. This is a part of the file that registers the validator to the configuration.
  6. Edit the AddPersonIDValidatorNodeCommand method: duplicate the code that invokes the AddSingleChildNodeCommand method as many as you need to add the validator parent nodes. Usually you will want to allow all four parent nodes, so you should copy it four times. The only difference between all the four invocations is the last parameter which is the type of the parent node. In step 14, we selected SelfNode, so the other three invocations should use PropertyNode, MethodNode, and FieldNode.

    C#
    sealed partial class CommandRegistrar
    {
        private void AddPersonIDValidatorNodeCommand()
        {
            AddSingleChildNodeCommand(
                Resources.PersonIDValidatorNodeUICommandText,
                Resources.PersonIDValidatorNodeUICommandLongText,
                typeof(PersonIDValidatorNode),
                typeof(Microsoft.Practices.EnterpriseLibrary.
                          Validation.Configuration.Design.SelfNode));
     
            AddSingleChildNodeCommand(
                Resources.PersonIDValidatorNodeUICommandText,
                Resources.PersonIDValidatorNodeUICommandLongText,
                typeof(PersonIDValidatorNode),
                typeof(Microsoft.Practices.EnterpriseLibrary.
                       Validation.Configuration.Design.PropertyNode));
     
            AddSingleChildNodeCommand(
                Resources.PersonIDValidatorNodeUICommandText,
                Resources.PersonIDValidatorNodeUICommandLongText,
                typeof(PersonIDValidatorNode),
                typeof(Microsoft.Practices.EnterpriseLibrary.
                       Validation.Configuration.Design.MethodNode));
     
            AddSingleChildNodeCommand(
                Resources.PersonIDValidatorNodeUICommandText,
                Resources.PersonIDValidatorNodeUICommandLongText,
                typeof(PersonIDValidatorNode),
                typeof(Microsoft.Practices.EnterpriseLibrary.
                       Validation.Configuration.Design.FieldNode));
        }
    }
  7. Change the captions of the Custom Validator as they will show in the configuration tool. Edit the resources file of the design-time support project (Bursteg.ProvidersLibrary.Configuration.Design).
  8. Edit the following resources:

    • PersonIDValidatorNodeName - PersonID validator
    • PersonIDValidatorNodeUICommandLongText - PersonID validator long text
    • PersonIDValidatorNodeUICommandText - PersonID validator command text

    You'll soon see where each of these captions are displayed.

  9. I've already said that adding a design-time support for all provider types is done using the same recipe, but with validators, you have to change some code in order to complete this task.
  10. Open PersonIDValidatorNode.cs and override the CreateValidatorData() method. As an implementation, paste the code from the ValueValidatorData property.

    C#
    public override ValidatorData CreateValidatorData()
    {
        PersonIDValidatorData data = new PersonIDValidatorData(this.Name);
        data.CountryCode = this.countryCode;
        data.Negated = this.negated;
        data.MessageTemplate = this.messageTemplate;
        data.MessageTemplateResourceName = this.messageTemplateResourceName;
        data.MessageTemplateResourceTypeName = this.messageTemplateResourceTypeName;
        data.Tag = this.tag;
        return data;
    }

    After you paste the code, remove the ValueValidatorData property. You don't need it anymore.

  11. Change the constructor that receives PersonIDValidatorData as a parameter. Have it call the base class constructor with the parameter value.
  12. C#
    public PersonIDValidatorNode(Bursteg.ProvidersLibrary.Configuration.PersonIDValidatorData data)
        :base(data)
    {
        ...
    }
  13. In order that the configuration tool can see your provider library and register the validator, you should copy the output assembly of the providers library and design type support (Bursteg.ProvidersLibrary.dll and Bursteg.ProvidersLibrary.Condiguration.Design.dll) to the Enterprise Library bin directory. By default, it is C:\Program Files\Microsoft Enterprise Library 3.1 - May 2007\Bin.

Test the Custom Validator

Test the Validator When Used in Code

  1. Open a new instance of Visual Studio (otherwise the configuration changes will not be applied) and create a new Console Application in order to test the validator. After the project is created, add a new class of type Person:
  2. C#
    public class Person
    {
        private string personID;
     
        public string PersonID
        {
            get { return personID; }
            set { personID = value; }
        }
    }
  3. Add references to the relevant assemblies: (Microsoft.Practices.EnterpriseLibrary.Common and Microsoft.Practices.EnterpriseLibrary.Validation) for the validation support, providers library(Bursteg.ProvidersLibrary), and System.Configuration.dll.
  4. In the Main method, create a new instance of Person and set its ID. Use the validator to check if the ID is valid.
  5. C#
    // Initialize a new instance of Person
    Person p = new Person();
    p.PersonID = "123";
     
    // Validate a valid person ID
    PersonIDValidator validator = new PersonIDValidator(3);
    ValidationResults results = validator.Validate(p.PersonID);
    Debug.Assert(results.IsValid);
    DisplayValidationResults(results);
     
    // Validate an invalid person ID
    validator = new PersonIDValidator(2);
    results = validator.Validate(p.PersonID);
    Debug.Assert(!results.IsValid);
    DisplayValidationResults(results);
     
    // Validate a valid Person ID, but with negated = true
    validator = new PersonIDValidator(3, true);
    results = validator.Validate(p.PersonID);
    Debug.Assert(!results.IsValid);
    DisplayValidationResults(results);
     
    // Validate an invalid Person ID, but with negated = true
    validator = new PersonIDValidator(2, true);
    results = validator.Validate(p.PersonID);
    Debug.Assert(results.IsValid);
    DisplayValidationResults(results);

    Where the DisplayValidationResults method can be something like:

    C#
    private static void DisplayValidationResults(ValidationResults results)
    {
        if (!results.IsValid)
        {
            foreach (ValidationResult result in results)
            {
                Console.WriteLine(result.Message);
            }
        }
    }

Test the Validator When Configured Using an Attribute

  1. Add the validator attribute to the ID property of the Person class.
  2. C#
    public class Person
    {
        private string personID;
     
        [PersonIDValidator(3)]
        public string PersonID
        {
            get { return personID; }
            set { personID = value; }
        }
    }
  3. In the Main method, validate the Person instance to check if the ID is valid.
  4. C#
    // Validate the person
    results = Validation.ValidateFromAttributes<Person>(p);
    DisplayValidationResults(results);

Test the Validator When Configured in the Configuration File

  1. Add a new app.config to your Console Application, and edit with the Enterprise Library Configuration Tool.
  2. Screenshot - VABCustomValidator7.jpg

  3. Add the Validation Application Block Configuration Section. Right click the configuration file name node and choose New -> Validation Application Block.
  4. Screenshot - VABCustomValidator8.jpg

  5. This will add the Validation Application Block configuration section to your configuration file. Add a new type to validate. Right click the Validation Application Block node and choose New -> Type.
  6. Screenshot - VABCustomValidator9.jpg

  7. This will open the Type Selector dialog. Click Load an Assembly and navigate to the output folder of the test project in order to find the assembly that contains the class Person. Finally, select the Person type.
  8. Screenshot - VABCustomValidator10.jpg

  9. When configuring validations for a type using the configuration tool, you cannot add validations straight on the type, you must group a few validations into a Ruleset. Add a new Ruleset by clicking the Person type and selecting New -> Ruleset. After the new Ruleset is added, you should change its name to something meaningful.
  10. Screenshot - VABCustomValidator11.jpg

  11. The Custom Validator we created in this guide validates objects of type string that are IDs of people. In the Person type, we would like to validate the Person property with this validator. To validate this property, we first have to add it to the Person node. Right click the Person node and choose New -> Property. Name the property exactly as it is named in the class definition, case sensitive.
  12. Screenshot - VABCustomValidator12.jpg

  13. Add the PersonIDValidator to the PersonID node. Right click the PersonID node and select New-> PersonID Validator Command Text. (Now you will see where the command text you edited in step 18 goes to.)
  14. Screenshot - VABCustomValidator18.jpg

  15. After the validator is added, a new node will be shown called PersonID Validator (same as the PersonIDValidatorNodeName resource from step 18). Go to the properties pane and set its country code.
  16. In the Main method, validate the person instance to check if the ID is valid, and don't forget to use the name of the Ruleset.

    C#
    // Validate the person
    results = Validation.ValidateFromConfiguration<Person>(p, "DefaultValidation");
    DisplayValidationResults(results);

Conclusion

Creating a Custom Validator is very easy to do, especially with the Application Block Software Factory. You create a provider library and generate the validator and you add design-type support using predefined recipes. Developers can use the new validator as simple as any other out-of-the-box validator.

History

  • July 26, 2007 – Created.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Israel Israel
Guy Burstein is a senior consultant and lecturer at Advantech Technologies – Microsoft Division, specializing in architecture, design and development of distributed applications.
He is a presenter at conferences and user-group meetings and has delivered seminars, courses and hands-on training for developing distributed applications using latest Microsoft tools and technologies.

Comments and Discussions

 
GeneralMy vote of 1 Pin
vijay__p3-Nov-10 0:41
professionalvijay__p3-Nov-10 0:41 
GeneralMy vote of 1 Pin
datlamurali22-Oct-09 9:57
datlamurali22-Oct-09 9:57 
GeneralBusiness logic layer validation [modified] Pin
Alexandru Matei15-Jun-09 23:30
Alexandru Matei15-Jun-09 23:30 
GeneralApplication Block Software Factory does not appear under Guidance Packages Pin
lindocile20-Nov-08 4:37
lindocile20-Nov-08 4:37 
QuestionIssues with Policy Injection block calling custom validators. Pin
Gwydion12-May-08 3:32
Gwydion12-May-08 3:32 
QuestionI don't have the Guidance Packages -&gt; Application Block Software Factory -&gt; Provider Library. Pin
Member 335401214-Mar-08 5:22
Member 335401214-Mar-08 5:22 
GeneralRe: I don't have the Guidance Packages -&gt; Application Block Software Factory -&gt; Provider Library. Pin
Member 335401214-Mar-08 8:10
Member 335401214-Mar-08 8:10 
GeneralDebugger support for Provider Libraries Pin
tillschuemmer21-Nov-07 23:18
professionaltillschuemmer21-Nov-07 23:18 
GeneralRe: Debugger support for Provider Libraries Pin
Guy Burstein (bursteg)22-Nov-07 1:07
Guy Burstein (bursteg)22-Nov-07 1:07 

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.