Dependency Injection

Vida útil de los servicios: Transient, Scoped & Singleton en ASP.NET Core

Artículo para explorar y comprender cómo funciona la vida útil de los servicios en ASP.NET Core mediante la inyección de dependencia.

La vida útil de los servicios define cómo crear y eliminar los servicios almacenados en el contenedor de servicios. Debes elegir una durabilidad adecuada para cada servicio según sus características y requerimientos.

Los servicios se pueden registrar con una de las siguientes vidas útiles:

Transient

Los objetos «Transient» o transitorios siempre son diferentes, se crean si los solicita desde el contenedor de servicios. Funciona mejor para servicios más livianos y sin estados.

Scoped

Los objetos «Scope» o de ámbito son los mismos para una solicitud determinada, pero varían entre solicitudes. Scoped es la vida predeterminada de Entity Framework Core cuando agrega AddDbContext.

Singleton

Los objetos «Singleton» o únicos son los mismos para cada solicitud, se crean la primera vez que se solicitan, cada solicitud posterior de la implementación del servicio desde el contenedor de inyección de dependencia utiliza la misma instancia. Debido a que la memoria no se libera hasta que se cierra la aplicación, considere usar memoria con un servicio singleton.

  • Importante: No resuelva un servicio «Scoped» desde un «Singleton» y tenga cuidado de no hacerlo indirectamente. Resuelva de la siguiente manera:
    • Resuelve servicios «Singleton» desde un servicio «Transient» o «Scoped»
    • Resuelve un servicio «Scoped» desde otro servicio «Transient» o «Scoped»

Primeros pasos

Este es un ejemplo para mostrar cómo funciona la vida útil de los servicios mediante la inyección de dependencia en ASP.NET Core.

Instalación

1. Instalar paquete

Install-Package Microsoft.Extensions.DependencyInjection

Uso

1. Crea clase «Operation» e interfaces

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }
public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

2. Añade servicios al contenedor

builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();

3. Inyección y uso en el controlador y en el middleware, para ver cómo los valores para obtener OperationId cambian según la vida útil del servicio.

_logger.LogInformation("Transient: " + transientOperation.OperationId);
_logger.LogInformation("Scoped: " + scopedOperation.OperationId);
_logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

4. Cuando inicias la aplicación, ves cómo el middleware «Transient» cambia con respecto al controlador. Mientras que «Scoped» y «Singleton» no cambian.

info: Lifetimes.MyMiddleware[0]
      Transient: cc44
info: Lifetimes.MyMiddleware[0]
      Scoped: 5582
info: Lifetimes.MyMiddleware[0]
      Singleton: d05c
info: DbContext.Controllers.DIController[0]
      Transient: 8af0
info: DbContext.Controllers.DIController[0]
      Scoped: 5582
info: DbContext.Controllers.DIController[0]
      Singleton: d05c

5. En una nueva solicitud http, puedes ver qué «Transient» cambia. «Singleton» no cambia. Pero «Scoped» cambia en comparación con los primeros resultados, porque es una nueva solicitud.

info: Lifetimes.MyMiddleware[0]
      Transient: 42b0
info: Lifetimes.MyMiddleware[0]
      Scoped: 839f
info: Lifetimes.MyMiddleware[0]
      Singleton: d05c
info: DbContext.Controllers.DIController[0]
      Transient: d512
info: DbContext.Controllers.DIController[0]
      Scoped: 839f
info: DbContext.Controllers.DIController[0]
      Singleton: d05c

Conclusion, «Transient» objects are always different. «Scoped» objects are the same for a given request, but vary between requests. And «Singleton» objects are the same for each request, they are created the first time they are requested.

Conclusión: los objetos «Transient» siempre son diferentes. Los objetos con «Scoped» son los mismos para una solicitud determinada, pero varían entre solicitudes. Y los objetos «Singleton» son los mismos para cada solicitud, se crean la primera vez que se solicitan.

Código de ejemplo

GitHub: Service lifetimes: Transient, Scoped & Singleton in ASP.NET Core