Inversión de control con Unity


Inversión de Control con Unity

La inyección de dependencias es una técnica para implementar aplicaciones desacopladas. Provee de un sistema para manejar dependencias entre objetos.
En un diseño arquitectónico de aplicación altamente escalable, es imprescindible que las capas que compongan esta arquitectura estén completamente desacopladas. También es importante que los objetos tengan un tiempo de vida definido, lo más corto posible. En ayuda de estas necesidades, los chicos de Patterns &Practices de Microsoft, han implementado Unity. Digamos que Unity es a .Net como Spring es a Java.
Además de las utilidades descritas, Unity también nos permite “mockear” implementaciones con objeto, por ejemplo, de tests.
Básicamente hay dos patrones clásicos en Inversión de Control: La Inyección de Dependencias o Dependency Injection y la Localización de Servicios o Service Locator. Ambas las veremos más adelante.

Container y configuración base.

En Unity (como en otros sistemas de Ioc) los mapeos entre interfaces e implementaciones se estructuran en Containers, indicando, para cada interfaz, cuál es su implementación, cuál es su nombre (opcional) y cual es su tiempo de vida (Lifetime).
Podemos tener un container (o varios) padre, y colgando de él uno (o varios) containers hijos. Este tipo de estructura no vamos a analizarlo en este artículo, nos contentamos con usar un único container.
Para usar Unity en nuestra aplicación, creamos una sección nueva en web.config (o app.config):

<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

Y después la configuramos

<unity configSource="bin\UnityContainer.config">
</unity>

Y añadimos una nueva Key para indicar el subcontenedor que vamos a usar en esta aplicación

<appSettings>
<add key="UnityContainer" value="APPContainer" />
</appSettings>

Como se puede apreciar, el archivo del container es un fichero XML que describiremos más adelante. También más adelante explicamos cómo usar un container directamente desde código manejado .NET, como mínimo para que, durante el tiempo de desarrollo, los errores sintácticos nos aparezcan al compilar.

LifeTimeManager.

Es el tipo de instanciación de clase que se realizará.

  • TransientLifetimeManager:
<lifetime type="transient"/>
  • Este LifetimeManager asegura que las instancias de objetos se crean nuevas cada vez (no se reutilizan entre diferentes llamadas).
    
    
  • PerResolveLifetimeManager:
<lifetime type="perresolve"/>
  • Asegura que las instancias de un tipo de objeto se reutilizan a lo largo de todo un grafo de dependencias de objetos creados por Unity.
  • ContainerControlledLifetimeManager:
<lifetime type="singleton"/>
  • Implementa un comportamiento “singleton” para cada objeto creado. El contenedor sí mantiene referencias a los objetos creados y se hace un dispose automático cuando se hace un dispose del contenedor.
  • ExternallyControlledLifetimeManager:
<lifetime type="external"/>
  • Este LifetimeManager mantiene una referencia débil a su instancia manejada. Es decir, implementa un comportamiento “singleton” pero el contenedor no mantiene una referencia al objeto que deberá ser eliminado (disposed) cuando se salga del ámbito.
  • PerThreadLifetimeManager:
<lifetime type="perthread"/>
  • Reutiliza una única instancia de cada clase por thread que accede al grafo de objetos de dependencias creados por Unity.
  • HierarchicalifetimeManager:
<lifetime type="hierarchical"/>
  • Implementa un comportamiento singleton, pero los contenedores hijos con comparten instancias con los contenedores padres.

Implementación.

En el punto anterior hemos visto qué tag hemos de añadir en el archivo de container de Unity para cada una de las implementaciones. Este sistema está bien en entornos de deploy, es decir, aplicaciones terminadas. Sin embargo, en tiempo de desarrollo, puede ser más cómodo implementar los containers en código, para obtener en tiempo de compilación los errores (escritura, etc).
Para registrar tipos en un container, se utiliza una sintaxis como esta:

container.RegisterType<IPaisesRepository, PaisesRepository>(new TransientLifetimeManager());

En la que estamos indicando, a un container en concreto (container), con qué implementación (PaisesRepository) debe resolver una interfaz (IPaisesRepository) y qué tipo de instanciación usar (TransientLifetimeManager).
Este registro, si lo queremos transformar más adelante en un archivo XML, se instanciaría de la siguiente manera:

Como hemos indicado antes, lo adecuado sería implementar en código los containers necesarios y, cuando llegue el momento de pasar a producción, crear los containers en formato XML. De este modo tenemos lo mejor de los dos mundos, por un lado, en tiempo de desarrollo los errores los detecta el compilador y, por otro lado, en tiempo de ejecución en producción obtenemos desacoplamiento completo.
En la mayoría de los casos, la forma normal de instanciación será la del ejemplo, es decir TransientLifetimeManager que supone un objeto único por cada referencia. Esto es especialmente útil en implementaciones SOA (incluso en WOA), pues los objetos tienen una vida muy corta (la necesaria para contestar las peticiones recibidas).

Dependency Injection (DI).

Una vez instanciado el container, la manera de resolver las implementaciones de las interfaces puede ser variada. Mediante la inyección de la dependencia en el constructor, Unity se encarga de instanciar el objeto de la clase referenciada. Por ejemplo:

 public class PaisesController : Controller
 {
 private IPaisesRepository _paisesRepository;

public PaisesController(IPaisesRepository paisesRepository)
 {
 _paisesRepository = paisesRepository;
 }

[Authorize]
 public ActionResult Lista(int? page, string sort)
 {
 ViewBag.RowCount = _paisesRepository.GetCount();
 var startPage = 0;
 if (page.HasValue && page.Value > 0)
 {
 startPage = page.Value - 1;
 }
 var result = _paisesRepository.GetPaged(startPage, 10, p => p.NombrePais, true);
 return View(result);
 }

Como se puede observar, en el constructor le indicamos el interfaz que debe cumplir el objeto que debe instanciar Unity. A partir de la construcción de PaisesController, ya podemos usar el objeto _paisesRepository sin tener que hacer ningún NEW.

Dependency Lookup (Service Locator).

Unity también nos permite resolver en tiempo de ejecución. Por ejemplo:

var actual = container.value.Resolve<IShortLivedObject>();

Además, también nos permite tener diferentes implementaciones registradas para el mismo interfaz y decidir, en tiempo de ejecución, cuál de estas implementaciones usar:

var actual = container.value.Resolve<IShortLivedObject>("implementacion1");

Por último (en este artículo), voy a hablar de la Inyección de Factorías. Es posible, que para resolver un objeto determinado, necesitemos un método. En este caso, podemos usar el InjectionFactory().
Por ejemplo, dada la siguiente factoría para crear objetos del tipo IComplexService:

interface IComplexServiceFactory
 {
 IComplexService GetNewInstance();
 }
 class ComplexServiceFactory : IComplexServiceFactory
 {
 public IComplexService GetNewInstance()
 {
 IComplexService complexService = new ComplexService();
 // Do required actions by our factory...
 complexService.Initialize("dato 3", "dato 4", "dato 5");
 return new complexService();
 }
 }

Para poder usar Unity en la resolución de este tipo, primero tendríamos que registrarlo:

container.RegisterType(new ContainerControlledLifetimeManager ());
 container
 .RegisterType(new InjectionFactory(x => x.Resolve()
 .GetNewInstance()));

Para resolver, finalmente, lo haríamos como siempre:

IComplexService complexService = IoCFactory
 .Instance
 .CurrentContainer
 .Resolve();

Google
Visita mi perfil en Google Plus

Anuncios

2 comentarios en “Inversión de control con Unity

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s