Validando ASP.NET Web Form con DataAnnotation en Ambos lado(Cliente y Servidor).

DataAnnotation es el namespace que desde el SP1 de .NET Framework 3.5 ha sido incluido para hacer validaciones a los campos definidos en un modelo de datos (Entity Framework por Ejemplo), la funcionalidad se muestra mucho en ASP.NET MVC y se utiliza definiendo atributos de validacion a un objeto; atributos que son aprovechados en ASP.NET MVC para hacer validaciones automaticas. En estos dias estoy involucrado en un proyecto de Webform que contiene un monton de clases y objetos, es por eso que he querido utilizar esta funcionalidad para reutilizar codigo disminuyendo considerablemente la cantidad de validadores que conlleva cada formulario web.

Si se busca en internet vemos que en la version actual de ASP.NET MVC (version 2) se logra combinar DataAnnotation con JQuery Validators para hacer validaciones tanto del lado del servidor como del lado del cliente con el minimo esfuerzo de programacion, pero si esa funcionalidad se quiere incorporar en ASP.NET Webform, se hace cuesta arriba buscar alternativas para hacer un trabajo similar.  Es por eso que he decidio compartir este codigo implementado luego de algunas horas de investigacion por parte de mis compañeros y yo involucrados en el proyecto.

 

La finalidad es la siguiente:

  • Hacer uso de DataAnnotation para centralizar las validaciones de los objetos.
  • Simplificar y Retulizar el codigo utilizado para validacion.
  • Tener Validacion del lado del servidor, asi como tambien del lado del cliente con el minimo esfuerzo.

 

Incluyendo el namespace System.ComponentModel.DataAnnotations en nuestra clase de personas podemos hacer lo siguiente:

public class Person
{

//Public Properties
[Display(Name = "Nombre")]
[Required(ErrorMessage = "El nombre es requerido.")]
[StringLength(35)]
public string Name { get; set; }

[Display(Name = "Apellido")]
[Required(ErrorMessage = "El apellido es requerido.")]
[StringLength(35)]
public string LastName { get; set; }

[Display(Name = "Edad")]
[Required(ErrorMessage = "La edad es requerida.")]
[Range(typeof(Int32), "18", "65",
ErrorMessage = "La edad debe ser entre 18 y 65 años.")]
public int Age { get; set; }

[Display(Name = "Email")]
[RegularExpression(@"w+([-+.']w+)*@w+([-.]w+)*.w+([-.]w+)*",
ErrorMessage = "El email es inválido.")]
public string Email { get; set; }

public Person()
{

}
}

 

 

Como podemos observar ante de cada propiedad hay algunos atributos  que son colocados de acuerdo a los tipo de validacion que querramos hacer.  Una vez incluido los atributos crearemos un “Custom Validator” que procese cada atributo de la propiedad segun el ASP.NET Control asociado a dicha propiedad.

 

public class DACustomVal : BaseValidator
{

public string PropertyName { get; set; }
public string SourceType { get; set; }

protected override bool EvaluateIsValid()
{
//Obtenemos la info del tipo, propiedades y del control.
Type objType = Type.GetType(SourceType, true, true);
PropertyInfo propInfo = objType.GetProperty(PropertyName);
Control control = this.FindControl(this.ControlToValidate);

//Verificamos si el control es de tipo textbox.
if (control is TextBox)
{
TextBox txt = this.FindControl(this.ControlToValidate) as TextBox;
foreach (ValidationAttribute attr in
propInfo.GetCustomAttributes(typeof(ValidationAttribute), true).OfType<ValidationAttribute>())
{
if (!attr.IsValid(txt.Text))
{
//this.Text = attr.ErrorMessage;
this.ErrorMessage = attr.ErrorMessage;
return false;
}
}
}
//Verificamos si el control es un dropdownlist.
else if (control is DropDownList)
{
DropDownList ddl = this.FindControl(this.ControlToValidate) as DropDownList;
foreach (ValidationAttribute attr in
propInfo.GetCustomAttributes(typeof(ValidationAttribute), true).OfType<ValidationAttribute>())
{
if (!attr.IsValid(ddl.SelectedValue))
{
this.Text = attr.ErrorMessage;
return false;
}
}
}


return true;
}

}

 

