Unity, Proxies, AOP y un poco de todo eso…

Publicado 24/9/2009 11:34 por Eduard Tomàs i Avellana

En mi opinión, usar un contenedor de IoC hoy en día, no es una opción sinó una obligación. Las ventajas que nos ofrecen son incotestables. Los patrones Service Locator y Dependency Injection nos permiten desacoplar nuestro código, y son la base para poder trabajar de forma modular y poder generar unos tests unitarios de forma más sencilla. Pero hoy no quiero hablaros de ninguno de estos patrones, sinó de otra de las capacidades de los contenedores de IoC: la generación de proxies.

Con esta técnica lo que podemos hacer es inyectar nuestro propio código para que se ejecute antes o después del código que contenga la clase en particular. Esto, si lo combinamos con los atributos nos proporciona unas capacidades potentísimas para poder tener programación orientada a aspectos.

Vamos a ver como realizar esta técnica usando Unity, pero no es exclusiva de este contenedor de IoC, otros contenedores como Windsor también tienen esta capacidad.

El mecanismo en Unity que nos permite generar proxies a partir de clases del usuario, se llama intercepción. Cuando creamos una intercepción en Unity debemos definir básicamente dos cosas:

  1. Qué ocurre cuando un método es interceptado (la política de intercepción).
  2. Cómo se intercepta un método (el interceptor).

Vamos a ver paso a paso como funciona el mecanimso.

1. Preparación del entorno

Vamos a crear una aplicación de consola, y añadimos las referencias a todos los ensamblados de Unity.

Luego vamos a crear una interfaz, y la clase que vamos a interceptar:

public interface IMyInterface
{
    string SomeProperty { get; set; }
}
public class MyClass : IMyInterface
{
    public string SomeProperty { get; set; }
}

Finalmente, en el método Main() creamos un contenedor de Unity y registramos el mapping entre la interfaz y el tipo:

static void Main(string[] args)
{
    UnityContainer uc = new UnityContainer();
    uc.RegisterType<IMyInterface, MyClass>();
}

Ahora estamos listos para empezar!!!

2. Configuración de Unity para que use un interceptor

Vamos a configurar Unity para que use un interceptor cuando se resuelva la interfaz IMyInterface. Para ello, primero debemos añadir la extensión de intercepción a Untiy y luego configurarla:

static void Main(string[] args)
{
    UnityContainer uc = new UnityContainer();
    uc.RegisterType<IMyInterface, MyClass>();
    // Añadimos la extensión de intercepción
    uc.AddNewExtension<Interception>();
    // La configuramos para que nos devuelva
    // un TransparentProxy cuando resolvamos IMyInterface
    uc.Configure<Interception>().
    SetInterceptorFor<IMyInterface>
        (new TransparentProxyInterceptor());
    var u = uc.Resolve<IMyInterface>();
    u.SomeProperty = "test";
}

Vamos a meter un breakpoint en la última línea y a ejecutar el código, para ver si Unity ha echo algo:

image

Vaya… pues no parece que haya hecho nada, la verdad. La variable u es de tipo MyClass, no parece haber ningún proxy por ahí…

Es normal, ya que hemos configurado Unity para que use un TransparentProxy al resolver la interfaz IMyInterface, pero no lo hemos dicho que debe hacer Unity con este proxy, así que simplemente para no hacer nada, no crea ni el proxy…

3. Crear el interceptor

Ha llegado el momento de crear un interceptor, que defina que ocurre cuando se intercepta un método o propiedad de la clase. Para ello vamos a crear una clase nueva que implementa la interfaz ICallHandler:

public class MyHandler : ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input, 
GetNextHandlerDelegate getNext) { IMethodReturn msg = getNext()(input, getNext); return msg; } public int Order { get; set; } }

Esta es la implementación básica por defecto de ICallHandler: no estamos haciendo nada, salvo pasar la llamada a la propiedad real de la clase. Es decir, nuestro interceptor no está haciendo realmente nada.

