Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 14 de mayo de 2013
ASP.NET MVCHasta ahora, los desarrolladores que queríamos usar Unity con ASP.NET MVC teníamos que recurrir a triquiñuelas o componentes no oficiales, como Unity.MVC3. No es que fuera especialmente incómodo ni que tuviera contraindicaciones, pero la verdad es que no dejaba de resultar curioso que no existieran adaptadores específicos “oficiales” para tecnologías tan difundidas como ASP.NET MVC, o WebAPI.

Pues bien, desde hace unas semanas tenemos disponible la versión 3.0 de Unity, que ha venido acompañando también a la reluciente versión 6 de la Enterprise Library. Como sabemos, esta creación del equipo de Patterns & Practices de Microsoft contiene un conjunto de componentes reutilizables llamados “application blocks” que resuelven problemáticas comunes en el desarrollo de sistemas, como logging, tracing, acceso a datos, gestión de excepciones y otras, incluyendo por supuesto inyección de dependencias e inversión de control.

En este post vamos a ver cómo podemos usar fácilmente esta nueva versión de Unity en nuestras aplicaciones ASP.NET MVC y algunas de las novedades que ofrece para este tipo de sistemas.

1. Instalación

Como es habitual, la instalación del componente la realizaremos usando Nuget:
PM> Install-Package unity.mvc
imageEsta acción instalará varios paquetes dependientes y creará un par de archivos en la carpeta App_Start: UnityConfig.cs y UnityMvcActivator.cs.

En el primero de ellos encontramos la configuración de contenedor, es decir, es el punto en el que registraremos nuestras clases e interfaces para posteriormente usarlas en la resolución de dependencias. En UnityMvcActivator.cs, en cambio, se encuentra el código de inicialización específico de la integración para ASP.NET MVC. Más adelante veremos para qué son útiles y qué tenemos que tocar en ambos archivos.

Por comodidad, no tendremos que incluir en el global.asax.cs código para inicializar el contenedor. El mismo paquete Unity.Mvc incluye automáticamente una llamada a UnityWebActivator.Start() durante el proceso de inicialización utilizando el componente WebActivatorEx, una útil herramienta que permite introducir módulos de forma dinámica, durante el arranque.

2. Registro de componentes

El registro de interfaces y clases la realizaremos como siempre, sobre el contenedor Unity. Para ello, sólo tenemos que dirigirnos hacia el archivo UnityConfig.cs e implementarlo de la misma forma que siempre:
public static void RegisterTypes(IUnityContainer container)
{
    // NOTE: To load from web.config uncomment the line below. [...]
    // container.LoadConfiguration();

    // TODO: Register your types here
    // container.RegisterType<IProductRepository, ProductRepository>();

    container.RegisterType<IProductServices, ProductServices>();
}
Por supuesto, podríamos incluir tantos pares interfaz-clase como sean necesarios para nuestros propósitos.

3. Instancias per request

Ya he comentado muchísimas veces por aquí la importancia de hacer que los componentes que implementen IDisposable sean liberados al finalizar la misma. De no ser así, estaríamos creando una bomba de relojería: si no se liberan componentes, en cada petición podemos estar perdiendo memoria o recursos, y eso, a la larga, ya os digo que no es una historia con final feliz ;-)

La nueva versión de Unity y su componente de integración con MVC ha simplificado bastante la gestión del ciclo de vida de estas instancias y ha eliminado algunas de las cosas feas que teníamos que hacer antes, como la creación de instancias usando HierarchicalLifetimeManagers para vincularlas a un contenedor padre que era liberado al finalizar la petición. Ahora podemos ir bastante más al grano a la hora de registrar las clases, usando directamente un PerRequestLifetimeManager:
public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterType<IProductServices, ProductServices>(
            new PerRequestLifetimeManager());
}
Sólo con esto ya conseguimos que las instancias sean únicas por request, es decir, que si desde varios puntos del proceso de la petición se solicita un objeto de un tipo registrado, la instancia será única y compartida para todos ellos.

Pero aún falta un detalle para que las instancias sean liberadas correctamente al finalizar la petición. La pista la encontramos en el archivo UnityMvcActivator.cs que Nuget ha dejado en la carpeta App_Start:
public static void Start() 
{
    var container = UnityConfig.GetConfiguredContainer();

    FilterProviders.Providers.Remove(
        FilterProviders.Providers
                       .OfType<FilterAttributeFilterProvider>()
                       .First());

    FilterProviders.Providers.Add(
        new UnityFilterAttributeFilterProvider(container));

    DependencyResolver.SetResolver(new UnityDependencyResolver(container));

    // TODO: Uncomment if you want to use PerRequestLifetimeManager
    // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility
    //          .RegisterModule(typeof(UnityPerRequestHttpModule));
}
Simplemente descomentamos la llamada a RegisterModule() del final y nuestras instancias serán liberadas al finalizar la petición de forma automática :-)

Seguro que habréis observado también que al comienzo de este método se elimina el proveedor de filtros por defecto y se establece el de Unity. Efectivamente, ¡tenemos de serie inyección de dependencias en filtros! En breve escribiré un post al respecto para que veamos cómo se implementan.

4. Sorpresa final: ¡convenciones!

Pues sí, esta es una de las grandes novedades que he podido descubrir hasta el momento de esta nueva versión. De la misma forma que ya se podía hacer con otros contenedores IoC, ahora Unity soporta el registro de componentes basado en convenciones, de forma que podemos ahorrarnos bastante trabajo a la hora de mantenerlo.

¿Un ejemplo rápido de su utilidad? Pues por ejemplo que ya no tendremos que asociar mil interfaces del tipo IMyComponent a la clase MyComponent: simplemente indicaremos a Unity que debe usar la convención de nombrado para interfaces y clases con nombres equivalentes :-)

Así, en una única línea en el método RegisterTypes() de UnityConfig.cs podemos decirle que todas las clases pertenecientes al ensamblado de la aplicación MVC deben asociarse a interfaces cuyo nombre corresponda con ellas (es decir, sea igual pero comenzando por “I”), y siempre con tiempo de vida por petición:
public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterTypes(
       AllClasses.FromAssemblies(typeof(MvcApplication).Assembly),
       WithMappings.FromMatchingInterface,
       WithName.Default, 
       WithLifetime.Custom<PerRequestLifetimeManager>
    );
}
Por supuesto, las convenciones son totalmente configurables, por lo que con mucha probabilidad podremos adaptarlas a nuestras necesidades. Otro día escribiré también algo más sobre esto ;)

Enlaces:

Publicado en Variable not found.

2 Comentarios:

Anónimo dijo...

Excelente post master, de verdad que era necesario que Unity viniera ya con WebActivator, para ese caso algunas veces usaba Ninject que ya lo tenía!

Saludos!

josé M. Aguilar dijo...

Muchas gracias, Julio! La verdad es que sí que se echaba en falta :-)

Saludos!