N-Layered Web Applications with ASP.NET 3.5 Part 2: Introducing the Validation Framework


NOTE: the concepts presented in this article are now considered obsolete possibly because better alternatives are available.

Update!! - I have written a new series on N-Layer design targeting ASP.NET 4.5 and Entity Framework 5. You can check out the new series here.

Update 02/03/2009: There is now a VB.NET version of the application available thanks to Sven Huijbrechts from ClearMedia bvba. Check it out at the Downloads section at the end of this article

Note: this is part two in a series of six. If you rather read this entire series off-line, you can buy the series as a convenient PDF document that comes with the full source. Besides the convenience, buying the PDF will also make you feel good as it shows your appreciation for the articles and helps me pay the bills for my server and hosting so I can keep running imar.spaanjaars.com and continue to provide you with great content. For more details, check out this post that shows you how you can buy the entire series right now.

This is part 2 of a six-part series of articles on N-Layer design using ASP.NET 3.5. This article series builds on top of my article series on N-Layer design that I released in early 2007. If you haven’t already done so, be sure to check out these articles first, as a lot of code and concepts used in this new series is explained in detail in the older series.

This installment and the next one deal with validation and show you how to implement a Validation Framework into your application in order to stop unwanted data from getting into your database.

After you’ve read the previous series, be sure to check out part 1 of this new series first, as it gives you an overview of the design of the new application, what’s new compared to the previous version, and what you can expect from this entire article series. Here’s a complete list of all the articles in both series that are currently available:

Quick Links to All Articles

Previous Series
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 1
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 2
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 3
Custom Sorting with N-Layer Design Classes and the GridView

Current Series
N-Layered Web Applications with ASP.NET 3.5 Part 1: General Introduction
N-Layered Web Applications with ASP.NET 3.5 Part 2: Introducing the Validation Framework
N-Layered Web Applications with ASP.NET 3.5 Part 3: Advanced Validation Topics
N-Layered Web Applications with ASP.NET 3.5 Part 4: Sorting, Paging and Filtering
N-Layered Web Applications with ASP.NET 3.5 Part 5: Dealing with Concurrency
N-Layered Web Applications with ASP.NET 3.5 Part 6: Security

Remember: you can buy the full set of articles right now if you want to say "thanks" in return for these articles or you just want to have the whole set as a single, convenient PDF.

Part 2, which you are reading now, deals with Validation. Validation is at the core of most business applications. Without any form of validation you’re pretty much lost and you’ll find your database flooded with invalid data, ranging from simple typos to complete viruses injected using SQL injection. So, the necessity of validation is beyond dispute, but how do you implement it? As always, you have many different options.

Introducing Validation

It’s probably no surprise that in a multi layer application you can add validation code to all of your layers. In case of the Contact Manager Application it means you can add it to the UI Layer, the Business Layer and to the Data Access Layer. In order to understand the benefits and possibilities of each of these options, it’s important to look at them in some detail.

Validation in the Presentation Layer

One common technique for validation in the Presentation Layer is using the ASP.NET Validation Controls. When they were introduced in ASP.NET 1.0 they really meant a boost in developers’ productivity and site security as they removed the need for a lot of custom (and often forgotten) validation code. (They didn’t work on browsers other than Internet Explorer but that has been fixed a long time ago). The Validation Controls are simple to use, provide validation services at both the client (for a good user experience) and at the server (to protect your data) and provide enough features to enable most of your validation needs in the Presentation Layer. I emphasize the Presentation Layer for a reason, as you’ll see in a bit. In its simplest form, validation using the ASP.NET controls only requires a few lines of code. In the following example you see a simple TextBox, a RequiredFieldValidator and a Button. The RequiredFieldValidator ensures at the client that the user has entered a value before the form can be submitted.

	<asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="reqVal" runat="server" 
       ErrorMessage="Please enter a value" ControlToValidate="TextBox1" />
<asp:Button ID="Button1" runat="server" Text="Button" />

Whether JavaScript is turned on or off at the client, you can check the validity of the page on the server using the IsValid property of the Page class:

	protected void Button1_Click(object sender, EventArgs e) 
{ 
  if (Page.IsValid) 
  { 
    // TextBox1 has a value so we can proceed.
  } 
}

So, you may ask, if this is all so easy to implement, why not use it for all your sites and applications? The answer is: it validates on the Presentation Layer, and on the Presentation Layer only. While this is nice for some simpler sites as the original Contact Manager Application, it won’t cut it in larger applications where the Bll or Business Entities projects are shared by other front ends. For example, what happens when you have a Command Line tool for bulk importing contacts from a text file? What happens when you allow your users to submit changes or get the latest details of your contacts through web services? Or how would you ensure proper validation when you decide to build a simple Win Forms data entry application to allow your users to enter data on a desk top application?

