Validation Application Block

Validation Application Block facilita la incorporacion de logica de validacion a las aplicaciones. Implementa varias de las reglas de validacion mas comunes como largo de string y expresiones regulares. Estas reglas son asignadas las propiedades de una clase usando atributos o el archivo de configuracion.
Permite definir la logica de validacion una vez y reutilizarla en las diferentes capas de la aplicacion (Presentacion, Logica de Negocios y Servicios). Incluye conectores para automatizar la validacion de controles ASP.NET, Windows Forms, WPF y un Behavior para automatizar validacion de servicios WCF.

Reglas de validacion

Validation Application Block incluye clases que implementan varias de las reglas de validacion comunmente usadas en aplicaciones empresariales:

  • Not Null: Verifica que un valor no es nulo.
  • Range Validator: Verifica que un valor este dentro de un rango indicado, puede usarse con cualquier tipo que implemente IComparable.
  • Date Time Range: Verifica que la fecha esta contenida en el rango indicado.
  • Relative Date Time: Verifica que una fecha este contenida en un rango de tiempo construido relativo a la fecha actual.
  • Domain: Verifica que el valor pertenece en un grupo de valores permitidos.
  • String Length: Verifica que el largo de string esta en el rango especificado.
  • Regular Expression: Verifica que un string cumple un patron definido en una expresion regular.
  • Contains Characters: Verifica que un string contiene algunos o todos los caracteres indicados.
  • Enum Conversion: Verifica que un string es convertible a un valor de un enumerado.
  • Type Conversion: Verifica que un string puede convertirse al tipo de dato indicado.
  • Property Comparison: Compara un valor con el valor de una propiedad.
  • Object Validator: Inidica que se debe ejecutar la validacion a un objeto referido por ejemeplo en una propiedad.
  • Object Collection: Indica que se debe ejecuta la validacion a todos los objetos referidos en una coleccion.
  • And Composite: Agrupar un conjunto de reglas verificando que todas estas se cumplan.
  • Or Composite: Agrupar un conjunto de reglas verificando que al menos una se cumpla.

Cada uno de estos validadores hereda de la clase base Validator y por lo tanto tienen algunas propiedades comunes:

  • MessageTemplate: Representa el mensaje de error entregado cuando la validacion falla. Puede usarse como plantilla incluyendo marcadores de posicion. {0} represente el valor validado, {1} representa el nombre de la propiedad, {2} representa el Tag. Indices mayores son especificos de cada validador.
  • MessageTemplateResourceName y MessageTemplateResourceTypeName: Sirven para localizar los mensajes de error, utiles si su aplicacion debe ser traducida en varios lenguajes.
  • Negated: Cambia el comportamiento del validador de tal forma que reporta un error cuando la condicion se cumple, lo contrario a como es usualmente.
  • Tag: Un string que puede usarse libremente como etiqueta, puede servir por ejemplo para categorizar los mensajes de error segun secciones de un formulario.

Los validadores puede agruparse formando un conjunto de reglas: Rulesets. Esto es util si se debe validar un objeto de dos o mas formas distintas dependiendo del contexto, por ejemplo, si una orden de venta se esta ingresando se valida de una manera y si se esta editando se valida de otra manera.
Al momento de validar un objeto puede indicar que Ruleset usar, incluso puede combinar dos o mas Ruleset lo que da mucha flexibilidad.
Los Rulsets se indentifican por nombre. Para asignar un validador a un Ruleset usando atributos se debe definir la propiedad Ruleset del atributo. Para hacerlo mediante archivo de configuracion se deben colocar los validadores dentro de un elemento <ruleset name="nombre">.

Usar los validadores

Hay tres formas de usar los validadores: Mediante codigo, asignandolos a clases mediante atributos, asignandolos a clases mediante el archivo de configuracion.

