viernes, 27 de agosto de 2010

Pseudo-AOP usando delegados en C#

Estaba yo tan feliz programando un servicio en WCF y me di cuenta de que en todos los métodos de servicio que escribía estaba repitiendo el mismo esquema de código una y otra vez: "Ejecuta todo el método en un bloque try-catch, y si algo va mal, escribe una traza del error y devuelve null (o cero, o el valor por defecto adecuado)". Decidí entonces que debía hacer algo al respecto, primero porque estaba violando el principio DRY (algo inadmisible para alguien que programa con educación), y segundo por vagancia (si se puede ahorrar aunque sea un simple copypaste, hay que hacerlo).

Encontré una solución muy mona que se basa en el uso de un método auxiliar que se encarga del trabajo sucio (capturar la posible excepción y escribir la traza en ese caso) más un delegado que contiene el código que queremos ejecutar realmente. El resultado es algo que, visto de lejos y en una noche sin luna y con niebla, se parece remotamente a la Programación Orientada a Aspectos. Ahora vamos a ello.

Supón que tienes un método (expuesto al exterior por medio de un servicio WCF en este caso, pero no tiene por qué ser así) tal que este:

Bien, quieres añadir control y traza de errores de forma genérica, es decir de forma que no tengas que escribir el mismo código para otros métodos similares. ¿Cómo podemos conseguir esto?

La solución que yo uso pasa por definir un método auxiliar llamado ExecuteMethod, que en su forma más básica es algo así:

Entonces modificamos la firma de nuestro método de servicio de forma que pasa a ser privado, y creamos un método nuevo que invoca a ExecuteMethod pasando el método original como parámetro. Se entiene mejor viendo el código directamente:

Nótese cómo usamos una expresión lambda para pasar de forma limpia y elegante (¡oh, ah!) un delegado que apunta a nuestro método original.

Ahora, podemos aplicar esta técnica de control a todos los métodos que queramos, por ejemplo:


La gracia de esta técnica es que podemos cambiar nuestra estrategia de control de errores y traza cambiando sólo el método ExecuteMethod. Por ejemplo, podemos decidir escribir trazas de error usando el método Debug.Write en un principio, para más adelante usar el registro de eventos de Windows, quedando entonces el método algo así:


También podemos decidir volver a lanzar la excepción en vez de devolver el valor predeterminado del tipo de retorno, o actuar de forma distinta según el tipo de excepción... en fin, las posibilidades son infinitas, o casi.

Para terminar, he aquí la versión de ExecuteMethod para invocar métodos de tipo void (es decir sin valor de retorno):

Esta versión se invoca de la misma forma que la anterior, excepto por supuesto que no es necesaria la palabra return:

En resumen, como dicen por ahí: se non è AOP, è ben trovato.

1 puesyocreoques:

pablo dijo...

Gracias por el post y yo te pregunto, ¿Qué pasa si la firma de todas las operaciones del contrato tiene la misma firma?, ¿podemos pasar como parámetro al servicio un delegado con el nombre de la función seleccionada?,¿Cómo lo harías?.
Supón el ejemplo de siempre, dos operaciones, suma de dos enteros y multiplicación de dos enteros, poder solicitar el servicio cono (suma,2,5) o(milti,2,5).
Gracias de antemano, tanto por el Blog como por todo lo demas