In all of these cases, you would need to rewrite your validation mechanism. While in itself this is not so bad (you need a way to overcome the differences in presentation between a Web Application and a Win Forms Application anyways) it poses a bigger issue than just more work: developers may — deliberately or not — forget to implement validation in the presentation or front end layer. So, your cool Web Service or Command Line Import Tool may work today, but may (and probably will) fail miserably tomorrow, possibly because the contact records file you’re trying to import has changed or contains invalid data. So, you’ll need to look into validation in deeper layers - like the business and data layer - to better protect your data and its integrity. I’ll show you the possibilities of validation in the data layer first, and then show you how I would have coded the business layer before I started using the Validation Framework.

Validation in the Database

I love validation at the data layer. It’s like a big, aggressive pit-bull guarding your house, not allowing anyone in except for his boss. It’s your last line of defense. In fact, it’s your last opportunity to sanitize or reject invalid data, before it hits your main database, possibly corrupting your important business data, leading to losses, bankruptcies, or other unforeseen disasters.

Validation in the database is pretty easy to implement. For example, for a required field whose value comes from a different table, all you need to do is connect a few tables to create a relationship between a primary key and a foreign key. Likewise, adding a business rule like “this field must have a value between 0 and 100” is as simple as adding a Check Constraint, either through the SQL Server Management Studio interface or through some custom T-SQL code:

	ALTER TABLE  dbo.Category ADD CONSTRAINT
CK_Category CHECK (SortOrder >= 0  AND SortOrder <= 100)
GO      

With this constraint, SQL Server will check the value of SortOrder any time it’s set (with INSERT and UPDATE statements) and reject the value if it’s not between 0 and 100.

This is just a simple example, but you could easily apply these kinds of rules to many other columns or relationships, protecting your data at the “deepest” level in your application architecture. So, judging from this, it seems that validating your data at the database level is a smart thing to do.

In many ways, it is. Except it has this huge issue: it doesn’t lend itself very well to be displayed in the Presentation Layer. Typically, when you violate a database rule, you get a nerdy looking error message like this one:

Msg 515, Level 16, State 2, Line 1
Cannot insert the value NULL into column 'ContentTypeId', table 'Test.dbo.Category'; column does not allow nulls. INSERT fails.
The statement has been terminated.


Useful as this may be to us, web and database developers, I am sure you agree your users typically expect something else. Something along the lines of “You forget to choose a type of content for this article. Please choose an option from the Type drop down and try again. And if you forget it again, it’s *you* who gets terminated. Make no mistake about it”. Whatever your house style of communicating with users, you need a way to translate these database errors into some stuff that users can understand. While you could catch these messages from your .NET Connection objects, convert them into something your user understands and then display them in the Presentation Layer, this is not an easy or straightforward thing to do.

Another downside is the moment where this validation takes places. Ideally, you want to validate the data before you start hitting the database, minimizing network overhead and processor time on the database server. When you validate data in the database, you’re incurring too much unnecessary overhead.

So, while database validation is great and should always be used no matter where else you’re validating your data, there’s another, more user friendly way to approach data validation. I am sure you saw this one coming, but if not, the answer is: Validation in the Business Layer.

Validation in the Business Layer

In my opinion, validation in the Business Layer is a very natural thing to do. It feels like slipping into a pair of your favorite sneakers: you just know beforehand that they’ll fit exactly right. The Business Layer is where you work with your data, where you accept it from the UI before you move it on to the database, it’s where you determine who can see what or make what changes and so on. Basically, the Business Layer is where all your data comes together in order to represent something useful. So, it makes a lot of sense to implement validation logic in there as well.

