Mostrando entradas con la etiqueta validation application block. Mostrar todas las entradas
Mostrando entradas con la etiqueta validation application block. Mostrar todas las entradas

viernes, 26 de octubre de 2007

PIAB, programación orientada a aspectos, aventuras validando un toco de datos y más...

Siguiendo con mi aventura de la aplicación con muchísimos formularios con muchísimos campos, me encontré con el siguiente problema: necesitaba validar muchas propiedades de muchas clases para luego persistirlas en la base de datos, bien, el tema es que se trataba de una tarea repetitiva además de molesta, como estoy utilizando Validation Application Block me veía ante la nada envidiable tarea de agregar cada clase al App.config considerar todos los campos que necesitan validación (en realidad todos ya que los campos en la base de datos tiene un límite) y colocarle un String Lenght Validator configurado a cada uno, a esto sumarle el mensaje de error; una tortura china podríamos decir. Como a mi, ni a ningún desarrollador que se precie, me gusta realizar tareas repetitivas comencé a darle vueltas al tema para encontrar una solución más piola, y llegó…
Vamos a resumir el escenario, muchas clases con muchas propiedades que es necesario persistir en la base de datos, por lo tanto cuanto menos es necesario validar el largo de los campos.

Tenemos dos opciones

• Un “if” en cada propiedad (no se me ocurre una cosa más fea en una aplicación)
• Utilizar Validation Application Block, pero tenemos el problema de la configuración para cada propiedad.

La solución Enterprise Library desde su versión 3 trae incluido un application block que tenía en la cabeza esperando la oportunidad de utilizarlo, esto me lleva a explicar de qué se trata es el policy injection application block PIAB.

Programación orientada a aspectos

Este extraño nombre es el que lleva un paradigma de programación que se basa (básicamente) en la siguiente idea: toda aplicación contiene aspectos que la atraviesan que se pueden separar modularmente, por ejemplo, una aplicación puede tener un “logeo” de lo que pasa, que va más allá de la lógica de negocios, simplemente se trata de “logear” lo que está ocurriendo, esto es claramente un aspecto, no tiene relación con el objetivo de la aplicación y realmente “ensucia” el código (scattered code ó código dispeso), bien, la POA se basa en que estos aspectos se pueden agregar de otra manera sin ensuciar la lógica básica de la aplicación.

Policy injection application block

Como dije antes Enterprise library tiene un AB para programación orientada a aspectos que lleva el nombre de policy injection application block (PIAB) que nos permite inyectar aspecto, algo interesante es que tenemos aspectos predefinidos, los más suspicaces imaginaran cuáles, los otros application blocks (además de otros), es así que podemos indicar sin necesidad de código que se invoque al validation application block, security application block, logging application block, caching application block y algunas cosas más además de nuestros propios aspecto, vamos a explicar de un modo sucinto cómo funciona.

Primero que nada PIAB se basa en dos conceptos básico:

• Matching Rules: Son las reglas que nos indican qué interceptar para aplicar los aspectos.
• Handlers: Son los encargados de inyectar los aspectos sobre los elementos que cumplan con las matching rules.

Matching rules

Hay muchísimas y muy interesantes, por supuesto podemos crear las nuestras, a continuación enumeramos algunas:

• And matching rule
• Assembly matching rule
• Custom attribute matching rule
• Member name matching rule
• Method signatura matching rule
• Namespace matching rule
• Property matching rule
• Parameter type matching rule
• Return type matching rule
• Tag matching rule

Handlers

Como dije antes tenemos handlers para los application block (logging, validation, catiching, security), y otros más además de poder crear los nuestros, enumeremos algunos:

• Authorization handler
• Caching handler
• Cursor handler
• Custom handler
• Exception handler
• Transaction scope handler
• Validation handler

Para lograr esto necesitamos que nuestra clase herede de MarshalByRefObject e instanciarla a través de PolicyInjection del siguiente modo

Persona p = PolicyInjection.Create<Persona>();

La otra opción es “wrappear” una clase existente de esta forma

Persona p1 = PolicyInjection.Wrap<Persona>(p);

