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 marzo de 2017
ASP.NET CoreEl sistema de inyección de dependencias de ASP.NET Core es un potente mecanismo que viene de serie en el framework y, que de hecho, es uno de los principales pilares en los que se basa su funcionamiento.

Probablemente sabréis (y si no, podéis echar un vistazo a este post) que existen diversas fórmulas para añadir servicios o componentes al contenedor de dependencias, y una de ellas es la denominada "Scoped". Estos componentes quedan registrados asociados a un ciclo de vida específico (ServiceLifetime.Scoped), que indica que cada vez que haya que se solicite un objeto de este tipo se retornará la misma instancia dentro el ámbito actual, que normalmente coincide con el proceso de una petición específica.

O en otras palabras, dentro de la misma petición, la instancia del componente será compartida por todos los objetos que dependan de él. Además, al finalizar el proceso de la petición, el framework invocará su método Dispose() (si existe, claro), permitiendo liberar los recursos que haya utilizado. Por ejemplo, suele ser una buena práctica emplear esta técnica para reutilizar un contexto de datos entre los servicios que participan en el proceso de una petición que accede a una base de datos, y asegurar su liberación final para no dejar conexiones abiertas.

Pero este post no va de ver cómo registrar estos componentes o de cómo utilizarlos en nuestras aplicaciones, sino de responder una simple curiosidad: ¿quién se encarga de crear este scope cuando comienza una petición y de liberarlo cuando finaliza? Ya, sé que no tiene mucha utilidad práctica en el día a día, pero es una buena excusa para echar un vistazo y entender cómo funcionan las cosas por dentro ;)

<Disclaimer>Todo lo que comentaremos a continuación son detalles de la implementación actual del framework y son válidos en la versión 1.1 de ASP.NET Core, pero podrían cambiar en futuras versiones.</Disclaimer>

Como sabéis, el método Configure() de la clase de inicialización de ASP.NET Core recibe una instancia de IApplicationBuilder que representa el pipeline de nuestra aplicación. Durante el proceso de configuración, utilizaremos esta instancia para añadir los middlewares que necesitemos en nuestra aplicación.

Pero si alguna vez habéis introducido un breakpoint en este método y habéis analizado el contenido que recibimos aquí, veréis que enmascarado tras el interfaz IApplicationBuilder se encuentra un objeto ApplicationBuilder que, a diferencia de lo que podríamos esperar, viene con el pipeline ya poblado con algunos middlewares, como se muestra en la siguiente captura de pantalla:

Instancia de IApplicationBuilder recibida en el método Configure()
De los tres middlewares incluidos por defecto, los dos últimos tienen que ver con la integración con IIS, pero el que nos interesa especialmente es el primero de ellos, que si escarbamos un poco podemos ver que se trata de un objeto RequestServicesContainerMiddleware.

Este middleware, definido de forma interna en el paquete Microsoft.AspNetCore.Hosting, está posicionado justo a la entrada del pipeline y básicamente se encarga de crear un scope justo cuando llega una petición, ceder el control al resto del pipeline y, finalmente, liberarlo junto con los objetos asociados al mismo cuando vuelve a retomar el control, es decir, cuando el proceso de la petición ha finalizado completamente:
public async Task Invoke(HttpContext httpContext)
{
    [...]
    using (var feature = new RequestServicesFeature(_scopeFactory))
    {
        try
        {
            httpContext.Features.Set<IServiceProvidersFeature>(feature);
            await _next.Invoke(httpContext);
        }
        finally
        {
            [...]
        }
    }
}
La clase RequestServicesFeature que se crea en el código anterior es la que realmente implementa la instanciación del scope, y es introducida en las features disponibles en el contexto HTTP como IServiceProvidersFeature para que esté disponible durante el proceso de la petición para actuar como service provider. Es decir, todas las instancias  creadas como ServiceLifetime.Scoped serán asociadas a este proveedor de servicios.

Cuando la petición ha sido ya procesada por el resto del pipeline y el control retorna al código de este middleware, la finalización del bloque using hace que se libere el objeto RequestServicesFeature y con él los objetos scoped que hayan sido creados en su ámbito. Sencillo, ¿verdad?

En conclusión, la respuesta a la pregunta que nos hacíamos al principio es bastante simple, e incluso previsible. La creación y liberación del scope que marca la vida de las dependencias durante el proceso de una petición se realiza desde un middleware ubicado justo en la puerta del pipeline. Esta posición privilegiada le permite crear un service provider específico para la petición, que actuará como scope, y liberarlo cuando el proceso de ésta haya finalizado, junto con todos los objetos que hayan sido creados en el mismo.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!