Podemos añadir aquí el código que queramos, p.ej:

public IMethodReturn Invoke(IMethodInvocation input, 
GetNextHandlerDelegate getNext) { Console.WriteLine("Se ha llamado {0} con valor {1}",
input.MethodBase.Name, input.Inputs[0]); IMethodReturn msg = getNext()(input, getNext); return msg; }

4. Indicar que métodos / propiedades queremos interceptar

Tenemos a Unity configurado para usar intercepción, y un interceptor creado… ahora nos queda finalmente vincular este interceptor con las propiedades o métodos que deseemos.

Para ello podemos usar los atributos: la idea es decorar cada propiedad o método con un atributo que indique que interceptor se usa para dicha propiedad y además configure dicho interceptor. Así, generalmente, vamos a usar un atributo para cada interceptor. En nuestro caso tenemos un sólo interceptor (MyHandler), así que añadiremos un atributo MyHandlerAttribute.

Para ello vamos a crear una clase que derive de HandlerAttribute (la cual a su vez deriva de Attribute), y redefinir el método CreateHandler. En este método debemos devolver el Handler que deseemos:

[AttributeUsage(AttributeTargets.Property)]
class MyHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler
(IUnityContainer container) { return new MyHandler(); } }

En este caso nuestro atributo es trivial, pero en otros casos el atributo puede tener parámetros (y pasárselos al constructor del interceptor, o incluso crear un interceptor u otro en función de dichos parámetros).

5. Aplicar el atributo a las propiedades que deseemos

Para ello simplemente decoramos las propiedades (o métodos) que deseemos con el atributo:

public class MyClass : IMyInterface
{
    [MyHandler]
    public string SomeProperty { get; set; }
}

Y…. ya hemos terminado! Si colocamos un breakpoint en el mismo lugar de antes, veremos que ahora si que Unity nos ha creado un proxy:

image

Y si ejecutamos el programa, veréis como la salida por pantalla es la siguiente:

Se ha llamado set_SomeProperty con valor test

Nuestro interceptor ha sido llamado… hemos triunfado!!! ;-)

Si añadís una propiedad extra a la interfaz (y a la clase) y NO la decoráis con el atributo veréis que, obviamente dicha propiedad NO es interceptada.

Esta técnica tiene unas posibilidades brutales… a mi se me ocurren a brote pronte, temas de logging, seguridad, validación de propiedades… vamos, todo aquello en lo que es aplicable la programación orientada a aspectos!

Un saludo a todos! ;-)

Archivado en: ,,
Comparte este post:

Comentarios

# re: Unity, Proxies, AOP y un poco de todo eso…

Thursday, September 24, 2009 5:15 PM by Carlos

Buen post, pero una sugerencia cambia la forma de publicar código(colores), es ilegible.

Salu2!

# re: Unity, Proxies, AOP y un poco de todo eso…

Thursday, September 24, 2009 5:19 PM by Eduard Tomàs i Avellana

@Carlos

Gracias por tu comentario... buscaré una manera alternativa para poner el código... la verdad es que uso un plugin para Writer que te pone los mismos colores que el VS... y así tengo configurado el VS, con el fondo negro. Visto así en el blog, la verdad es que pierde bastante... :(

Un saludo!

# AOP para interceptar métodos estáticos

Thursday, February 11, 2010 1:06 PM by Rodrigo Liberoff

Hola, he estado trabajando con AOP y me encuentro con el siguiente limitante: que no puedo interceptar métodos estáticos. ¿Alguna sugerencua o experiencia que puedas compartir?

MIL GRACIAS :)

# re: Unity, Proxies, AOP y un poco de todo eso…

Thursday, February 11, 2010 4:39 PM by Eduard Tomàs i Avellana

MMMmmm...

Pues diria que con Unity no es posible interceptar métodos estáticos.

Fuera de Unity... echale un vistazo a www.codeproject.com/.../LinFuPart6.aspx

A ver si es lo que necesitas!! ;)

Un saludo!