Inyección de Dependencias en ASP.NET Core – II
En la entrada anterior sobre Inyección de Dependencias en ASP.NET Core veíamos un sencillo ejemplo de cómo registrar una interfaz y su implementación y de cómo utilizarla con el contenedor IoC que viene por defecto en ASP.NET Core.
En esta entrada vamos a explicar con algo más de profundidad las diferentes opciones que tenemos en .NET Core a la hora trabajar con IoC.
En el ejemplo que vimos, configuramos la implementación de nuestra clase como un Singleton.
Sin embargo, el contenedor IoC que viene con .NET Core tiene tres tipos diferentes de ciclos de vida.
- Singleton
- Transient
- Scoped
Enumeraré brevemente las características de cada uno, si bien esto no es propio de .NET Core en sí, sino de IoC.
Pero creo que conviene hacer un pequeño repaso porque tal cual, son soportados dentro de .NET Core.
Singleton
El contenedor de IoC será el encargado de crear y compartir una única instancia del servicio durante el funcionamiento de la aplicación Web.
Transient
El contenedor de IoC será el encargado de crear una nueva instancia del tipo de servicio indicado cada vez que lo solicitemos.
Scoped
El contenedor de IoC será el encargado de crear una instancia del tipo de servicio indicado por cada petición (request), siendo compartida esta instancia a lo largo de la petición (request).
En el ejemplo que preparé de la entrada anterior, vimos como declarar o registrar un servicio Singleton.
A continuación y siguiendo aquel ejemplo, expondré como registrar el servicio según los tres tipos posibles.
Singleton
services.Add(new ServiceDescriptor(typeof(ILog), new MyLog()));
Transient
services.Add(new ServiceDescriptor(typeof(ILog), n => new MyLog(), ServiceLifetime.Transient));
Scoped
services.Add(new ServiceDescriptor(typeof(ILog), n => new MyLog(), ServiceLifetime.Scoped));
Por defecto y si no indicamos el tipo de ciclo de vida, se declara como Singleton.
Ahora bien, services tiene diferentes métodos de extensión que nos facilita las mismas operaciones para hacerlas más legibles o cómodas.
Estas son las siguientes:
Singleton
services.AddSingleton<ILog, MyLog>(); // o bien: services.AddSingleton(typeof(ILog), typeof(MyLog));
Transient
services.AddTransient<ILog, MyLog>(); // o bien: services.AddTransient(typeof(ILog), typeof(MyLog));
Scoped
services.AddScoped<ILog, MyLog>(); // o bien: services.AddScoped(typeof(ILog), typeof(MyLog));
El contenedor de IoC se encargará de gestionar el ciclo de vida de los objetos creados sea cual sea su tipo de registro.
Cuando el contenedor de IoC elimina el servicio de acuerdo al ciclo de vida, eliminará también sus dependencias si las tiene.
Un buena práctica es que nuestro servicio implemente la interfaz IDisposable, para que ejecute automáticamente este método al intentar eliminar el servicio una vez concluido su ciclo de vida.
Dando una vuelta de tuerca a la inyección y cómo se puede llevar a cabo ésta, en el ejemplo que vimos en la entrada anterior inyectábamos nuestra clase MyLog a través del constructor, sin embargo, también podríamos inyectarlo a través del método.
Aquel mismo ejemplo que vimos pero inyectando la resolución en el método, quedaría de la siguiente forma:
public FooController() { } public IActionResult Index([FromServices] ILog log) { log.Write("\t Log => Executing /foo/index"); return View(); }
Llegados a este punto en el que hemos visto la inyección por constructor y la inyección por método, estés pensando en un tercer mecanismo de inyección, la inyección por propiedad.
Pues en el IoC de .NET Core, la inyección por propiedad no está soportada.
Para soportar este mecanismo de inyección, tendremos que usar otro contenedor diferente.
Pero también te diré que una buena práctica o recomendación es resolver la inyección en el constructor en lugar de hacerlo por método, ya que por método puede ser más complicado detectar determinados problemas o errores, o realizar determinadas pruebas.
Hay más detalles de mayor profundidad sobre IoC de los que he hablado hasta ahora, pero creo que lo que hemos visto hasta ahora cubre la inmensa mayoría de los casos con los que te encontrarás al trabajar con .NET Core e IoC.
En la siguiente entrada, veremos de forma práctica el comportamiento de los diferentes ciclos de vida. Un sencillo ejemplo que mostrará claramente el funcionamiento de Singleton, Transient y Scoped dentro de una aplicación Web desarrollada con ASP.NET Core.
Happy Coding!