La última vez que conversamos acerca de configuraciones sacamos a relucir lo sencillo que es agregar soporte para funciones de validación callback, y antes de eso comentábamos lo sencillo que era agregar soporte para validación de forma declarativa usando los atributos de validación incluídos en la Configuration Framework de la .Net Framework.
Hoy seguiremos caminando en el soporte de validación customizada de secciones y elementos de validación, pero esta vez creando nuestros propios atributos de validación customizados.
Atributos de Validación
Un atributo de validación nos permite validar los elementos o secciones de nuestra configuración de .Net, simplemente tenemos que adornar ese elemento con el atributo en cuestión. La .Net framework nos incluye algunos atributos ya dentro de la caja, pero podemos agregar los nuestros propios de forma sencilla.
Realmente un atributo de validación costa de dos clases: La clase que marca el atributo (un atributo marcador) y la clase que ejecuta la acción de validar (el validador). Como podremos notar en este lado de la framework se utilizan los atributos de una manera muy particular (y que en lo personal me gusta) de mezclar el atributo como marcador pero no como ejecutor.
Usemos un simple ejemplo, imaginemos que necesitamos validar la entrada de correos electrónicos, como todos sabemos, podemos seguir un par de reglas expuestas en una serie de RFC’s acerca de direcciones de email. Bueno, comencemos por la clase que hace realmente la validación, esta debe heredar de la clase ConfigurationValidatorBase y luego el override de los métodos CanValidate (que nos dice si podemos o no validar el tipo en cuestión) y Validate (quien realmente realiza la validación).
using System;
using System.Configuration;
using System.Text.RegularExpressions;
namespace Cprieto.Samples {
public class EmailValidator : ConfigurationValidatorBase {
private const string Word = "[^x00-x1F^(^)^<^>^@^,^;^:^\^"^.^[^]^s]";
private const string IpEntry = "[[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}]";
private static readonly string Domain = string.Format("({0}+(.{0}+)*", Word);
private static readonly Regex EmailRegex =
new Regex(string.Format("^{0}+(.+)*@{1}|{2})$", Word, Domain, IpEntry), RegexOptions.Compiled);
public override bool CanValidate(Type type) {
return type == typeof (string);
}
public override void Validate(object value) {
var item = (string) value;
if (!string.IsNullOrEmpty(item) && !EmailRegex.IsMatch(item))
throw new ArgumentException("value is not a valid email");
}
}
}
Bien, ahora simplemente creamos el atributo marcador, este debe heredar de ConfigurationValidatorAttribute y la parte importante de este es el override de la propiedad ValidatorInstance que retorna una nueva instancia de nuestra clase validadora.
using System.Configuration;
namespace Cprieto.Samples {
public class EmailValidatorAttribute : ConfigurationValidatorAttribute {
public override ConfigurationValidatorBase ValidatorInstance {
get { return new EmailValidator(); }
}
}
}
Ahora simplemente lo aplicamos a nuestra sección de configuración:
using System.Configuration;
namespace Cprieto.Samples {
public class SampleConfigurationSection : ConfigurationSection {
[CallbackValidator(Type = typeof(PortValidator),
CallbackMethodName = "Validate")]
[ConfigurationProperty("port", DefaultValue = 80)]
public int Port {
get { return (int) this["port"]; }
}
[ConfigurationProperty("host", IsRequired = true)]
public string Host {
get { return (string) this["host"]; }
}
[EmailValidator]
[ConfigurationProperty("email")]
public string Email {
get { return (string) this["email"]; }
}
}
}
Y claro, nunca cae de más un pequeño archivo de validación que debe marcar como inválido
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="sample"
type="Cprieto.Samples.SampleConfigurationSection, Cprieto.Samples.ValidationCallback"/>
</configSections>
<sample port="80" host="localhost" email="invalid@@email" />
</configuration>
Bueno, y ahora me queda pensar que escribo para el siguiente post de la serie
Hasta la próxima!
