sábado, junio 27, 2009

Sistema de plugins en C# usando Reflexión

En este post os voy a describir un pequeño sistema de plugins que implementé para un proyecto y que me resultó de mucha utilidad y sobre todo, me permitió aumentar el sistema sin tener que cambiar nada de lo ya establecido (códigos, recompilaciones, etc.).

La idea es disponer de un sistema de plugins para un CMS (gestor de contenidos) de forma que cada plugin sea autogestionado y no haya que tocar nada del sistema a la hora de introducir un nuevo plugin, salvo lógicamente introducir su definición en base de datos. En el proyecto que implementé, los plugins eran elementos visuales que permitían realizar la maquetación de las páginas mostrando el contenido de base de datos de diferentes formas (contenido tabulado, histórico, contenido con scroll, publicidad, etc.).



No voy a entrar en cómo implementar toda la estructura del sistema de elementos visuales (será para un artículo posterior), lo importante es cómo podemos, acceder a clases de c# sin utilizar elementos tipados. Es decir, lo normal para instanciar un objeto es utilizar la siguiente nomenclatura:

TipoObjeto miObjeto = new TipoObjeto(parametros);

En el caso de un sistema dinámico de plugins, no podemos utilizar esa nomenclatura porque no sabemos a priori la clase del objeto a utilizar. En esta situación, la reflexión se nos ofrece como técnica perfecta.

Implementación del sistema de plugins

Los plugins se dan de alta en base de datos, cada proyecto puede utilizar su propia definición de tablas. En mi caso, creé dos tablas, una para guardar la definición del elemento y otra para almacenar las propiedades modificables del elemento. Por ejemplo, uno de los plugins era para introducir un bloque de código HTML directamente en la página. La definición de este elemento es:


y la definición de sus propiedades es:


Lo importante de los datos almacenados para cada elemento es el campo "clase" en el que se guarda la clase de código que implementa la funcionalidad de este elemento. En este caso, la clase es plugins.elementoHtml. Este campo se utiliza luego para la reflexión.

Interfaz de los plugins
Este paso no es necesario pero es bueno que todas las clases que se encarguen de plugin implementen una interfaz obligando a que el método público de acceso sea siempre igual y no haya fallos de parámetros. En mi caso, la interfaz definía los siguientes métodos:

La interfaz define dos métodos, uno para obtener el código html del plugin cuando se debe insertar en el centro de la página y otro para cuando se debe insertar en el lateral de la página. Los parámetros que se pasan son el id particular para cada elemento y un object extras con parámetros extras que hicieran falta en cada caso.

El quid de la cuestión

En cada proyecto, obtendremos referencias a nuestros plugins de la manera que queramos. El quid de la cuestión se encuentra en las líneas que utilizan la reflexión para acceder a los métodos de la clase de cada plugin:




Lo que hacemos es:
  1. Obtener el campo "clase" de la base de datos y llamar al método Type.GetType para obtener el tipo del elemento.
  2. A través de reflexión podemos obtener acceso al constructor e invocarlo pasándole los argumentos necesarios, en este caso null porque no hay argumentos.
  3. Obtener la información del método "Renderizar" que es el que utilizamos de cada clase y que define la interfaz.
  4. Invocamos el método pasándole el objeto inicializado mediante el constructor y le pasamos un vector de objects con los parámetros que el método está esperando.
  5. El resultado del método (en este caso un string), se obtiene haciendo un cast a string.
Conclusión
La reflexión es una herramienta muy potente dentro del lenguaje .net que permite llamar a métodos y objetos sin conocer previamente el tipo. Esto permite generar sistemas como el descrito de una forma eficiente. El problema estriba en que, inicialmente no es un tipo de programación sencilla y requiere un tiempo para acostumbrarse a cómo funciona.

Espero que os haya gustado este artículo y que os sirva esta idea para vuestros desarrollos. En mi caso, me ha sido de mucha utilidad.




1 comentario:

Kiquenet dijo...

Hola,

algún ejemplo real con código fuente ? quizá de Microsoft ??

Saludos y gracias.