DEV Community

Cover image for Inyección de Dependencias(DI) en ASP.NET Core, mejores prácticas para escribir código reutilizable, escalable y desacoplado
Eduardo Barrios
Eduardo Barrios

Posted on • Updated on

Inyección de Dependencias(DI) en ASP.NET Core, mejores prácticas para escribir código reutilizable, escalable y desacoplado

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!!!
Alt Text

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).

Alt Text

Alt Text
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.

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.
Alt Text

El servicio utiliza el Contexto de Datos para traer el listado de la Base de Datos.
Alt Text

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.
Alt Text

El resultado es el siguiente
Alt Text

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.
Alt Text

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.
Alt Text

El siguiente paso es decirle al servicio de Albumes que implemente la interface IAlbumesService.
Alt Text

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.
Alt Text

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.
Alt Text

Ahora invocamos el método AddDependency en el método ConfigureServices en la clase Startup.cs
Alt Text

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.
Alt Text

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.
Alt Text

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/

Top comments (4)

Collapse
 
gerpuntomaarrob profile image
ger ma

Hola eduardo, porque utilizas UseInMemoryDatabase? siempre es bueno usar esta opción en una BD de producción? o solo fue por el ejemplo?

Collapse
 
ebarrioscode profile image
Eduardo Barrios

Escribí un artículo sobre eso por si quieres leerlo, saludos
dev.to/ebarrioscode/ingenieria-de-...

Collapse
 
ebarrioscode profile image
Eduardo Barrios

Hola lo utilice únicamente para el ejemplo, en la vida real he utilizado base de datos en memoria para hacer unit test a métodos que utilizan acceso a una base de datos, con database in memory he podido hacer mocks de la base de datos para poder probar componentes que funcionan aisladamente sin tener que conectarme realmente a alguna base de datos de algún entorno como dev, qa, production, etc.

Collapse
 
andresglz01 profile image
AndresGlz01

Fue un artículo muy interesante, ¿has escrito sobre reflexión?