In my original article series I left out validation as I wanted to keep things simple — at an entry level that anyone could follow. But I did get a lot of requests for it; where to implement it, how to do it exactly and so on. Usually, I recommended implementing a private Validate method that would throw an exception when any of your business rules was invalid. For example, you could call the following Validate method from the Save method of the EmailAddress class:

	private static void Validate (EmailAddress myEmailAddress) 
{ 
  if (myEmailAddress.ContactPersonId <= 0) 
  { 
    throw new InvalidOperationException("Can't save an EmailAddress 
              without a valid ContactPersonId."); 
  } 

  if (string.IsNullOrEmpty(myEmailAddress.Email))
  { 
    throw new InvalidOperationException("Can't save an EmailAddress 
              without an e-mail address."); 
  } 

  if (!IsValidEmailAddress(myEmailAddress.Email)) 
  { 
    throw new InvalidOperationException("Can't save an EmailAddress 
              without a valid e-mail address."); 
  } 
} 

public static int Save(EmailAddress myEmailAddress) 
{ 
  Validate (myEmailAddress);
  myEmailAddress.Id = EmailAddressDB.Save(myEmailAddress);
  return myEmailAddress.Id; 
} 

While this approach certainly works, it has a few drawbacks. First of all, it means writing lots of repetitive and boring code. Since it’s so boring to write, it’s easy to forget to validate one or more business rules. Secondly, it uses an annoying “one by one” pattern, where the error conditions are fixed one at a time. You get an error about the contact person ID, fix it and try again, only to be greeted by an error stating that the e-mail address is invalid. Finally, it uses exception based validation which means you need to use try/catch blocks whenever you’re calling methods that trigger the validation.
Obviously, it would be a lot easier if you could present a complete list with broken business rules in the user interface (or wherever you need it).

Wouldn’t it be great if you could do something like this in the code behind of your pages (assuming you have a ContactPerson instance stored in myContactPerson:

	if (myContactPerson.Validate()) 
{ 
  ContactPersonManager.Save(myContactPerson);
} 
else
{ 
  lstErrors.DataSource = myContactPerson.BrokenRules;
  lstErrors.DataBind();
} 

And wouldn’t it be great if all you need to do to display a list of errors is to drop a data-bound control like a BulletedList on your page (lstErrors in the example above) that would take care of displaying the errors like this (or any other way you prefer of course):

  • Enter your first name.
  • Enter your last name.
  • Enter your date of birth.

And finally, I am sure you agree it would be very useful if that list of errors would automatically adapt to the language the user has chosen.

Read on to see how you can accomplish this with a relatively small amount of code.

Introduction to the Validation Framework

As I mentioned in the first part of this series, the Validation Framework used in the Contact Manager Application is inspired by the work by AzamSharp in his article “Creating a Domain Object Validation Framework”. (Or maybe by Jan Tielens; see here for more information). Before I read AzamSharp’s article on validation, I had been working on various flavors of validation myself, including some using custom attributes on properties (I am a big fan of attributes as you can find out here and here).

My earlier solutions typically required a lot more code than the one presented by AzamSharp. But even though I like this framework, it has a few shortcomings that I wanted to address (lack of built-in localization behavior, no Validate() method on the business entity but on a separate ValidationEngine instead, generics List<T> types in the public API and a BusinessBase class which in reality was more like a ValidationBase. Over the next couple of sections I’ll show you how I fixed these issues.

Design of the Validation Framework

Figure 15 shows a class diagram of a typical ContactPerson class that inherits from the ValidationBase class. (Later you’ll see another intermediate class between these two called BusinessBase providing translation services and other shared behavior; for now, just focus on the main ValidationBase class). This ValidationBase contains one property (BrokenRules which is described later) and two important methods: GetValidationMessage and Validate.

The ContactPerson Class Inherits from ValidationBase
Figure 15 – The ContactPerson Class Inherits from ValidationBase

It’s this latter method that you saw at work when I showed you a quick example of the Validation Framework:

	if (myContactPerson.Validate()) 
{ 
  ContactPersonManager.Save(myContactPerson);
} 

So, how does the Validate method know whether the ContactPerson is valid? Simple: it asks all of its properties if they are valid or not. Normally, properties are not able to express whether they contain a valid value or not. However with the use of the Attribute class this is easy to implement. I’ll show you a quick example first, then dig deeper into the architecture later, explaining how things work together.

If you look in the code for the ContactPerson class in the BusinessEntities namespace you’ll find the following property for the contact’s FirstName:
 

	/// <summary>
/// Gets or sets the first name of the contact person.
/// </summary>
[NotNullOrEmpty(Key = "FirstNameNotEmpty")] 
public string FirstName { get; set; }

Notice the NotNullOrEmpty attribute on top of the property. Basically this says: when you try to validate me, I’ll be valid when I contain a value that is not null and not an empty string. Likewise, the Email property looks like this:

	/// <summary>
/// Gets or sets the actual e-mail address text of the e-mail address.
/// </summary>
[NotNullOrEmpty(Key = "EmailNotEmpty")] 
[ValidEmail(Key = "EmailNotValid")] 
public string Email { get; set; }

These two attributes require you to enter a value for the e-mail address that is not null or empty. On top of that, you have to make sure that the Email property contains a valid e-mail address as well (that is, it must contain an @ symbol, a period, contain a user and a domain name or IP address and so on).

The Key property of the attribute is used as the localization key for multi-lingual web sites. If you don’t have a localized site, you can add the validation message to the Message property instead:

	[NotNullOrEmpty(Message = "Enter your first name.")] 
public string FirstName { get; set; }

You add these attributes to your code at development time, simply by typing their code on top of a property. You’ll see more examples of these attributes, how to create and apply your own and an explanation of the Key property later. For now, I’ll give you an overview of how this validation process works first.

From the Attributes Class to the Validate() Method to the BrokenRules Class

Imagine you have a simple class with a single property with a single validation attribute that marks the field as required. Something like the following User class will do:

	public class User : ValidationBase
{  
  [NotNullOrEmpty(Message = "Please enter a user name.")] 
  public string UserName { get; set; } 
}

Also imagine that you have a new instance of this class without the UserName set. That means that the class should be considered invalid and that its BrokenRules collection should contain a BrokenRule instance indicating the reason why the rule is broken.

You could write a quick unit test to check whether this is the case or not:

	[TestMethod] 
public void NewUserIsInvalid() 
{ 
  User myUser = new User(); 
  Assert.IsFalse(myUser.Validate());
  Assert.IsTrue(myUser.BrokenRules.FindByPropertyName("UserName").Count > 0);
}

Straight forward as the example may be from a code perspective, a lot is going on under the hood to make it work. Here’s a run-down of the numerous steps involved:

  1. A new user is instantiated. Because the UserName property is defined as an automatic property, it will simply default to null.
  2. The Validate method is accessed (more later on why this is a method and not a property).
  3. If Validate() is overridden in the child class, that version is called. Since that’s not the case here, the version of Validate in ValidationBase is called.
  4. This method first gets rid of any existing BrokenRule instances by calling Clear on the BrokenRules collection.
  5. Then Validate uses reflection to get a list of all properties that require validation and tries to validate them:
    1. First, it gets a list of all the public and instance properties of the class.
    2. It then loops through each of these properties and for each property it loops through its validation attributes.
    3. For each attribute it checks if it’s valid by calling IsValid on the attribute.
    4. The specific attribute checks the value of the property it’s attached to and determines if it meets the criteria set for the property.
    5. When the attribute determines the value is not valid, a new BrokenRule instance is filled with the error message that is retrieved from the attribute and the property name retrieved from the property. This BrokenRule instance is then added to the BrokenRules collection of the User instance.
  6. Eventually, Validate returns false because the UserName rule is broken. This causes the Assert to succeed.
  7. The BrokenRules collection should now contain a BrokenRule for the UserName property. Calling FindByPropertyName gives you a new BrokenRules collection containing all the broken rules for the requested property. (Notice that the unit test is the only piece of code that uses a string literal to refer to a property name. All other code in the Validation Framework uses strongly typed objects and members.).

Obviously, this is just a high-level overview of the process. The next couple of sections dig deeper in the actual code for the classes that make up this Validation Framework.

The ValidationBase Class and the Validate Method

The ValidationBase class is the base class for all classes that want to implement validation. Figure 16 shows its class diagram:

The ValidationBase Class
Figure 16 – The ValidationBase Class

This class is marked as abstract which means you can’t create instances of it. This makes perfect sense as there is no point in having ValidationBase instances in your application, It’s only purpose (and a good one at that) is to serve as a base class for other classes in your application, like a ContactPerson or an EmailAddress.

Besides the BrokenRules property, ValidationBase contains two methods: Validate() and GetValidationMessage.

The Validate method is responsible for checking the validity of all the properties on a business entity (a class that ultimately inherits from ValidationBase). It does this by executing LINQ queries to find all public properties and their validation attributes. The method has two overloads which are implemented as follows:

	public virtual bool Validate() 
{ 
  return Validate(true); 
} 


public virtual bool Validate(bool clearBrokenRules) 
{ 
  if (clearBrokenRules) 
  { 
    this.BrokenRules.Clear();
  } 
  PropertyInfo[] properties = this.GetType().GetProperties(
          BindingFlags.Public | BindingFlags.Instance);
  var valProps = from PropertyInfo property in properties 
                 where property.GetCustomAttributes(
                    typeof(ValidationAttribute), true).Length > 0 
                 select new
                 { 
                   Property = property, 
                   ValidationAttibutes = property.GetCustomAttributes(
                     typeof(ValidationAttribute), true) 
                 }; 

  foreach (var item in valProps) 
  { 
    foreach (ValidationAttribute attribute in item.ValidationAttibutes) 
    { 
      if (!attribute.IsValid(item.Property.GetValue(this, null))) 
      { 
        string message = string.Empty;
        if (attribute.Key != string.Empty)
        { 
          message = GetValidationMessage(attribute.Key); 
        } 
        else
        { 
          message = attribute.Message; 
        } 
        this.BrokenRules.Add(new BrokenRule(item.Property.Name, message ));
      } 
    } 
  } 
  return (this.BrokenRules.Count == 0);
}

(Note: I’ll discuss the parameterless overload and the use of the clearBrokenRules parameter in part 3 of this article series.)

First, an array of all the public instance properties is retrieved using the GetProperties method of the Type class. A LINQ query then gets all the properties that have at least one ValidationAttribute (a custom Attribute defined in the Spaanjaars.Validation namespace) applied. The query returns an anonymous type with two properties: the property itself and an object array of the validation attributes.

The foreach code block then loops through each of the attributes and calls its IsValid method and passes that method the value of the property. If the underlying is not valid according to the IsValid method, a new BrokenRule instance is added the BrokenRules collection. If the attribute’s Key property is set, GetValidationMessage is called to translate the key into an error message. If the Key is not set, the Message property of the attribute is used. The GetValidation method is useful if you want to localize your application or if you want to store all your validation messages in a central location. Localization is discussed later in this article.

To see how this works, take another look at the User class:

	public class User : ValidationBase
{ 
  [NotNullOrEmpty(Message = "Please enter a user name.")] 
  public string UserName { get; set; } 
}

When Validate is called on an instance of User, the collection of properties will contain only one item: the UserName property. The collection of validation attributes on this property will be one as well: the NotNullOrEmpty validation attribute. This attribute returns false from its IsValid method when the property’s underlying value is null or an empty string. It also adds a BrokenRule with the error message “Please enter a user name.” to the BrokenRules collection.

To understand how the Validate method is able to determine the validity of the properties, you need to look at the ValidationAttribute base class.

The ValidationAttribute base class

The ValidationAttribute class is an abstract class with one method and two properties, depicted in Figure 17:

The ValidationAttribute Class
Figure 17 – The ValidationAttribute Class, the Base Class for all Custom Validation Attributes

The code for the class looks like this:

	public abstract class ValidationAttribute : System.Attribute
{
  private string _key; 
  private string _message; 
  
  public abstract bool IsValid(object item); 
  
  public string Message 
  { 
    get { return _message; } 
    set
    { 
      if (!string.IsNullOrEmpty(_key))
      { 
        throw new ArgumentException(
            "Can't set Message when Key has already been set."); 
      } 
      _message = value;
    } 
  } 

  public string Key 
  { 
    get { return _key; } 
    set
    { 
      if (!string.IsNullOrEmpty(_message))
      { 
        throw new ArgumentException(
            "Can't set Key when Message has already been set."); 
      } 
      _key = value;
    } 
  } 
}

The Message and Key properties are mutually exclusive and are used to determine the actual validation message or a key defined in a resource file that holds the (localized) validation message. Classes that inherit from ValidationBase need to set either the Key or the Message property using named parameters (or through a constructor) when applying the relevant attributes to the properties.

Since IsValid is defined as abstract, it has no behavior of its own in the ValidationAttribute base class. However, custom attributes inheriting from ValidationAttribute are required to implement the method as you’ll see next.

Creating Custom Validation attributes

The ValidationAttribute is an abstract class and as such cannot be instantiated or used as an attribute on a property in a business entity. It serves as a base class for your own validation attributes that do provide specific validation behavior. Consider for example the NotNullOrEmpty attribute. This attribute can be used to check whether the property that the attribute is attached to contains a value that is not null or empty. The code for this attribute looks like this:

	[AttributeUsage(AttributeTargets.Property)]
public sealed class NotNullOrEmptyAttribute : ValidationAttribute
{ 
  public override bool IsValid(object item) 
  { 
    if (item is string) 
    { 
      return !string.IsNullOrEmpty(item as string);
    } 
    return item != null; 
  } 
}

The first line with the AttributeUsage attribute tells the compiler to make sure the NotNullOrEmpty attribute can only be applied to properties. You’ll get a compile error if you try to apply it to another class member like a method.

The only method in this class is an override of IsValid. As a parameter it accepts an object that contains the underlying value of the property being validated. In the case of the earlier example with the User class, this is the value of the UserName property (null, an empty string or a valid value). Inside the method you can write whatever behavior you need for validation. In the case of the NotNullOrEmpty attribute, the code checks to see if the item is a string and then delegates responsibility of the check to the IsNullOrEmpty methods. If the item is not a string, it’s simply compared with null.

The cool thing of this framework is that you can pretty much validate anything you want (as long as the rules are related to a single property). For example, a ValidEmailAddress attribute that validates whether a property’s value represents an e-mail address could look like this:

	[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidEmailAttribute : ValidationAttribute
{ 
  public override bool IsValid (object item) 
  { 
    string tempValue = item as string; 
    if (string.IsNullOrEmpty(tempValue))
    { 
      return true; 
    } 
    return (tempValue).Contains("@"); 
  } 
}

Obviously, this is a simplified example as the code only checks whether the value contains an @ symbol, but I am sure you get the idea. You could easliy enhance this method by using a strong regular expression that checks the true validity of an e-mail address (syntactically, not whether it exists or not). Notice how this method returns true when the item contains null or an empty string. While that certainly doesn’t represent a valid value, I decided to implement it like this because it allows me to make fields optional. So, the e-mail address can be empty, but *if* it’s filled in, it has to be valid. To define a required e-mail address, you simply add a NotNullOrEmpty attribute to the property:

	[NotNullOrEmpty(Message = "Please enter an e-mail address.")] 
[ValidEmail(Message = "This is not a valid e-mail address.")] 
public string Email { get; set; } 

When the Email property is left empty, the first attribute will flag a business rule violation. If a value is entered that doesn’t look like an e-mail address, the second attribute flags an error. Only when the Email property contains a valid e-mail address is the property considered to be valid. If you don't want this behavior and want a single validation attribute instead, you can change the ValidEmail attribute and give it a Required property that you can set to true or false as a named parameter when you apply the attribute.

You’re not limited to simple string comparisons. By adding public properties to your validation attributes you can for example create range validators. The following class shows how to implement a simple ValidRange validator that ensures the property’s value is between the Min and Max values:

	[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidRange : ValidationAttribute
{ 
  public double Min { get; set; } 
  public double Max { get; set; } 
  
  public override bool IsValid(object item) 
  { 
    double tempValue = Convert.ToDouble(item);
    return tempValue >= Min && tempValue <= Max; 
  } 
} 

Because attributes on members support named parameters there’s no need to create special overloads of the ValidRange’s constructor; you simply specify the necessary values by prefixing them with the property’s name. The following example shows you how to make sure a property called Priority has a value that falls between 0 and 10 (inclusive):

	[ValidRange(Message=@"This is not a valid priority. 
           Valid values are between 0 and 10", Min=0, Max=10)] 
public int Priority { get; set; }

Notice how the Min and Max values are set using the PropertyName=Value syntax. This makes it easy to assign one or more properties of the class without the need to create specialized overloads.

From here, you can pretty much create any attribute you like. The following possible attributes come to mind:

  • RegularExpressionAttribute – validates a value given a regular expression.
  • DateTimeNotEmpty – Ensures that a DateTime instance does not equal DateTime.MinValue or DateTime.MaxValue.
  • ValidSsn – Validates a social security number.
  • ValidCreditCardNumber – this class could validate a credit card number.
  • MaxLengthAttribute – Ensures that a string is not longer than a given maximum length.
  • MinLengthAttribute – Ensures that a string is not shorter than a given minimum length.
  • ExactLengthAttribute - Ensures that a string has a specific length.

Many of these attributes could inherit from the RegularExpression attribute which would make implementing them as simple as overriding the regular expression property of the class. With the base classes in the Validation Framework you have a very flexible set of tools. Adding new validation attributes or tweaking the behavior of others by overriding a few methods or properties is now very easy, while you still maintain the same usage experience: apply the attribute and set a few named arguments.

When I discussed the ValidationBase and its Validate method to validate the properties through their attributes, I also mentioned the BrokenRule and BrokenRules classes. You’ll see how they fit in in the next section.

The BrokenRule and BrokenRules Classes

The BrokenRule and BrokenRules classes are used to communicate the broken validation rules back to the calling code. The BrokenRule class is a simple data container that has two properties: Message and PropertyName. Both are visible in Figure 18.

The BrokenRule Class
Figure 18 – The BrokenRule Class

The Message property always contains the expanded error message associated with the broken rule, whether the message was applied to the property directly, or retrieved from a localization resource. The PropertyName contains the name of the property that broke the rules. For example, this property could contain a value of Email when you assigned an invalid value to an Email property. The PropertyName is used to get a collection of all broken rules for a specific property which in turn is useful for unit testing. This is explained later when I discuss the FindByPropertyName method of the BrokenRules collection.

As you saw earlier, each class that inherits from ValidationBase has a BrokenRules property that you can check for the rules that are broken by your business object.
The BrokenRules collection inherits from Collection<BrokenRule> and has two constructors and a useful method to find specific broken rules by the name of the property that triggered the rule.

The BrokenRulesCollection Class
Figure 19 – The BrokenRulesCollection Class

The two constructors of this class allow you to create a brand new collection and one based on an existing IList<BrokenRule>. This overload is used in the FindByPropertyName that filters the inner list and returns all the broken rules that belong to a certain property:

	public BrokenRulesCollection FindByPropertyName(string propertyName) 
{ 
  return new BrokenRulesCollection(
               (from rule in this
                where rule.PropertyName.ToUpperInvariant() ==
                       propertyName.ToUpperInvariant() 
                select rule).ToList<BrokenRule>());
}

This simple LINQ query selects all the BrokenRule instances with a matching (case insensitive) property name. The LINQ query result is converted to an IList which is then fed into the constructor of the BrokenRules collection. This method, and the FindByMessage method are mostly useful for Unit Tests that need to check the validity of a ValidationBase instance and check for specific properties, But you can also use it to get the relevant broken rules in the UI and display them next to their respective UI controls.

To see how all of this works together, the next section introduces a simple Person class with a few properties. The Person class inherits ValidationBase and implements the Validation Framework. A number of Unit Tests and screen shots from the debugger will then show you how everything fits together.

Putting it all together: a very simple Person class and a Bunch of Unit Tests

To test out framework code like the validation code I’ve shown you so far, it’s always a good idea to write unit tests to validate your assumptions and code implementation. As of Visual Studio 2008, the Unit Testing framework is now included in the Professional edition and up (it used to be in the more expensive Team Editions only), so integrated unit testing is now available to more developers than ever. I am a big fan of unit testing, so I use it in many of the projects I code or design.

In order to test some of the validation principles found in the Validation Framework with Unit Tests, I created a simple Person class with the following properties:

	public class Person
{ 
  public string FirstName { get; set; } 
  public string LastName { get; set; } 
  public string EmailAddress { get; set; } 
  public int Age { get; set; } 
}       

As you can see, this is a very simple, yet typical class with a few auto-implemented properties. It looks quite a bit like the ContactPerson class introduced in my previous article series.

To ensure you only store valid people in your data store, let’s say you want to enforce the following business rules on this Person class :

  1. The first name is required
  2. The last name is required
  3. The email address is required and must contain a value that represents an e-mail address.
  4. The age should be between 0 and 150. (Notice that normally a DateTime for the date of birth would be a better option for the Age property).

To change the Person class to implement this validation behavior, you need to carry out two steps:

  1. Make Person inherit from ValidationBase
  2. Implement the necessary attributes to enforce the validation rules.

The Person class could end up like this:

	public class Person : ValidationBase
{ 
  [NotNullOrEmpty(Message="First name is required.")] 
  public string FirstName { get; set; } 

  [NotNullOrEmpty(Message = "Last name is required.")] 
  public string LastName { get; set; } 

  [NotNullOrEmpty(Message = "Email address is required.")] 
  [ValidEmail(Message="A valid e-mail address is required.")] 
  public string EmailAddress { get; set; } 

  [ValidRange(Message="Age must be between 0 and 150.", Min=0, Max=150)] 
  public int Age { get; set; } 
}

In order to test this class and see if the right validation rules are applied, you can write a number of Unit Tests. For example, you could write a simple test that asserts that a brand new Person without any properties set on it is invalid:

	[TestMethod] 
public void NewPersonIsNotValid() 
{ 
  Person myPerson = new Person(); 
  Assert.IsFalse(myPerson.Validate());
}

If you debug this code and put a watch on the Person instance, you can see how the BrokenRules collection is filled with a broken rule for each invalid attribute:

Watching the BrokenRules Property in the Watch Window
Figure 20 – Watching the BrokenRules Property in the Watch Window

In Figure 20 you can see four broken rules; one for each property in the Person class. The ToString method on the BrokenRule class is responsible for prefixing the validation message with the property that caused the rule to be broken. When presenting the broken rules in the UI you typically use the Message property instead that doesn’t contain the property name.

Obviously, in a real world application you could use the BrokenRules collection for something more useful, like binding it to a data bound control, returning it from a Web Service call, or logging it somewhere.

You could (and should) write other tests to validate individual properties; often you want to write a separate test for all possible edge cases. For example, when testing a property with the NotNullOrEmpty attribute applied, you want to test for null, string.Emtpy and a valid value like this:

	[TestMethod] 
public void NullIsNotAValidFirstName() 
{ 
  Person myPerson = new Person { FirstName = null }; 
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName(
                  "FirstName").Count > 0);
} 

[TestMethod] 
public void EmptyIsNotAValidFirstName() 
{ 
  Person myPerson = new Person { FirstName = string.Empty };
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName(
                   "FirstName").Count > 0);
} 

[TestMethod] 
public void ImarIsAValidFirstName() 
{ 
  Person myPerson = new Person { FirstName = "Imar" }; 
  myPerson.Validate();
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName(
                   "FirstName").Count == 0);
} 

Notice that with invalid cases I can be sure that Validate must return false, so I can assert myPerson.Validate(). With a valid example, I cannot be sure the entire instance is valid because other properties could be broken as well. In that case, I am not using Assert.IsTrue(myPerson.Validate()) but simply myPerson.Validate() instead. Also note that in tests where I expect the validation to fail I check the Count property of the filtered broken rule collection to be at least 1.

You could write similar tests for other properties, like the Age attribute. For example, with a ValidRange attribute as applied on the Age property, you likely want to ensure that the lower and upper bounds are valid, and values outside the range aren’t. The following code snippet shows the implementation of a number of tests for the Age property:

	[TestMethod] 
public void AgeCannotBeMinusOne() 
{ 
  Person myPerson = new Person { Age = -1 }; 
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count > 0);
} 

[TestMethod] 
public void AgeCannotBeLargeNegative() 
{ 
  Person myPerson = new Person { Age = int.MinValue };
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count > 0);
} 

[TestMethod] 
public void AgeCannotMoreThan150() 
{ 
  Person myPerson = new Person { Age = 151 }; 
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count > 0);
} 

[TestMethod] 
public void AgeCannotBeLargePositive() 
{ 
  Person myPerson = new Person { Age = int.MaxValue };
  Assert.IsFalse(myPerson.Validate());
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count > 0);
} 

[TestMethod] 
public void AgeCanBeLowerbound() 
{ 
  Person myPerson = new Person { Age = 0 }; 
  myPerson.Validate();
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count == 0);
} 

[TestMethod] 
public void AgeCanBeUpperbound() 
{ 
  Person myPerson = new Person { Age = 150 }; 
  myPerson.Validate();
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count == 0);
} 