Una vez conluido este “Custom Validator” podemos hacer uso de este en nuestro formulario web, como se exibe a continuación:

 

 <fieldset class="general-form">
<
h4>Registro de Personas</h4>
<
asp:ValidationSummary ID="vs" CssClass="error" runat="server" DisplayMode="BulletList" />
<
p>
<
label for="txtName">Nombre:</label>
<
asp:TextBox runat="server" ID="txtName" />
<
CustomDAVal:DACustomVal ID="cvName" runat="server"
ControlToValidate
="txtName" PropertyName="Name" SourceType="Objects.Person" />
</
p>
<
p>
<
label for="txtApellido">Apellido:</label>
<
asp:TextBox runat="server" ID="txtApellido" />
<
CustomDAVal:DACustomVal ID="DACustomVal1" runat="server"
ControlToValidate
="txtApellido" PropertyName="LastName" SourceType="Objects.Person" />
</
p>
<
p>
<
label for="txtEdad">Edad:</label>
<
asp:TextBox runat="server" ID="txtEdad" />
<
CustomDAVal:DACustomVal ID="DACustomVal2" runat="server"
ControlToValidate="txtEdad" PropertyName="Age" SourceType="Objects.Person" />
</
p>
<
p>
<
label for="txtEmail">Email:</label>
<
asp:TextBox runat="server" ID="txtEmail" />
<
CustomDAVal:DACustomVal ID="DACustomVal3" runat="server"
ControlToValidate="txtEmail" PropertyName="Email" SourceType="Objects.Person" />
</
p>
<
p>
<
asp:Button Text="Guardar" ID="btnSave" runat="server" CssClass="button"
Height="31px" Width="86px" />
</
p>
</
fieldset>

 

 <CustomDAVal:DACustomVal>  representa mi “Custom Validator” el cual asocia el control de ASP.NET con la propiedad del objeto.   En este punto ya contamos con la validacion del lado del servidor, en este caso usamos un “Validation Summary” (El class hide evita que el texto se despligue al lado de los textbox, un issue que se puede manejar.).  Como la validación que hacemos es del lado del servidor, debemos hacer postback al servidor para que los validadores funciones, tal y como se muestra en la pantalla.

 

VS[1]

 

Para que esta misma funcionalidad se aproveche en el lado del cliente y evitar un postback al servidor, se ha incluido en el metodo “OnPreRender” del “Custom Validator” algunas lineas de código que  itera sobre los controles del formulario y de forma dinamica incluye los validadores de .NET de acuerdo a los atributos definidos en cada una de las propiedades.

 

