Click here to Skip to main content
15,867,704 members
Articles / Web Development / ASP.NET

Using .NET 3.5 DataAnnotations for ASP.NET input validation

Rate me:
Please Sign up or sign in to vote.
3.73/5 (6 votes)
7 Oct 2009CPOL5 min read 51K   540   12   6
Using .NET 3.5 DataAnnotations for ASP.NET input validation.

Introduction

This is a very basic introduction on using the new functionality released with .NET 3.5.1 SP1 which introduces a new DLL, System.ComponentModel.DataAnnotations. This new DLL allows you to add attributes to your object properties to enforce validation instead of the normal ASP.NET validators.

Background

The main reason I started looking into the System.ComponentModel.DataAnnotations namespace was so I could clean up my ASPX pages. While the new attributes do not have (at least, not from what I have found) client side validation, it does help tremendously in keeping your validation logic in one place. Your website might be using the same object on many pages, and if the field is editable, you have to "cut/paste" the validation logic from page to page. Using the new attributes, you can keep most of your validation logic with the property itself, and the developer only needs to add one custom validator.

Using the Code

I like easy to read code, so hopefully I have presented some here. This is a standard VS 2008 file system website. Unzip, add a reference to System.ComponentModel.DataAnnotations.dll, and run.

Notice that on the Default.aspx page, I have several controls, with the last one (End Date) using normal validation controls.

Let's start with the most important piece of this project, the custom validation class. I created a standard class called "CustomValidator" and it inherits from BaseValidator. There is one method that must be overridden, EvaluateIsValid, and I have added two properties to hold the property name and the object type that is to be used when validating.

C#
public class CustomValidator : BaseValidator
{
    protected override bool EvaluateIsValid()
    {
    }

    ///<summary>
    /// Name of property in our object that has the DataAnnotation 
    /// attributes to use for validation
    ///</summary>
    public string PropertyName { get; set; }

    /// <summary>
    /// Object type we need to get so we can use reflection to find the property
    /// </summary>
    public string ObjectType { get; set; }

}

Shown above is the basic class definition, and we we implement the validation logic next. We need to add some basic logic to the EvaluateIsValid method to make sure the user gave us the minimal information needed.

C#
protected override bool EvaluateIsValid()
{
     // Make sure user correctly added all the needed fields in the aspx file
     if (string.IsNullOrEmpty(PropertyName))
         throw new NullReferenceException("Property Name was not set.");

     if (string.IsNullOrEmpty(ObjectType))
         throw new NullReferenceException("Object Type was not set.");

}

Nothing special about this, just making sure the user gave us the property name and the name of the object that holds the property. This is set in the .aspx page, and we will get to that piece in a bit. Next, we need to find the control on the web page that we need to validate. At this point, we are only validating textboxes (although I also did put some logic in for the DropDownList).

C#
// Find the control to validate so we can get the value to validate
Control Ctrl = FindControl(ControlToValidate);
string value;

if (Ctrl is TextBox)
{
    value = ((TextBox)Ctrl).Text;
}
else if (Ctrl is DropDownList)
{
    value = ((DropDownList)Ctrl).SelectedValue;
}
else
{
    // At this point we are only using this for Textbox and DropDown
    return false;
}

Again nothing special, just find the control and get the value to be validated. This uses the standard "FindControl" method all ASP.NET developers should be used to.

Now onto the fun stuff. Now that we have made sure the user gave us the object/property name, and we have a value we need to get the object.

C#
// Find the object type that has our property
Type t = Type.GetType(ObjectType);

if (t == null)
    throw new NullReferenceException("Failed to find type.");

// Get the property we need to find the attributes for validation
PropertyInfo prop = t.GetProperty(PropertyName);

if(prop == null)
   throw new NullReferenceException("Failed to find property.");

If you have not used Reflection before, the above code is probably cryptic. It's fairly simple. Type.GetType() gets the type of our class; in our case, it's a Member class that I created. t.GetProperty() gets the property name; in this case, the property we specify in our .aspx page.

These next couple of lines can be left out if you don't use the DisplayNameAttribute, but they are handy if your property name is something like "MemberBillingZipCode" and you really would prefer to display "Zip Code" to the user instead.

C#
string FriendlyName;

// This maps back to the DisplayName() attribute on the property
DisplayNameAttribute display = prop.GetCustomAttributes(
  typeof(DisplayNameAttribute), true).OfType<DisplayNameAttribute>().FirstOrDefault();

// If the developer added a DisplayName attribute then use it instead of the
// standard property name
if (display != null)
    FriendlyName = display.DisplayName;
else
    FriendlyName = prop.Name;

The DisplayNameAttribute is one we can use on our properties to indicate we want to display a more friendly name to our users. If the developer adds that attribute, we use it; if not, we use the name of the property.

Now onto the real validation:

C#
// Find only the ValidationAttribute for this property. If you leave off the
// "typeof(ValidationAttribute)" you would get all custom attributes on this property.
// Also note the "as ValidationAttribute[]"
ValidationAttribute[] attribs = prop.GetCustomAttributes(
     typeof(ValidationAttribute), true) as ValidationAttribute[];

// Iterate over attributes and evaluate each DataAnnotation.
// Note I stop once I find the first failure. You can change this to build
// a list of all failures in validation for a property.
for (int i = 0; i < attribs.Length; i++)
{
     if (!attribs[i].IsValid(value))
     {
       // You can use the ErrorMessage param of the Required, StringLength etc
       // attribute if you have a very specific error message you want shown
       // but take a look at not adding it and letting the built in function
       // FormatErrorMessage do it.
       ErrorMessage = attribs[i].FormatErrorMessage(FriendlyName);
       ToolTip = ErrorMessage;
       return false;
     }
}

This is the real meat of the logic. We have our prop variable which is our property, so we can inspect it and get all the ValidationAttributes for it and iterate over them. Each attribute has its own "IsValid" method which we pass our value to, and if the validation fails, we let the framework build an error message for us, unless we have specified one.

So now, we need an object to validate against. I created a "Member" class as shown below:

C#
namespace DA.Examples
{
   public class Member
   {
      [Required(), StringLength(20), DisplayName("First Name")]
      public string FirstName { get; set; }

      [Required(), StringLength(20), DisplayName("Last Name")]
      public string LastName { get; set; }

      /// Notice this one has a "ErrorMessage" param
      [Required(), Range(18, 99, ErrorMessage = 
         "We don't want you"), DisplayName("Age")]
      public int Age { get; set; }

      [Required(), DisplayName("Join Date"), 
        RegularExpression(@"\d{2}/\d{2}/\d{4}")]
      public DateTime JoinDate { get; set; }
   }
}

This is a very basic class with nothing too special besides our new attributes. Make sure you add usings for System.ComponentModel and System.ComponentModel.DataAnnotations. Also, namespaces make this work easier. Look at the FirstName and LastName properties, they each have the Required, StringLength, and DisplayName attributes. DisplayName("") allows you to choose a more friendly name to the user. Required() makes the field required (easy huh), and StringLength does exactly what you think it does.

Now onto our web page. Create a standard web page and add our fields. We need to add a reference to our CustomValidator so we can use it like any other control, so we add a Register:

ASP.NET
<%@ Register TagPrefix="CVal" Namespace="CustomValidations" %>

Now add a textbox and our new validator control:

ASP.NET
<asp:TextBox ID="txtFirstName" runat="server" />
<CVal:CustomValidator ID="valFirstName" runat="server" 
      ControlToValidate="txtFirstName" 
      Text="*" Display="Dynamic" PropertyName="FirstName" 
      ObjectType="DA.Examples.Member" />

The textbox is nothing special, so let's look at our control. We use it like any other import control by giving its TagPrefix. Give it an ID, runat="server", ControlToValidate, Text, Display - these are standard to any ASP.NET validation control you have already used. The two new properties are "PropertyName" and "ObjectType". If you look at the CustomValidator we created. These are the two public properties at the bottom of the class. We just need to map these values to the object/property we want to use for validation. After hooking up this, when the page is postback and causesvalidation is true, this is fired and validates the user input for us.

The code above enforces the user to not only enter a value for the first name, but it also makes sure the user does not enter a value longer that 20 characters long. To do this with the current validation controls, you would do:

ASP.NET
<asp:TextBox ID="txtFirstName" runat="server" />
<!-- This is the normal way to add validators -->
<!-- Great thing about this is the client side validation -->
<asp:RequiredFieldValidator ID="reqFirstName" ControlToValidate="txtFirstName" 
       Text="*" Display="Dynamic" InitialValue="" 
       ErrorMessage="First Name is required" runat="server" 
       ToolTip="First Name is required" />
<asp:RangeValidator ID="rngFirstName" ControlToValidate="txtFirstName" 
       runat="server" Text="*" 
       ErrorMessage="First name can not be over 20 characters." 
       ToolTip="First name can not be over 20 characters" 
       MinimumValue="0" 
       MaximumValue="20" Type="String" />

Now, the great thing about using standard validation controls is the client side validation. The bad thing is having to use multiple validation controls for one input field.

Points of Interest

Love the new functionality and clean up your ASPX page.

License

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


Written By
Software Developer (Senior) National Diagnostics
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralOK article Pin
Donsw27-Jun-10 17:57
Donsw27-Jun-10 17:57 
GeneralLooks like JSR303 in the Java world... Pin
ptmcomp13-Oct-09 8:45
ptmcomp13-Oct-09 8:45 
GeneralRe: Looks like JSR303 in the Java world... Pin
Sean Rhone13-Oct-09 13:34
Sean Rhone13-Oct-09 13:34 
GeneralValidation value Pin
Richard Deeming13-Oct-09 8:12
mveRichard Deeming13-Oct-09 8:12 
GeneralMy vote of 2 Pin
MR_SAM_PIPER12-Oct-09 13:32
MR_SAM_PIPER12-Oct-09 13:32 
GeneralRe: My vote of 2 Pin
Sean Rhone13-Oct-09 13:37
Sean Rhone13-Oct-09 13:37 

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.