[TestMethod] 
public void AgeCanBeBetweenLowerAndUpperbounds() 
{ 
  Person myPerson = new Person { Age = 130 }; 
  myPerson.Validate();
  Assert.IsTrue(myPerson.BrokenRules.FindByPropertyName("Age").Count == 0);
}

By having this set of tests, you can be sure you’re notified of problems if someone changes the rules. For example if the lower bound of the Age validation attribute is changed from 0 to 1, the AgeCanBeLowerbound test will fail. Likewise, other tests will fail as well as other settings are changed.

Implementing the Validation Framework the way I have shown you here gives you a lot of flexibility, without requiring a lot of hand written code. However, it may seem to you that the Validation Framework only works on single instance properties. That is, you can only validate a single property’s value at a time, without the ability to relate it to the value of another. Fortunately, validating related properties is easy as well, as you’ll learn in the next article in this series. Stay tuned!

Summary

Validation is a very important concept in most day to day business applications. You can implement validation in different layers, including the UI, the Business Layer and the database. By implementing validation in the UI, you get responsive applications, but as a down side you’ll need to rewrite rules every time you present data in the UI. Database validation works at the lowest possible level: right before the data gets stored in the database. Although this ensures a great integrity of your data, it isn’t always the most practical solution as you’ll need to deal with low-level exceptions which are difficult to transform and translate into the user interface.

By implementing validation in the Business Layer with the Validation Framework I’ve shown you in this article, you can circumvent the problems typically associated with UI and database validation and get the following benefits:

  1. Your validation rules are easy to write and implement.
  2. It’s easy to reuse your validation rules across your business entities.
  3. You can ensure that the rules are applied regardless of the user interface used.
  4. You can localize your validation messages so they are easy to understand for end users.
  5. You have a centralized validation mechanism, making it easy to get a full list of broken rules or invalid properties from a single location.

In the next part in this article series I’ll show you some advanced validation topics. You’ll see how to write your own validation code in your business entities to enforce complex business rules and you’ll learn how to localize the entire Validation Framework so it can be used in multi-lingual web sites.

Downloads

 


 


Where to Next?

Wonder where to go next? You can post a comment on this article.

Doc ID 477
Full URL https://imar.spaanjaars.com/477/n-layered-web-applications-with-aspnet-35-part-2-introducing-the-validation-framework
Short cut https://imar.spaanjaars.com/477/
Written by Imar Spaanjaars
Date Posted 12/14/2008 20:28

Comments

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the Doc ID of the document.

(Plain text only; no HTML or code that looks like HTML or XML. In other words, don't use < and >. Also no links allowed.