El comportamieno del PIAB depende del handler, por ejemplo si colocamos una matching rule para que intercepte cuando se hace set de una propiedad y la asociamos a un handler del logging, en cuanto en algún sitio de la aplicación se asigne valor a dicha propiedad se van a loger un conjunto de datos, si la asociamos a un validation hadler y no pasa la validación va a lanzar una excepción, etc.

La gran solución

Así es que me di cuenta (en realidad estaba pensado así sólo que yo no lo sabía) que podía crear una property matching rule y agregar todas las propiedades, por ejemplo, que fueran string y tuvieran el mismo largo máximo, agregar un validation handler contra una regla para el tipo string que tenga un range validator que verifique el valor de la propiedad lenght, veámoslo un poco más gráfico.



Código
Persona p = PolicyInjection.Create<Persona>();            

try
{
 p.Apellido = "Ariel Arnaldo Ortega";
}
catch (ArgumentValidationException ex)
{
 foreach (ValidationResult result in ex.ValidationResults)
 {
  Console.WriteLine(result.Message);
 }
}
El PIAB intercepta las clases más precisamente el momento en que se hace “set” de alguna de las propiedades enumeradas, acto seguido llama al handler que invoca al VAB, este tiene una regla para el tipo string (string como las propiedades que quiero validar), sobre su propiedad lenght a la que le aplica un range validator que permite validar que el largo se encuentre en los valores que yo necesito, digamos entre 5 y 10, en caso que la validación no sea exitosa el PIAB lanza una excepción (esta parte es medio fea pero no había otra forma de hacerlo según me parece) y dentro de ella se encuentra el Validation Results con el resultado de la validación, con esto logro resumir en 5 reglas la validación de 300 campos, mágico, hasta la próxima.

jueves, 18 de octubre de 2007

Validation Application Block, composite validators inútiles

No trabajo mucho con winform y como para compensar tengo que hacer una aplicación con 300 text box más o menos todos con validación, etc. La aplicación tiene que permitir al usuario ingresar muchos datos, ahora, no todos junto, el usuario puede poner algunos datos, guardarlos y luego continuar, como paso final genera un archivo de salida que se integra a un sistema, muy bonito...para el usuario.
 Lo que me hacía falta era la forma de validar ciertas cosas, por decir, un campo "edad" numérico, pero sólo si tiene un valor, por supuesto que para validar que sea numérico utilizo TypeConversionValidator, perfecto, el problema es que no puedo lanzar la validación si el campo no se encuentra lleno porque tira una fea excepción. Entonces dije "es una excelente ocasión para utilizar los composite validator, and y or" en teoría nos permiten aplicar validaciones convinadas, pero de un modo extraño.

¿Que esperamos de los composite validator?

Del AndComposite uno esperaría que un AND funcione del siguiente modo: si pongo dentro dos reglas de validación que el total sea válido sólo si ambas lo son, es más, para ser fino podemos hacerlo "lazzy" es decir, que si detecta que la primer regla no es válida que no continúe, esto hubiera sido genial para poner un string lenght validator y luego un type conversion para que si el primero no es válido no continúe y no "pinche", pero no, no funciona así, remitamonos al código del método DoValidate

protected internal override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
{
    foreach (Validator validator in this.validators)
    {
        validator.DoValidate(objectToValidate, currentTarget, key, validationResults);
    }
}

 
Parece que no nos soluciona el problema, el tipo sigue adelante a pesar que la primer regla no pase, me pregunto ¿para qué sirve entonces?¿No es lo mismo que poner varios validadores sueltos?

Segundo intento OrCompositeValidator

Siguiendo con nuestra preciada inocencia imaginamos que el OCV nos dirá que algo es válido si alguna de sus reglas lo es, por lo tanto podemos poner un string lenght negado para que sólo continúe en caso contrario, pero no, otra vez la implementación no nos ayuda

protected internal override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
{
    List<ValidationResult> nestedValidationResults = new List<ValidationResult>();
    foreach (Validator validator in this.validators)
    {
        ValidationResults results = new ValidationResults();
        validator.DoValidate(objectToValidate, currentTarget, key, results);
        if (results.IsValid)
        {
            return;
        }
        nestedValidationResults.AddRange(results);
    }
    base.LogValidationResult(validationResults, this.GetMessage(objectToValidate, key), currentTarget, key, nestedValidationResults);
}

 
Lo que hace es salir en cuanto alguna regla sea válida, o sea es "lazzy" lo contrario del And, por lo tanto no nos sirve porque además si la regla no es válida agrega el error al ValidationResult, es decir, si la primer regla dentro del OCV no es válida continúa (esto es correcto) pero ¡agrega el error al resultado! por más que la segunda sea válida, con lo cual no sirve para nada tampoco.

