Click here to Skip to main content
Click here to Skip to main content
Go to top

Simple Validation in VS.NET 2003

, 28 Nov 2004
Rate this:
Please Sign up or sign in to vote.
A design for simple (passive – no eventing) business layer validation

Sample Image - simplevalidation.jpg

Introduction

This is the first in a series of articles, code samples and presentations that deal with design (and architecture?) ‘Below’ the enterprise level. Enterprise patterns are valuable for applications that have requirements for scalability, availability, maintainability, security and manageability, but using them for applications with less strenuous requirements (over-engineering) can have a negative impact on both developability and maintainability.

So I approached building this design sample in a different way. The primary requirement is that it does the job; the secondary requirement is that it is easy to explain/understand. Nothing else gets a look in. This has led to some interesting design decisions – it doesn’t look like anything I have built for a while. I’ll point these design points out as we go through it.

If you download simplevalidation.zip from the source files link above, extract and open the SimpleValidation solution it contains using VS.NET 2003 (it works OK in VS.NET 2005 as well) you will see the following projects:

  • DataTransferObjects
  • IntegerValidationRules
  • OrderValidationRules
  • ValidationTest

This solution implements a simple (passive – no eventing) way of performing business layer validation. The objective here is to implement validation of datasets, business object collections, XML documents or whatever else you are using as your data transfer objects as an aspect. If you don’t understand where this would fit in a layered service oriented design, I’ll cover ‘standard’ (simple?) layers and tiers in another article in a couple of weeks.

To see what it does, run the ValidationTest (default) project. This exercises two different validation libraries, IntegerValidationRules and OrderValidationRules.

Integer Validator

When you click on the Validate button in the Integer test it ends up executing the following line of code:

Dim results As IntegerValidationRules.ValidationResultCollection = 
  IntegerValidationRules.Validator.Validate(ii, -1)

This calls the Validate method of the Validator class in the IntegerValidationRules assembly, passing in an object to validate and the maximum number of validation failures we will put up with before giving up (-1 means any number).

The first part of the validate method (shown below) loads up the ValidationRules collection if required. This is the first interesting decision. Normally I would construct a class (Validator) and call a method on it. Object construction is not expensive in .NET so performance is not really a reason to use static methods.

IntegerValidationRules Validator – Part 1

public class Validator
{
 public static SortedList ValidationRules; 
  public static ValidationResultCollection Validate(
object objectToValidate, int maxValidationFailures)
  {  
    if  (ValidationRules == null)
    {
       // Add the validation rules from the current 
       // assembly to the set to use - sorted by Priority
       ValidationRules = new SortedList();
       Assembly vAss = Assembly.GetExecutingAssembly();
       foreach (Type typ in vAss.GetTypes())
       {
          if (typ.BaseType == typeof(ValidationRuleBase))          {
            ValidationRuleBase rule = 
  (ValidationRuleBase) vAss.CreateInstance(typ.FullName);
            ValidationRules.Add(rule.RulePriority.ToString() + 
                 rule.RuleName, rule);
          }  
        }
     }

The reason the Validate method is static is because the ValidationRules variable is static and there are no other member variables of the class, so Validate may as well be static (save a line of code when calling it). I am using reflection to work out what the validation rules in this assembly are and because reflection is expensive, I only want to reflect on this assembly once and store the result in the static ValidationRules collection which ends up containing - after the first time I execute this method - a list of all the classes in this assembly that inherit from the ValidationRuleBase class, sorted by priority.

Another option would have been just to populate a list of all the validation rules in code. The reason I did it using reflection is because I feel it is more obvious. The validation rules called by this assembly’s Validator’s Validate method are all the classes in the assembly that inherit ValidationRuleBase. The good news is that you can easily see what these will be (look at the files in the assembly) the bad news is that you can’t chop and change these at run time – but did you need to?

Once we have worked out what the validation rules are (or figured that they are already loaded) we go ahead and call the Validate methods of each rule.

IntegerValidationRules Validator – Part 2

// Perform Validation
ValidationResultCollection results = new ValidationResultCollection();
int failures = 0;
foreach (ValidationRuleBase rule in ValidationRules.Values)
{
   ValidationResultCollection ruleResults = rule.Validate(objectToValidate);
   if ((ruleResults != null) && (ruleResults.Count > 0))
   {
     results.AddRange(ruleResults);
      if (maxValidationFailures > -1)
      {
         foreach (ValidationResult result in ruleResults)
         {
            if (result.ResultType == ResultType.Failed)
            {
               failures++;
               if (failures >= maxValidationFailures)
               {
                  return results;
               }
            }
         }
      }
   }
} 

The validate methods return a collection of ValidationResults, which may contain zero or more results. This is to give maximum flexibility to the individual rules. The collections of results from each rule are concatenated into a collection of results for the whole Validator and returned.

As we return another interesting design decision is obvious. The types we are using such as IntegerValidationRules.ValidationResult, IntegerValidationRules.RulePriority, IntegerValidationRules.ResultType etc. are implemented inside each Validation Rules library – they are not shared.

The bad thing about this is that we can’t for example easily combine the results of one validation library with another, each one stands alone with its own set of types. Again the reason this is done is for simplicity:

