Novedades de Unity 2.0

Unity, el contenedor IoC de Microsoft, hace algunas semanas que tiene nueva versión: la 2.0. Y viene con algunas novedades interesantes respecto a la versión anterior, que os comento brevemente 🙂

Por fin… un único assembly!

Vale que Unity era poco más que un wrapper sobre ObjectBuilder2 pero tampoco era necesario que nos lo recordaran continuamente… ahora ya no tendremos que añadir la referencia a ObjectBuilder2 además de la referencia a Unity cada vez… Ahora existe un solo assembly: Microsoft.Practices.Unity.dll. ¿Un detalle sin apenas importancia? Pues probablemente 🙂

Resoluciones deferidas

Esta funcionalidad nos permite resolver tipos con Unity sin tener una dependencia contra Unity. P.ej. imagina que tenemos Unity configurado de la siguiente manera:

IUnityContainer uc = new UnityContainer();
uc.RegisterType<IA, A>();

Y tenemos un método que necesita construir un objeto IA a través de Unity… Antes debíamos pasarle el contenedor Unity:

static void OldFoo(IUnityContainer ctr)
{
// ... Código antes de resolver IA ...
IA a = ctr.Resolve<IA>();
}

Ahora, con Unity 2.0 podemos obtener un resolvedor (perdonad por la palabreja) que no es nada más que un delegate que cuando sea invocado llamará a Resolve de Unity. Esto nos permite que la función OldFoo no tenga dependencia alguna contra Unity:

static void NewFoo(Func<IA> resolver)
{
// ... Código antes de resolver IA ...
IA a = resolver();
}

Y la llamada a NewFoo quedaría de la forma:

var resolver = uc.Resolve<Func<IA>>();
NewFoo(resolver);

De esta manera el método NewFoo no necesita para nada el contenedor Unity 🙂

Paso de parámetros en Resolve

Esto permite pasar parámetros a un objeto cuando se llama a su método Resolve.

Supon que tenemos lo siguiente:

interface IB { }

class B : IB
{
public B(int value) { }
}

Y tenemos un mapping declarado en Unity:

IUnityContainer uc = new UnityContainer();
uc.RegisterType<IB, B>();

Como es de esperar la siguiente llamada falla:

IB b = uc.Resolve<IB>();
// Exception is: InvalidOperationException - The type Int32 cannot be constructed.

Unity se queja, porque cuando va al constructor de la clase B se encuentra que le debe pasar un parámetro de tipo Int32 (int). Pero como no tiene ningún mapeo de int, se queja.

Para solucionar este caso podemos usar los ResolverOverride. P.ej. para pasarle el valor 3 al parámetro “value” basta con usar:

IB b = uc.Resolve<IB>(new ParameterOverride("value", 3));

Pero no solo nos sirven para especificar valores de parámetros cuyos tipos no estén registrados en el contenedor, también podemos indicarle valor a un parámetro incluso en el caso que Unity pudiese proveer un valor para el parámetro:

interface IA { }
class A : IA { }
interface IB { IA A { get; } }
class B : IB
{
public IA A { get; private set; }
public B(IA a)
{
A = a;
}
}

class Program
{
static void Main(string[] args)
{
IUnityContainer uc = new UnityContainer();
uc.RegisterType<IA, A>(new ContainerControlledLifetimeManager());
uc.RegisterType<IB, B>();
IA a1 = uc.Resolve<IA>();
IA a2 = uc.Resolve<IA>();
IB b = uc.Resolve<IB>();
IB b2 = uc.Resolve<IB>(new ParameterOverride("a", new A()));
}
}

Dado el siguiente código tenemos que:

  1. a1 == a2 es true puesto que IA está registrado como singleton, por lo que todas las llamadas a Resolve<IA> devuelven el mismo valor
  2. a1 == b.A es true por la misma razón de antes: Cuando Unity debe proporcionar un valor IA al constructor de la clase B, utiliza el método Resolve por lo que devuelve el singleton.
  3. a1 == b2.A es false porque aquí aunque Unity puede proporcionar un valor para el parámetro, el valor especificado en el ParameterOverride tiene preferencia.

Por fin!!! Podemos saber que hay registrado en el contenedor

Se han añadido métodos para saber todos los registros de mapeos del contenedor y para saber si un mapeo en concreto existe:

IUnityContainer uc = new UnityContainer();
uc.RegisterType<IA, A>(new ContainerControlledLifetimeManager());
uc.RegisterType<IB, B>();
bool isTrue = uc.IsRegistered<IA>();
bool isFalse = uc.IsRegistered<IC>();
int numRegs = uc.Registrations.Count();

Por cierto, que dado el siguiente código… cuanto vale numRegs?

Sí habeis dicho 2, habéis fallado… recordad que Unity se registra a si mismo, por lo que realmente numRegs vale 3 (el propio registro de Unity, el de IA y el de IB).

InjectionFactory

InjectionFactory es un mecanismo que permite indicarle a Unity un método (una Func<IUnityContainer, object>, usualmente una lambda expresion) a usar cada vez que deba resolver un objeto especificado. Permite que Unity use factorías nuestras.

Imagina que tenemos una factoría para crear objetos IA:

interface IFactory
{
IA GetNewInstance();
}
class Factory : IFactory
{
public IA GetNewInstance()
{
// Hace lo que tenga que hacer nuestra factoria...
return new A();
}
}

Y ahora deseamos usar Unity para la creación de objetos IA. Pues podemos realizar el siguiente Register:

IUnityContainer uc = new UnityContainer();
uc.RegisterType<IFactory, Factory>(new ContainerControlledLifetimeManager());
uc.RegisterType<IA>(new InjectionFactory
(x => x.Resolve<IFactory>().GetNewInstance()));

En la última línea le estamos indicando a Unity que cada vez que alguien haga un Resolve<IA> ejecute el delegate que le indicamos (en este caso que obtenga una IFactory y llame al método GetNewInstance).

Esto si lo combinamos con lo de las resoluciones diferidas es realmente interesante.

Y estas serían las novedades, a mi juicio, más interesantes de Unity 2 (que no son todas… si las queréis saber todas, las tenéis aquí!).

3 comentarios sobre “Novedades de Unity 2.0”

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *