Inyección de Dependencias(DI) en ASP.NET Core, mejores prácticas para escribir código reutilizable, escalable y desacoplado
Eduardo Barrios
Posted on November 16, 2019
Los Desarrolladores de Software siempre estamos en busca de una forma reutilizable para resolver un problema común y para lograrlo es necesario recurrir a estándares como lo son los patrones de diseño de Software. Antonio Leiva define un patrón de diseño como una forma de solucionar un problema que se puede extraer, explicar y reutilizar en múltiples ámbitos.
Ahora sabemos que es un patrón de diseño, pero!!!
Inyección de Dependencias es un patrón de diseño de software que nos permite desarrollar componentes acoplados libremente o desacoplados para obtener como resultado la fácil gestión de cambios a futuro, implementación fácil de pruebas unitarias, factoría para emitir instancias de clases, prevención de fugas de memoria, entre otros.
DI proporciona un mecanismo para la construcción de gráficos de dependencia independientes de las definiciones de clase, aplicando esta técnica nuestras clases van a depender de interfaces y no de implementaciones, esto se relaciona directamente con el primero de los 5 principios para desarrollar software de calidad, haciendo referencia a SOLID y el principio de responsabilidad única.
Por su parte ASP.NET Core nos permite la utilización del patrón de diseño Inyección de Dependencias sin la necesidad de utilizar librerías de terceros para lograrlo, además ASP.NET Core provee un Contenedor de Inversión de Controles(IoC) quien es el encargado de proveer las instancias de los tipos los cuales le decimos en el inicio de la Aplicación(Startup).
Para este post utilizaremos un proyecto WebApi de .NET Core 3.0, utilizaré los mismos modelos, base de datos en memoria y estructura del proyecto de mi post anterior.
Base de Datos en memoria con ASP.NET Core 3.0 MVC + Entity Framework Core
Eduardo Barrios ・ Nov 7 '19
Vamos a Visual Studio Comunity 2019.
Inicialmente visualizamos el archivo HomeController.cs, este código utiliza un servicio para mostrar un listado de Albumes y su relación con Artista, mediante Entity Framework Core. Por el momento no estamos pensando en inyección de dependencias.
El servicio utiliza el Contexto de Datos para traer el listado de la Base de Datos.
La clase contexto WebApiDbContext posee una sobreescritura del método OnConfiguring para enviarle la configuración de la base de datos en memoria y el método constructor está sobrecargado.
Analizemos el código anterior, como punto principal vemos que funciona, pero nuestro controlador crea y depende directamente de la instancia de la clase AlbumesService, las dependencias de código son problemáticas y deben evitarse, ¿por qué? supongamos que queremos reemplazar la implementación de AlbumesService, debería modificar el controlador también y esto en un proyecto grande representa problemas porque puede que olvide modificar diferentes controladores que utilicen el mismo servicio y el funcionamiento del sistema seria inconsistente. Un problema más en este código podría ser que si AlbumesService tuviera alguna dependencia esta debe ser configurada en el controlador o clase que utilice el servicio de Albumes. Por otra parte esta implementación como se encuentra en este momento dificultaría la realización de pruebas unitarias debido a que sería difícil probar componentes que no están desacoplados y no hay manera de pasarle un servicio fake porque se está utilizando el servicio de forma explicita como una instancia directa.
Ahora abordemos la problemática y solucionemos mediante Inyección de Depedencias.
Pensemos en Inyección de Dependencias, como primer paso debemos utilizar una interface que nos permita abstraer la implementación de dependencias, vamos a relacionarla con el servicio Albumes.
El siguiente paso es decirle al servicio de Albumes que implemente la interface IAlbumesService.
Ahora debemos registrar el servicio en el Contenedor de servicios integrados IServiceProvider que nos proporciona ASP.NET Core, los servicios son registrados en el método ConfigureServices de la clase Startup de la aplicación, una vez definido el servicio debemos registrarlo.
Observemos que para este escenario estamos utilizado el método AddTransient para indicar la interface para el servicio y su implementación, esto hace referencia al ciclo de vida de los servicios que viven en el contenedor. Dentro del contenedor que nos proveé ASP.NET Core existen 3 ciclos de vida básicos los cuales podemos utilizar para inicializar las dependencias desde nuestro contenedor.
Transient: Se crean cada vez que se solicitan desde el contenedor de servicios. Esta vida útil funciona mejor para servicios ligeros y sin estado.
Scoped: Se crean una vez por solicitud del cliente(conexión). Se utiliza cuando queremos servir la misma instancia dentro del mismo contexto de una petición HTTP, pero diferente entre distintos contextos HTTP.
Singleton: Se crean la primera vez que se solicitan o cuando Startup.ConfigureServices se ejecuta y se especifica una instancia con el registro del servicio. Cada solicitud posterior utiliza la misma instancia.
Para el paso anterior hay otra alternativa por la que podemos optar, personalmente me gusta la siguiente alternativa que nos recomienda Microsoft, consiste en separar todos los servicios en una clase static dentro de un método que recibe como parámetro el IServiceCollection y lo retorna con los servicios ya registrados y unificados para después inyectar todo en el método ConfigureServices, creamos una carpeta llamada Middleware este nombre es arbitrario y dentro una clase llamada IoC que hace referencia al Contenedor de Inversión de Control.
Ahora invocamos el método AddDependency en el método ConfigureServices en la clase Startup.cs
Seguidamente vamos al controlador a cambiar esa instancia explicita que teniamos con AlbumesService y la modificaremos pensando en inyectar dependencias, observemos que ahora en lugar de instanciar AlbumesService tenemos una propiedad de lectura que además es una interface IAlbumesService y en ningún momento la inicializamos nosotros, inyectamos la dependencia en el método constructor y quien se encarga de instanciar el servicio es el contenedor, he aquí la razón de agregar las dependencias en el método ConfigureServices de la clase Startup.
Ahora compilamos, ejecutamos y vamos a la ruta de nuestro WebApi desde Postman y podemos observar que este código funciona, el resultado es el mismo cierto, pero en el estado actual en el que se encuentra nuestro código es mejor, debido a que abordamos las problemáticas anteriormente mencionadas.
En conclusión la técnica de Inyección de Dependencias nos ayuda a escribir código reutilizable debido a que el servicio no se instancia en ningún controlador ni en otra clase que lo utiliza podemos reutilizarlo en N cantidad de controladores y clases que necesitemos, ya que la lógica está encapsulada dentro de un servicio, además es escalable y mantenible, podemos modificar la lógica del servicio sin afectar otras partes de nuestro sistema y evitar inconsistencias lógicas, además el código está desacoplado por lo que fácilmente podemos comprobar el correcto funcionamiento de un fragmento de código en específico.
Link al repositorio en GitHub:
https://github.com/EbarriosCode/DependencyInjectionDotNetCore3.0
Referencias:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.0
https://devexperto.com/patrones-de-diseno-software/
Posted on November 16, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 29, 2024