  1. We don’t have to make any references from the validation rules libraries and their clients to a shared ‘base’ assembly
  2. We can easily change the way any individual validation rules library works, say changing the priorities available for rules, without impacting any other code
  3. Its more obvious (most important reason) what’s happening here

If you take a look at a ValidationRule such as IntegerObjectsShouldBeEvenRule you will notice that the Validate method (and the Validator class’ Validate) are not strongly typed (they take obects).

public override ValidationResultCollection Validate(object objectToValidate)
{
 // This rule only checks integers
 if (!(objectToValidate is int)) { return null; } 

Normally I am a firm believer in typing everything, but objects are more generic to start with and because we are sharing nothing between assemblies the developer can change this in the Validator and ValidationRuleBase classes for a specific rule library if required.

Order Validator

The second example shows validation of a strongly typed dataset (STDS). This STDS would actually map to a couple of tables in the AdventureWorks sample database but I didn’t want to mess with SQL Server, so the dataset is constructed in code by the testbed’s form load method.

The DataTransferObjects (DTO) assembly contains the definition for the dataset. We are simulating code that would run in the business layer of an SOA application, and the DTO should define the (serializable of course) messages we are sending between layers – in this case OrdersDataSet.

When you click validate a call to the Validate method of the Validator class of the OrderValidationRules assembly is called:

Dim results As OrderValidationRules.ValidationResultCollection = 
  OrderValidationRules.Validator.Validate(drOrder, -1) 

This time we pass an Order datarow, which represents an order. Exactly what the individual validations rules do is very open, they can check stuff inside the object being validated (which the sample ones here do) or they can access a database for more information, or they might call a web service – the validation ‘plumbing’ doesn’t care at all.

One thing the plumbing doesn’t do is muck around with the DataTable Error Collections. They are there and can be very useful, but adding support for these to the generic part of the Validation framework would have made it more complex. If the Validation rules want to work with these they can.

Instead we are using a more generic approach. The ValidationResult class includes Reference and ReferenceHint fields. These are used in the case of the DataSet to indicate which row of which table the rule is complaining about.

Eventually we get back to the caller which then has the option of iterating through the result collection and using the information available there to set the datatable errors collections (it doesn’t).

Code Generation - Lite

OK, well how would you use this yourself?

If you download, unzip and run the exe from the template installer link at the top of this article, it will install a new project type (“Validation Rules Library”) and a new Code project item type (“Validation Rule”) for both VB and C#.

Using the project types is a way to do code generation, and that is one of the things that is happening here – code generation (which is good).

Well that's it (it's supposed to be 'Simple' after all). I hope that this sample is useful to somebody! Check my blog at http://endintiers.com/ for more 'simple' .NET designs.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Chris Hewitt
Web Developer
Australia Australia
.NET Consultant/Trainer with Readify check my blog

Comments and Discussions

 
Generalover-engineering Pinmemberinetfly12312-Sep-07 12:32 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 28 Nov 2004
Article Copyright 2004 by Chris Hewitt
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid