Introduction
In this article I’m going to describe an implementation of ASP.NET validator that uses the new System.ComponentModel.DataAnnotations assembly that came in .NET 3.5 SP1.
The Validator
In order to create the validator, you need to inherit from the BaseValidator
abstract class and implement its EvaluateIsValid
method. I’ve created an ASP.NET Server Control project and added a control class that does the exact thing. You need to reference the System.ComponentModel.DataAnnotations assembly in order for it to work. Here is how it’s implemented:
namespace Validators
{
[ToolboxData("<{0}:DataAnnotationValidator
runat="server"></{0}:DataAnnotationValidator>")]
public class DataAnnotationValidator : BaseValidator
{
#region Properties
public string SourceTypeName { get; set; }
public string PropertyName { get; set; }
#endregion
#region Methods
protected override bool EvaluateIsValid()
{
Type source = GetValidatedType();
PropertyInfo property = GetValidatedProperty(source);
string value = GetControlValidationValue(ControlToValidate);
foreach (var attribute in property.GetCustomAttributes(
typeof(ValidationAttribute), true)
.OfType<ValidationAttribute>())
{
if (!attribute.IsValid(value))
{
ErrorMessage = attribute.ErrorMessage;
return false;
}
}
return true;
}
private Type GetValidatedType()
{
if (string.IsNullOrEmpty(SourceTypeName))
{
throw new InvalidOperationException(
"Null SourceTypeName can't be validated");
}
Type validatedType = Type.GetType(SourceTypeName);
if (validatedType == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Invalid SourceTypeName", SourceTypeName));
}
return validatedType;
}
private PropertyInfo GetValidatedProperty(Type source)
{
PropertyInfo property = source.GetProperty(PropertyName,
BindingFlags.Public | BindingFlags.Instance);
if (property == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Validated Property Does Not Exists", PropertyName));
}
return property;
}
#endregion
}
}
As you can see, I use two main properties in the validator which help me to get the relevant source type name and the property to validate.
Testing the Validator
In order to test the validator, I have created a Web Application project and put inside of it a simple Person
class which uses Data Annotations for validation:
namespace ASPNETDataAnnotation
{
public class Person
{
[Required(ErrorMessage="ID is requiered")]
public string ID { get; set; }
[StringLength(20, ErrorMessage = "First name is too long!")]
public string FirstName { get; set; }
[StringLength(20, ErrorMessage="Last name is too long!")]
public string LastName { get; set; }
[RegularExpression(
@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+*",
ErrorMessage="Must be a valid e-mail address")]
public string Email { get; set; }
}
}
Also, I’ve created a web form that uses the validator:
lt;%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="WebForm1.aspx.cs" Inherits="ASPNETDataAnnotation.WebForm1" %>
<%@ Register Assembly="Validators" Namespace="Validators" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<div>
<asp:Label ID="lblID" Text="ID: " runat="server" />
<asp:TextBox ID="txtID" runat="server" />
<cc1:DataAnnotationValidator ID="valID"
runat="server" ControlToValidate="txtID"
PropertyName="ID"
SourceTypeName="ASPNETDataAnnotation.Person, ASPNETDataAnnotation" />
</div>
<div>
<asp:Label ID="lblFirstName" Text="First Name: " runat="server" />
<asp:TextBox ID="txtFirstName" runat="server" />
<cc1:DataAnnotationValidator ID="valFirstName"
runat="server" ControlToValidate="txtFirstName"
PropertyName="FirstName"
SourceTypeName="ASPNETDataAnnotation.Person, ASPNETDataAnnotation" />
</div>
<div>
<asp:Label ID="lblLastName" Text="Last Name: " runat="server" />
<asp:TextBox ID="txtLastName" runat="server" />
<cc1:DataAnnotationValidator ID="valLastName"
runat="server" ControlToValidate="txtLastName"
PropertyName="LastName"
SourceTypeName="ASPNETDataAnnotation.Person, ASPNETDataAnnotation" />
</div>
<div>
<asp:Label ID="lblEmail" Text="E-Mail: " runat="server" />
<asp:TextBox ID="txtEmail" runat="server" />
<cc1:DataAnnotationValidator ID="valEmail"
runat="server" ControlToValidate="txtEmail"
PropertyName="Email"
SourceTypeName="ASPNETDataAnnotation.Person, ASPNETDataAnnotation" />
</div>
<div>
<asp:Button ID="btnValidate" Text="Validate" runat="server" />
</div>
</div>
</form>
</body>
</html>
Running the web form and clicking on the button while we have bad data will generate a validation error as expected:
Summary
In the post, I showed one way to create an ASP.NET data annotation validator. In other frameworks like ASP.NET MVC or WCF RIA Services you have such validators implemented.