En resumen ninguno de los dos sirven para nada, si alguien puede sacarles provecho que me avise.....

La solución

La solución fue....un range validator, un valor por defecto y evitar que se ingresen datos que no sean numéricos en dichos TextBox, eso lo hice de un modo más o menos elegante.

public void AssignNroHandler(TextBox txt)
{
  txt.KeyPress += HandleNro;
}

public void HandleNro(object sender, KeyPressEventArgs e)
{
 short nro = 0;
 e.Handled = !short.TryParse(Convert.ToString(e.KeyChar), out nro);
}

Después por cada TextBox que quiero evitar que se ingresen datos que no sean numéricos hago

this.AssignNroHandler(this.textBox1);

Y santo remedio, en fin.

viernes, 20 de julio de 2007

Validaton Application Block desde Web Config

Introducción En la entrada anterior vimos como crear validaciones agregando atributos a nuestras clases, ahora vamos a ver como podemos hacer el mismo trabajo sin tocar para nada las clases, incluso podemos aplicar reglas de validación a clases a las cuales no tenemos acceso al código, todo gracias al VAP. Comencemos Tomemos nuestra clase y quitemos todos los atributos.
1:     public class Persona
2:     {
3:         private int _edad;
4:         private string _nombre;
5:         private string _apellido;
6:         private string _email;
7:  
8:         public string Email
9:         {
10:             get { return _email; }
11:             set { _email = value; }
12:         }
13:         public string Apellido
14:         {
15:             get { return _apellido; }
16:             set { _apellido = value; }
17:         }
18:         public string Nombre
19:         {
20:             get { return _nombre; }
21:             set { _nombre = value; }
22:         }
23:         public int Edad
24:         {
25:             get { return _edad; }
26:             set { _edad = value; }
27:         }
28:     }
Ahora, agregamos un App.Config y lo abrimos con el editor de EntLib. Acto seguimos hacemos click derecho y seleccionamos Add Validation Application Block Acto seguido seleccionado Add New Rule Set y le ponemos de nombre Default Luego agregamos un nuevo tipo, con esto vamos a agregar nuestra clase Persona. Buscamos el tipo en el cuadro que aparece y luego hacemos botón derecho y selecionamos Choose Members Y seleccionamos las propiedades que queremos validar Hacemos botón derecho sobre el Apellido (por ejemplo) y seleccionado New -> Not Null Validator Presionamos F4 y modificamos sus propiedades para que nos muestre un mensaje en caso de no superar la validación desde la propiedad Message Template Y seguimos agregando los validadores, utilizamos el código de validación que vimos antes
   1:              Persona p = new Persona();
   2:   
   3:              Console.WriteLine("Ingrese nombre");
   4:              p.Nombre = Console.ReadLine();
   5:   
   6:              Console.WriteLine("Ingrese apellido");
   7:              p.Apellido = Console.ReadLine();
   8:   
   9:              Console.WriteLine("Ingrese edad");
  10:              p.Edad = Convert.ToInt32(Console.ReadLine());
  11:   
  12:              Console.WriteLine("Ingrese email");
  13:              p.Email = Console.ReadLine();
  14:   
  15:              ValidationResults results = ValidationFactory.CreateValidator("Default").Validate(p);
  16:   
  17:              if (!results.IsValid)
  18:              {
  19:   
  20:                  StringBuilder sb = new StringBuilder("Errores de validación");
  21:                  sb.AppendLine();
  22:   
  23:                  foreach (ValidationResult result in results)
  24:                  {
  25:                      sb.AppendLine(result.Message);
  26:                  }
  27:   
  28:                  Console.WriteLine(sb.ToString());
  29:              }
  30:              else
  31:              {
  32:                  Console.WriteLine("todo bien");
  33:              }

Lo corremos y eureka!! funciona, buenísimo y muy flexible. En la próxima vemos como hacer nuestros propios validadores.