protected override void OnPreRender(EventArgs e)
{
if (this.EnableClientScript == true) {

//Para que los validadores no escriban nada del lado del control,
//asignamos este valor a la propiedad text de cada validador.
string emptyVal = "&nbsp;";

//Obtenemos la info del tipo, propiedades y del control.
Type objType = Type.GetType(SourceType, true, true);
PropertyInfo propInfo = objType.GetProperty(PropertyName);
Control control = this.FindControl(this.ControlToValidate);

foreach (object attr in propInfo.GetCustomAttributes(false))
{

if (attr is RequiredAttribute)
{
RequiredAttribute reqAttr = (RequiredAttribute)attr;

// Create requiredFieldValidator
RequiredFieldValidator rfVal = new RequiredFieldValidator();
rfVal.ControlToValidate = this.ControlToValidate;
rfVal.ErrorMessage = reqAttr.ErrorMessage;
rfVal.Text = emptyVal;
rfVal.ID = String.Format("rfv_{0}", PropertyName);
rfVal.ValidationGroup = this.ValidationGroup;

if (control is DropDownList)
rfVal.InitialValue = "-1";

this.Controls.Add(rfVal);
}
else if (attr is StringLengthAttribute){

//Si tenemos un atributo de maximo de caracteres le aplicamos ese atributo a la propiedad del texbox.
StringLengthAttribute slAttr = (StringLengthAttribute)attr;

if (this.FindControl(this.ControlToValidate) is TextBox){
((TextBox)this.FindControl(this.ControlToValidate)).MaxLength = slAttr.MaximumLength;
}

}
else if (attr is RegularExpressionAttribute){

RegularExpressionAttribute rexAttr = (RegularExpressionAttribute)attr;
RegularExpressionValidator rexVal = new RegularExpressionValidator();

rexVal.ControlToValidate = this.ControlToValidate;
rexVal.EnableClientScript = true;
rexVal.ErrorMessage = rexAttr.ErrorMessage;
rexVal.Text = emptyVal;
rexVal.ValidationExpression = rexAttr.Pattern;
rexVal.ID = String.Format("rexv_{0}", PropertyName);
rexVal.ValidationGroup = this.ValidationGroup;

this.Controls.Add(rexVal);
}
else if (attr is RangeAttribute)
{

RangeAttribute ranAttr = (RangeAttribute)attr;
RangeValidator ranVal = new RangeValidator();

ranVal.ErrorMessage = ranAttr.ErrorMessage;
ranVal.Text = emptyVal;
ranVal.ControlToValidate = this.ControlToValidate;
ranVal.ValidationGroup = this.ValidationGroup;
ranVal.ID = String.Format("ranv_{0}", PropertyName);
ranVal.EnableClientScript = true;
ranVal.MinimumValue = ranAttr.Minimum.ToString();
ranVal.MaximumValue = ranAttr.Maximum.ToString();


switch (ranAttr.OperandType.Name) {
case "Int32":
ranVal.Type = ValidationDataType.Integer;
break;
case "Decimal":
ranVal.Type = ValidationDataType.Currency;
break;
case "Double":
ranVal.Type = ValidationDataType.Double;
break;
case "DateTime":
ranVal.Type = ValidationDataType.Date;
break;
default:
ranVal.Type = ValidationDataType.String;
break;
}

this.Controls.Add(ranVal);

}

}

}

base.OnPreRender(e);
}

 

Con este código automáticamente agregamos los controles necesarios para tener validaciones del lado del cliente incluyendo los validadores de: Requerido, Rango, RegularExpression y adicional a esto controlamos el máximo de caracteres que puede recibir un textbox con el atributo “StringLengthAttribute”.  Al final el formulario mantiene el mismo aspecto pero llevando las validaciones al lado del cliente para evitar los molestos postback.

 

 VC[1]

Puede descargar el website completo desde Aquí, aunque está hecho en VS2010, ASP.NET 4.0 el código es totalmente compatible con VS2008 y .NET Framework 3.5 SP1.

6 comentarios en “Validando ASP.NET Web Form con DataAnnotation en Ambos lado(Cliente y Servidor).”

  1. Excelente tu artículo, tan solo un apunte, la función DACustomVal puede ser optimizada, ya que realizas findcontrol varias veces y utilizas is, la clausula is realiza una conversión implicita por lo que es mas adecuado convertir el control directamente evitando realizar el cast dos veces.

    TextBox control = FindControl(ControlToValidate) as TextBox;
    if (control != null)
    {

    }

    Te envio el código refactorizado, un saludo.

    public class DACustomVal : BaseValidator
    {
    public string PropertyName { get; set; }
    public string SourceType { get; set; }

    protected override bool EvaluateIsValid()
    {
    //Obtenemos la info del tipo, propiedades y del control.
    Type objType = Type.GetType(SourceType, true, true);
    PropertyInfo propInfo = objType.GetProperty(PropertyName);
    TextBox control = FindControl(ControlToValidate) as TextBox;

    //Verificamos si el control es de tipo textbox.
    if (control != null)
    {
    foreach (ValidationAttribute attr in propInfo.GetCustomAttributes(typeof (ValidationAttribute), true).OfType().Where(attr => !attr.IsValid(control.Text)))
    {
    //this.Text = attr.ErrorMessage;
    ErrorMessage = attr.ErrorMessage;
    return false;
    }
    }
    else
    {
    DropDownList ddl = FindControl(ControlToValidate) as DropDownList;
    if (ddl != null)
    {
    foreach (ValidationAttribute attr in propInfo.GetCustomAttributes(typeof (ValidationAttribute), true).OfType
    ().Where(attr => !attr.IsValid(ddl.SelectedValue)))
    {
    Text = attr.ErrorMessage;
    return false;
    }
    }
    }

    return true;
    }
    }

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *