Data Annotations Property Validator for ASP.NET
Entity Framework, LINQ to SQL and ASP.NET MVC support Microsoft's new validation API, which you can find on System.ComponentModel.DataAnnotations. It basically consists on attribute classes, inherited from System.ComponentModel.DataAnnotations.ValidationAttribute. It is very easy to define your own, although Enterprise Library, xVal and Spring.NET validations are considerably power powerfull.
I wanted to have a custom ASP.NET validator that would validate the value of a form control against the validation attributes defined for a given property of a class, in a way similar to the Web Client Software Factory's PropertyProxyValidator, like you can read in this post. This is what I came up with: class DataAnnotationsValidator!
Here is the code:
public
class DataAnnotationsValidator : BaseValidator{
protected override void OnInit(EventArgs e)
{
if ((this.Enabled == true) && (this.Visible == true))
{
this.Page.RegisterRequiresControlState(this);
}
base.OnInit(e);
}
protected override void LoadControlState(Object savedState)
{
Object [] state = savedState as Object [];
base.LoadControlState(state [ 0 ]);
this.DisplayMode = (ValidationSummaryDisplayMode) state [ 1 ];
this.PropertyName = (String) state [ 2 ];
this.SourceTypeName = (String) state [ 3 ];
}
protected override Object SaveControlState()
{
Object [] state = new Object [ 4 ];
state [ 0 ] = base.SaveControlState();
state [ 1 ] = this.DisplayMode;
state [ 2 ] = this.PropertyName;
state [ 3 ] = this.SourceTypeName;
return (state);
}
protected override Boolean EvaluateIsValid()
{
if ((String.IsNullOrEmpty(this.SourceTypeName) == true) || (String.IsNullOrEmpty(this.PropertyName) == true))
{
return (true);
}
Type type = Type.GetType(this.SourceTypeName, false);
if (type != null)
{
PropertyInfo prop = type.GetProperty(this.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
if (prop != null)
{
ValidationAttribute [] attrs = prop.GetCustomAttributes(typeof(ValidationAttribute), true) as ValidationAttribute [];
List<ValidationException> errors = new List<ValidationException>();
String value = this.GetControlValidationValue(this.ControlToValidate);
if (attrs.Length == 0)
{
MetadataTypeAttribute [] metadata = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true) as MetadataTypeAttribute [];
if (metadata.Length != 0)
{
prop = metadata[0].MetadataClassType.GetProperty(this.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
if (prop != null)
{
attrs = prop.GetCustomAttributes(typeof(ValidationAttribute), true) as ValidationAttribute [];
}
}
}
for (Int32 i = 0; i < attrs.Length; ++i)
{
try
{
attrs [ i ].Validate(value, prop.Name);
}
catch (ValidationException ve)
{
errors.Add(ve);
}
catch
{
}
}
this.ErrorMessage = this.formatErrorMessage(errors);
return (errors.Count == 0);
}
}
this.ErrorMessage = String.Empty;
return (true);
}
private String formatErrorMessage(IList<ValidationException> results)
{
String str = String.Empty;
String str2 = String.Empty;
String str3 = String.Empty;
String str4 = String.Empty;
StringBuilder builder = new StringBuilder();
switch (this.DisplayMode)
{
case ValidationSummaryDisplayMode.List:
str3 = "<br/>";
break;
case ValidationSummaryDisplayMode.SingleParagraph:
str3 = " ";
str4 = "<br/>";
break;
default:
str = "<ul>";
str2 = "<li>";
str3 = "</li>";
str4 = "</ul>";
break;
}
if (results.Count != 0)
{
builder.Append(str);
foreach (ValidationException result in results)
{
builder.Append(str2);
builder.Append(result.Message);
builder.Append(str3);
}
builder.Append(str4);
}
return (builder.ToString());
}
[Browsable(true)]
[DefaultValue(ValidationSummaryDisplayMode.List)]
public ValidationSummaryDisplayMode DisplayMode
{
get;
set;
}
[Browsable(true)]
[DefaultValue("")]
public String PropertyName
{
get;
set;
}
[Browsable(true)]
[DefaultValue("")]
public String SourceTypeName
{
get;
set;
}
}
As you can see, it is smart enough to try to find the validation attributes on a metadata class defined with a MetadataTypeAttribute attribute, like you would use with generated code, such as Entity Framework's. It also supports all of ValidationSummaryDisplayMode's options.
This is how you would use it:
<asp:TextBox runat="server" ID="text" />
<blog:DataAnnotationsValidator runat="server" ControlToValidate="text" SourceTypeName="SomeClass, SomeAssembly" PropertyName="SomeProperty" />
<asp:Button runat="server" Text="Validate" OnClick="OnValidate" />
Suppose the class you want to validate looks like this:
public
class SomeClass{
[Required]
[RegularExpression(@"\d\d\d")]
public String SomeProperty
{
get;
set;
}
}
And that's it! Happy validation!