Es posible usar los validadores mediante codigo creando una instancia de un validador y luego llamando el metodo Validate(). En el siguiente ejemplo se crea una instancia de EnumConversionValidator que verifica si un string es convertible a un enumerado, luego se llama el metodo Validate() pasando como argumento el valor a validar. Se obtiene como resultado un objeto ValidationResults cuya propiedad IsValid indica si validacion fue correcta:

Validator unitValidator = new EnumConversionValidator(typeof(Unit));
ValidationResults result = unitValidator.Validate(obj);
bool valid = result.IsValid;
string msgError = result.ToString();

Usar los validadores mediante codigo no es lo mas comun, no se justifica para los validadores mas simples pues es mas corto utilizar directamente C#. Para los validadores mas complejos si podria utilizarse en partes de su aplicacion donde decida no utilizar la infrastructura de Validation Application Block, pero quiera reutilzar la logica de validacion de por ejemplo validadores que haya creado para encapsular la lógica de negocios de su aplicación

El segundo modo de uso para los validadores es asginarlos mediante atributos a propiedades de una clase. En el siguiente codigo de ejemplo se asignan validadores a las propiedades de la clase Cliente. La propiedad Nombre tiene dos validadores asignados NotNull y StringLenght. La propiedad FechaNacimiento tiene un validador RelativeDateTime asignado al Ruleset Adultos que sera utilizado solo en cuando se deba validar que un cliente sea adulto. La propiedad Email tiene un validador de expresion regular que ademas tiene un mensaje de error personalizado.

public class Cliente
{
    [NotNullValidator()]
    [StringLengthValidator(1, 50)]
    public string Nombre
    {
        get { return nombre; }
        set { nombre = value; }
    }

    [RelativeDateTimeValidator(-120, DateTimeUnit.Year, -18, DateTimeUnit.Year)]
    public DateTime FechaNacimiento
    {
        get { return fechaNacimiento; }
        set { fechaNacimiento = value; }
    }

    [RegexValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", MessageTemplate = "Invalid e-mail address")]
    public string Email
    {
        get { return email; }
        set { email = value; }
    }
}

Solo se pueden aplicar atributos a propiedades publicas y leibles (tienen get). Tambien se pueden aplicar a parametros de metodos pero solo son tomados en cuenta por el behavior WCF y por Policy Injection Application Block.
El comportamiento de los atributos cuando son heredados de una clase base es el siguiente: Si la clase derivada hereda el miembro y no lo reemplaza, el validador se mantiene. Pero si el miembro se reemplaza entonces los validadores se pierden.
Una vez que los validadores son asignados a una clase, se puede validar un objeto en cualquier parte de la aplicacion utilizando el metodo Validate de la fachada estatica Validation, lo veremos en detalle mas adelante.

La tercera alternativa es asignar validadores usando el archivo de configuracion, la ventaja de esta alternativa es que la logica de validacion es modificable sin necesidad de recompilar la aplicacion aportando flexibilidad adicional.
Se define primero la clase a la cual se asignaran los validadores usando el elemento <type>. Luego si los validadores pertenerseran a un Ruleset se debe incluir el elemento <ruleset>. Las propiedades se definen dentro del elemento <properties>. Dentro de cada propiedad se incluyen los validadores con sus atributos. Los validadores se identifican por clase y ensamblado, lo que permite incluir validadores creados por nosotros mismos.

<validation>

  <type assemblyName="ValidationQuickStart.BusinessEntities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

     name="ValidationQuickStart.BusinessEntities.Customer">

    <ruleset name="RuleSetA">

      <properties>

        <property name="FirstName">

          <validator lowerBound="0" lowerBoundType="Ignore" upperBound="20"

            upperBoundType="Inclusive" negated="false" messageTemplate="First name must be less than 20 characters"

            messageTemplateResourceName="" messageTemplateResourceType=""

            tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation"

            name="String Length Validator" />

        </property>

        <property name="DateOfBirth">

          <validator lowerBound="1969-01-01" lowerBoundType="Inclusive"

