Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Resumen
Veremos como interceptar llamadas a métodos de una capa de servicios en C# para
implementar funcionalidad común, como el manejo de logs y las transacciones contra la base
de datos.
Tags
AOP, C#, ContextBoundObject. ContextAttribute, Attribute, IMessageSink, IMessage,
IMethodMessage
Si nuestra aplicación posee un Service Layer[1], es probable que repitamos tareas en varios servicios.
Por ejemplo, el manejo de las transacciones de la base de datos y el registro de la invocación del
servicio en un log. Dichas operaciones llevan a tener código duplicado en distintos servicios. Veremos
una forma de evitar dicha duplicidad aplicando algunos conceptos de AOP en C#.
Supondremos un servicio para actualizar clientes que recibe un DTO [2] con la información que se
desea actualizar, podemos tener algo de la siguiente manera:
Ya teniendo tres o cuatro servicios que se ejecuten dentro de una transacción podemos notar que existen
varias lineas de código que tienen que ver con el log y el manejo de transacciones que se repetirán y
nos llevaran a cometer el pecado tener código duplicado. Utilizar un template method para reducir las
líneas de código duplicadas no parece ser una solución que podamos aplicar en este caso. Tendremos
que optar otra estrategia.
Es aquí donde utilizar algunas ideas de la programación orientada a aspectos (AOP) puede resultarnos
de utilidad: tener un aspecto que se encargue de la funcionalidad común. Queremos lograr que al
invocar un método de un servicio automáticamente se registre dica invocación en el log y, si se le
indica explicictamente, manejar una transaccion contra la base de datos.
El primer paso de nuestra implementación será indicar que métodos deben ejecutarse dentro de una
transacción. Para ello los decoraremos mediante el atributo que se verá asi:
[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : Attribute
{
Luego codificaremos la clase ServiceMessageSink que sera la responsable de interceptar todos los
mensajes (invocaciones a metodos en nuestro caso) que se le envíen a nuestro servicios. Para poder
implementar lo que deseamos haremos que esta clase implemente la interfaz IMessageSink que nos
permitirá acoplarnos al mecanismo que nos provee el framework para interceptar la llamada.
Según la documentacion de .NET [5] la llamada a un método que se realiza a través de un proxy
atraviesa una cadena enlazada de objetos que implementan la interfaz IMessageSink antes de llegar al
objeto real. El mensaje se encapsula en un objeto del tipo IMessage y en particular nos interesan
aquellos que sean del tipo IMethodMessage. Esta ultima interfaz nos proveera una forma de saber el
nombre del método y los atributos que lo decoran mediante reflection.
if (methodMessage != null)
{
Loguear Invocacion Servicio
}
//Invoco al método
IMessage m = this.nextSink.SyncProcessMessage(msg);
Commit de la transaccio
return m;
}
catch (System.Exception e)
{
Rollback de la transaccio
throw;
}
finally
{
Finalizo la transaccio
}
}
return this.nextSink.SyncProcessMessage(msg);
}
#endregion
}
Se fijará si el metodo que invocamos esta decorado con el atributo Transaction que vimos
anteriormente.
Para que esto funcione primero debemos declarar otro atributo llamado TransactionAwareService para
decorar a aquellas clases cuyos métodos serán interceptados. Deberá heredar de ContextAttribute e
implementará la interfaz IContextObjectSink que nos demandará completar el método GetObjectSink
para asociar la clase a la cual decora con el IMessageSink que acabamos de codificar.
[AttributeUsage(AttributeTargets.Class)]
public class TransactionAwareService : ContextAttribute, IContributeObjectSink
{
public TransactionAwareService() : base("Transaction") { }
#endregion
}
Por ultimos heredaremos todas nuestras clases que implementen servicios de una clase base. La
llamaremos BaseService, decoraremos esa clase con el atributo TransactionAwareService y por último
haremos heredar la misma de ContextBoundObject para utilizar el mecanismo de proxys transparentes
que mencionamos anteriormente. La clase nos quedará así
[TransactionAwareService]
public class BaseService : ContextBoundObject
{
}
De esta forma logramos interceptar llamadas a los métodos de nuestros servicios de forma transparente
y con pocas lineas de codigo utilizando las capacidades de remoting del framework. Logramos asi
ahorrarnos unas cuantas lineas de código que de otra manera estarían condenada a ser repetidas en los
distintos servicios. Si el día de mañana cambia algo relacionado con las transacciones y el loggin será
un solo lugar el que haya que mantener.
Referencias
[1] http://martinfowler.com/eaaCatalog/serviceLayer.html
[2] http://en.wikipedia.org/wiki/Data_Transfer_Object
[3] http://es.wikipedia.org/wiki/Template_Method_(patrón_de_diseño)
[4]http://en.wikipedia.org/wiki/Proxy_pattern
[5]http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.imessagesink.aspx