Unity es un contenedor de inyección de dependencias que facilita el desarrollo de aplicaciones proporcionando un sistema para gestionar tanto la construcción de objetos como las dependencias entre los diferentes componentes de una aplicación.
Supongamos que hemos definido un interface a través del cual gestionaremos todo el sistema de registro de eventos de nuestra aplicación.
public interface ILoggingService
{
void Log(string message);
}
Y a su vez una clase que implementa dicho interface y que en este caso simplemente enviara el mensaje a la consola del sistema.
public class ConsoleLoggingService: ILoggingService
{
public void Log(string message)
{
Console.WriteLine("Console: " + message);
}
}
El funcionamiento básico del contenedor es sencillo, en primer lugar registraremos en el contenedor los tipos de objetos que deseamos que gestione. Al hacerlo, además, indicaremos al contenedor cual debe ser el ciclo de vida del objetos de ese tipo. En el caso que nos ocupa registraremos el interface ILoggingService en el contenedor mapeando dicho interface al tipo ConsoleLoggingService de tal forma que cuando solicitemos al contenedor un objeto implementando el interface ILoggingService nos devuelva un objeto del tipo ConsoleLoggingService. Como además deseamos que en la aplicación solo exista una única instancia del sistema de eventos (Singleton), indicaremos al contenedor que se encargue de controlar la vida del objeto, de esta forma la primera vez que solicitemos el interface se creará el objeto pero las veces sucesivas se nos devolvera una referencia al mismo objeto. El siguiente fragmento de código muestra el proceso de registro utilizando uno de los métodos que nos brinda el contenedor.
IUnityContainer container = new UnityContainer();
container.RegisterType<ILoggingService,
ConsoleLoggingService>(new ContainerControlledLifetimeManager());
Veamos como funciona la inyección de dependencias. El contenedor nos proporciona varias formas de realizar la inyección de dependencias: a través del constructor, a través de una propiedad ó a través de un método. En el caso general, las inyección requiere que marquemos el constructor, la propiedad o el método con un atributo que indicará al contenedor que debe realizar la inyección de un objeto. El siguiente fragmento de código muestra la utilización del atributo InjectionConstructor para indicar al contenedor que debe inyectar un objeto con el interface ILoggingService a la hora de construir un objeto del tipo MainApplicationModule.
public class MainApplicationModule
{
ILoggingService mLoggingService;
[InjectionConstructor]
public MainApplicationModule(ILoggingService loggingService)
{
mLoggingService = loggingService;
mLoggingService.Log("Inicializado el módulo principal de la aplicación");
}
}
Nótese que en este caso, al existir solo un constructor, el contenedor inyectará las dependencias en el constructor de forma autómatica aunque no hayamos marcado el constructor, sin embargo, dicho atributo es útil para indicar que constructor utilizar en el caso de que existan varios. Si tenemos una clase con varios constructores y ninguno de ellos está marcado con el atributo de inyección, el contenedor utilizará aquel que tenga más parámetros, lanzando una excepción en el caso de que exista más de un constructor que cumpla la propiedad de tener el máximo número de parámetros.
1 static void Main(string[] args)
2 {
3 using (IUnityContainer container = new UnityContainer())
4 {
5 container.RegisterType<ILoggingService,
6 ConsoleLoggingService>(new ContainerControlledLifetimeManager());
7 MainApplicationModule mainModule = container.Resolve<MainApplicationModule>();
8 Console.ReadLine();
9 }
10 }
En la linea 7 del anterior fragmento de código, solicitamos al contenedor que nos devuelva un objeto del tipo MainApplicationModule. Obsérvese que en este caso no ha sido necesario registrar el objeto con el contenedor previamente. La utilidad de crear un objeto a través del contenedor de un tipo que no ha sido previamente registrado es que el contenedor evaluará e inyectará las dependencias de dicho objeto al generarle.
Después de la ejecución de la linea 7 aparecerá en la consola del sistema la siguiente línea indicando que se ha realizado el proceso de inyección de la dependencia ILoggingService correctamente:
Console: Inicializado el módulo principal de la aplicación
Las ventajas de la utilizacion del patron de diseño de inyección de dependencias se hacen evidentes a médida que el número de componentes de la aplicación aumenta, permitiendonos mantener un acoplamiento débil entre dichos componentes y facilitando el mantenimiento posterior de la aplicación al unificar el sistema de construcción y cableado de objetos.
Puede que surja la pregunta de que tipo de objetos debemos instanciar a través del contenedor. En mi experiencia, los componentes que forman parte de la arquitectura o flujo principal de la aplicación, esto es, servicios, vistas, presentadores, módelos, etc, son buenos candidatos para ser incluidos en el contenedor.
Finalizaremos este artículo enumerando dos ventajas instantaneas de utilizar un contenedor para estructurar nuestra aplicación:
- Si en lugar de mostrar los eventos del sistema por la consola decidimos registrarles en una base de datos, podríamos definir un nuevo tipo DatabaseLoggingService implementando el interface ILoggingService y registrar dicho tipo en lugar de ConsoleLoggingService. Con esa sencilla modificación la aplicación pasaría a registrar los eventos en una base de datos.
- El contenedor introduce el concepto de 'cadena de montaje' y nos permite extender las acciones a realizar a la hora de crear un objeto. Un uso típico es el 'EventBroker' o sistema de gestión de eventos que permite marcar eventos de los tipos con el atributo de publicación y métodos con el atributo de suscripción, encargandose el contenedor a la hora de construir los objetos de cablear los publicadores con los suscriptores facilitando la implementación del patron de diseño observador.
Hola, gracias por el Post,
Hola, gracias por el Post, quisiera saber si puedo y ¿como? ocupar el Unity en ambiente Web, especificamente quiero "compartir" en string de coneccion a la base de datos o otros datos de la aplicación entre las clases de acceso a datos pero no entre sesiones de usuario.
El tema es que pasa con las sesiones y el singleton?, que pasa con el Thread Safe?.
La verdad es que he visto y buscado mucho, mi meta es como hacer de la mejor forma posible, utilizando el EnterpriceLibrary y Linq, una aplicacion Web en n capas (3 a lo menos). De sobremanera me interesa como manejar la capa de datos ahora que esta lo del linq.
Supongo que podrías almacenar
Supongo que podrías almacenar el contenedor en el diccionario del estado de aplicacion de ASP.NET accesible a través de la propiedad 'Application' recreandole de nuevo cuando fuera necesario. De todas formas, si lo que necesitas es simplemente almacenar un objeto (o varios) para compartirles dentro de toda la aplicación, podrías utilizar ese mismo diccionario para hacerlo.
Hasta donde yo sé el contenedor no es 'Thread safe' por lo que tendrías que realizar la sincronización tú mismo donde fuera necesario o adaptar el contenedor.
Respecto a la última parte, echa un vistazo, si es que no lo conoces ya, al ASP.NET MVC.
Gracias
Muy bueno el articulo pero desde la perpectiva de usuaria me gustaria que explicaras que es lo que hace los métodos RegisterType y Resolve(Devuelve una instancia concreta del tipo que está registrado me parece).Si pudieras explicarlo.Y si es necesario para la implementacion de ese ejemplo incluir algun espacio de nombres como System.ComponentModel.
De antemano gracias...
Dunia.
En general registraremos con
En general registraremos con el contenedor los tipos de objetos que queremos gestionar a través de él y utilizaremos Resolve para obtener un instancia concreta de dichos tipos como tú dices.
Al utilizar Resolve, el contenedor construirá un nuevo objeto de ese tipo, resolviendo a su vez todas las dependencias que pudiera tener dicho objeto.
También podemos, como en el ejemplo del artículo registrar un tipo como Singleton, de tal forma, que al llamar a Resolve por primera vez se cree el objeto y las siguientes veces se nos devuelva el mismo objeto siempre.
Espero haber resuelto tus dudas. Un saludo.
Gracias por la aclaración y
Gracias por la aclaración y disculpa la molestia ,la duda fue aclarada...Y la respuesta fue bastante rapida.
Salu2,
Dunia.