            upperBound="" upperBoundType="Ignore" negated="false" messageTemplate="Must be born after 1969"

            messageTemplateResourceName="" messageTemplateResourceType=""

            tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.DateTimeRangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation"

            name="Date Range Validator" />

        </property>

        <property name="Email">

          <validator pattern="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"

            options="None" patternResourceName="" patternResourceType=""

            messageTemplate="Invalid e-mail address" messageTemplateResourceName=""

            messageTemplateResourceType="" tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RegexValidator, Microsoft.Practices.EnterpriseLibrary.Validation"

            name="Regex Validator" />

        </property>

      </properties>

    </ruleset>

  </type>

</validation>

La configuracion es bastante extensa, no es practico modificarla manualmente excepto para cambios menores. Se recomienda usar la herramienta de configuracion de Enterprise Library para modificar la editar la configuracion de validacion.

Validar un Objeto

Para validar un objeto al cual se le ha asignado reglas de validacion mediante atributos o el archivo de configuracion podemos usar la fachada Validation con la cual es posible validar un objeto en una sola linea de codigo. En el codigo de ejemplo se valida una instancia de la clase Cliente.

Cliente cli = new Cliente();
ValidationResults results = Validation.Validate<Cliente>(cli);

Se puede indicar adicionalmente uno o mas ruleset a utilizar en la validación. En el siguiente ejemplo se utilizan validadores pertenecientes a dos ruleset: rulesetA y rulesetB.

Cliente cli = new Cliente();
ValidationResults results = Validation.Validate<Cliente>(cli, "rulesetA", "rulesetB");

Si va a validar un tipo de objeto muchas veces quizas sea conveniente usar el siguiente procedimiento para obtener una instancia de un objeto Validator que representa todos los validadores asignados a la clase y es reutilizable.
Internamente la fachada Validation crea una instancia de estas cada vez que se llama el metodo Validate.

Customer cust = new Customer();

Validator<Customer> validator = ValidationFactory.CreateValidator<Customer>();

ValidationResults results = validator.Validate(cust);
Entendiendo el objeto ValidationResults

El metodo validate devuelve un objeto ValidationResults. Su propiedad IsValid es true si la validacion fue correcta, false si falló. ValidationResults es a su vez una colección de objetos ValidationResult (notar sin s). Cada objeto ValidationResult representa el resultado de validacion de cada uno de los validadores asignados a la clase que fallaron la validación. Las propiedades mas importantes del objeto ValidationResult son:

  • Key: Representa el miembro de la clases que se valido, generalmente el nombre de una propiedad.
  • Message: El mensaje de error.
  • Tag: Valor definido en la propiedad Tag del validador cuando se expresa con atributos o configuracion.
  • Target: El objeto que se valido.
  • Validator: Una referencia al validador que realizo la validacion.
  • NestedValidationResults: Obtiene los resultados de validación anidados en los validadores que se componen de otros validadores, por ejemplo Or Compsite y And Composite.

Para obtener un mensaje de error que incluya todos los validadores que fallaron de interar por la coleccion ValidationResults.

Self Validation

Self Validation implementar logica de validacion dentro de la clase que se va a validar. La logica se define en metodos dentro de la propia clase a validar. Estos metodos deben tener una firma especifica, recibir un objeto ValidationResults y devolver void. Pueden ser no-publicos y son heredables. Para denotar su existencia se marcan con el atributo SelfValidation. La clase tambien debe marcarse con un atributo, HasSelfValidation. En el siguiente ejemplo se define el metodo ChequearTemperatura que cumple con la firma adecuada y esta marcado con el atributo SelfValidation. Observe ademas que la clase esta marcada con el atributo HasSelfValidation.

[HasSelfValidation]
public class TemperaturaRango
{
  private int minimo;
  private int maximo;

  // ...

  [SelfValidation]
  public void ChequearTemperatura(ValidationResults results)
  {
    if (maximo < minimo)
      results.AddResult(new ValidationResult("Maximo menor que minimo", this, "", "", null));
  }
}
Extension

Es posible extender Validation Application Block creando nuevos validadores. Para crear un nuevo validador herede de la clase base Validator o la generica Validator<t>.
Para usar el validador como atributo debe crear el atributo correspondiente heredando de la clase ValidatorAttribute.
Otra alternativa es hererdar de uno de los validadores existentes para extender su funcionalidad o hacerlo mas especifico.
En la aplicación Quickstart hay ejemplos de como crear validadores.

Reutilizar codigo de validacion

En una aplicacion se realiza validacion en varias etapas de un proceso. Por ejemplo, un usuario ingresa datos para una orden, la interfaz de usuario valida los datos a medida que el usuario los ingresa para dar feedback inmediato de posibles errores. En una aplicacion web esto puede hacerse mediante javascript y ajax, pero entonces hay que volver a validar la pagina cuando se envia al servidor web pues la el codigo cliente podria haber sido modificado o javascript este desactivado.
Una vez que estan todos los datos ingresados y validados en la capa de presentacion la orden va al componenete que registra los datos. Es buena practica que este componente vuelva a validar los datos antes de registrar pues no hay seguridad de que el codigo cliente este comprometido o hayan clientes delgados que no realicen validacion.
Ademas, si el componente que registra ordenes esta expuesto en un servidor diferente como un servicio web, es quizas necesario validar los argumentos que reciben las operaciones del servicio web.
Si la aplicacion tiene mas de una implementacion de la capa de presentacion, pudiendo ser ASP.NET, Windows Forms, Silverlight o WPF, habria que repetir el codigo de validacion en cada una de estas implementaciones.

En definitiva, la validacion ocurre en varias partes o capas de la aplicacion. Es buena idea definir la logica de validacion una vez y reutilizarla en cada uno de los puntos en que es requerida. Validation Application Block permite exactamente esto (si se utilizan las mismas entidades en las diferentes capas).

Windows Forms

Se incluye ValidationProvider que es una implementacion de ExtenderProvider. ValidationProvider agrega propiedades a los controles de un formulario para ser definidas en al vista de diseño. ValidationProvider permite asignar al control una propiedad de la entidad y responde al evento Validating del control automaticamente ejecutando los validadores asignados a esa propiedad mediante atributos o configuracion. Puede usar el evento ValueConvert si el tipo de dato del valor del control no coincide con el de la entidad. ValidationProvider puede asociarse con ErrorProvider para mostrar automaticamente un icono con un mensaje de error cada vez que falla la validacion de un control. En ValidationProvider debe definir el Ruleset y el tipo de la entidad a validar.

ASP.NET

Se incluye PropertyProxyValidator que es un Control validador, hereda de BaseValidator. Es asignable a controles ASP.NET como cualquier otro validador. Debe definir el tipo de la entidad a validar, el Ruleset y el nombre de la propiedad de la entidad que sera asociada al control. Cuando se valida el formulario PropertyProxyValidator ejecuta los validadores asociados a las propiedades de la entidad. Puede utilizar el evento ValueConvert si el tipo de dato del valor del control no coincide con el de la entidad.
Si desea realizar la validacion usando AJAX para logar una mejor experiencia de usuario puede descargar el componente Validation Guidance Bundle que es parte de Web Client Software Factory pero puede utilzarse en forma independiente.

WCF

Incluye un bahavior que permite validar automaticamente contratos y parametros. El behavior agrega un Parameter Inspector al pipline. Esto permite ejecutar la validacion sobre los parametros de las operaciones del servicio. Es posible validar contratos que tengan validadores asignados como tambien parametros que tengan atributos asignados directamente. Asignar validadores a los parametros de un metodo solo solo funciona para esta integracion con WCF y para la integracion con Policy Injection.

WPF

Un adaptador para Windows Presentation Foundation puede encontrarse en WPF integration for validation application block.

Vea tambien
Links