jueves, 24 de junio de 2010

WCF RIA services + inyección de dependencias

Tengo que confesarlo: soy un fan del patrón inyección de dependencias. Y también me gusta bastante la tecnología WCF RIA services, que estoy usando ahora en un proyecto. Problemilla: ambas cosas no casan muy bien a priori, puesto que a no ser que se indique lo contrario, las clases que proporcionan los servicios RIA (las clases derivadas de DomainContext) son instanciadas automáticamente por el motor de ASP.NET, asumiento que tienen un constructor sin parámetros. Pero claro, la inyección de dependencias se consigue precisamente pasando las dependencias como parámetros del constructor (hay otras formas, pero tampoco casan muy bien con la creación automática de la clase).

Bueno, ¿y cómo "se indica lo contrario"? La solución está explicada en esta entrada de StackOverflow: básciamente se trata de decirle a la clase DomainService que use una factoría personalizada cada vez que necesite una instancia del servicio, cosa que se hace desde el constructor estático se la clase Global de ASP.NET. Esa factoría personalizada es una clase que proporcionamos nosotros y desde la que podemos crear la instancia del servicio a nuestro gusto.

Veamos pues cómo extender esta idea para usar inyección de dependencias en nuestros servicios RIA.

El proyecto

He creado un solución de Visual Studio sencilla de ejemplo para ilustrar el concepto. Se compone de dos proyectos Silverlight (la aplicación principal y una biblioteca de clases para el acceso a los servicios), más un proyecto web (que aloja la aplicación Silverlight) y un proyecto de bliblioteca de clases que aloja el servicio (derivado de DomainService). La idea es usar inyección de dependencias para suministrar una capa de acceso a datos a la clase que implementa el servicio.

La solución tiene tal que este aspecto:


En enlace RIA está establecido entre el proyecto Client.Services de la parte cliente y el proyecto Services de la parte servidor, quedando pues las dependencias entre proyectos tal que así:


Además de las dependencias entre proyectos, es necesario añadir un par de referencias más al proyecto MyRiaApp.Web:
  • System.ServiceModel.DomainServices.Server.dll, necesario para poder acceder a la clase DomainService.

  • Microsoft.Practices.Unity.dll, se trata de Unity, el inyector de dependencias de Microsot, que es por cierto el que se usa en el proyecto.
El proyecto MyRiaApp.Services expone un servicio RIA llamado MyRiaDomainService que contiene métodos expuestos al cliente por medio del enlace RIA. Para simplificar, en este proyecto de ejemplo sólo se expone un método, que devuelve un entero con un contador de elementos que supuestamente son objetos útiles para la aplicación. Este es el código del servicio:
Tal como he mencionado al principio, el servicio usa una capa de datos para aislar el acceso al motor de persistencia (rimbombante nombre para lo que es la base de datos), representado por un interfaz llamado IDataAccessLayer cuyo código es este:

Un posible esqueleto de implementación de IDataAccessLayer podría ser este:

Estamos usando el mecanismo de inyección de dependencias con la clase MyRiaDomainService, a la que pasamos una instancia de IDataAccessLayer en el constructor; y también con la clase IDataAccessLayer, a la que pasamos la cadena de conexión. Ahora se trata de hacer que ASP.NET use el inyector de dependencias (Unity, en este caso) para resolver las idems a la hora de crear instancias del servicio.

Inyectando

Para conseguir tal cosa, añadiremos al proyecto web una clase Global.asax en la que pegaremos el siguiente código (resalto las partes más interesantes):

¿Qué hemos hecho aquí? Pues un par de cosas:
  • Le hemos dicho a ASP.NET que use la clase MyAppDomainServiceFactory cada vez que necesite crear una instancia de un servicio RIA.

  • Hemos creado una instancia de UnityContainer, que queda disponible para toda la aplicación por medio de Global.DependencyInjector.

  • Hemos creado una instancia de DataAccessLayer, que hemos registrado en el inyector de dependencias.

  • Hemos configurado MyAppDomainServiceFactory para que obtenga las instancias de los servicios a partir del inyector de dependencias.
De esta forma, cada vez que se requiera una instancia de un servicio, será el inyector de dependencias quien lo proporcione, encargándose de resolver cualquier dependencia existente (la dependencia de IDataAccessLayer en MyRiaDomainService, en este caso.)

Nótese que una ventaja adicional de este sistema es que funcionará incluso con servicios que no tengan ninguna dependencia. Por ejemplo, en paralelo a MyRiaDomainService tenemos un AuthenticationDomainService que sólo tiene un constructor sin parámetros. Unity será capaz de crear una instancia de dicho servicio sin problemas, simplemente no le inyectará ninguna dependencia.

Un apunte más. Estamos obteniendo la cadena de conexión para la capa de datos a partir del archivo de configuración Web.config (estamos haciendo una "inyección de dependencias manual" en este caso). Pego aquí el contenido de dicho archivo (la cadena de conexión es un ejemplo, claro), que de paso muestra los elementos necesarios para que funcionen los servicios RIA:

Conclusión

El patrón inyección de dependencias ayuda a conseguir código legible y mantenible. Ahora ya sabes cómo usarlo en conjunción con los servicios RIA, lo cual sin duda incrementará tu felicidad en varios enteros, o no.

0 puesyocreoques: