Uso de servicios con ámbito en BackgroundService

Al registrar implementaciones de IHostedService mediante cualquiera de los métodos de extensión AddHostedService, el servicio se registra como singleton. Puede haber escenarios en los que quiera confiar en un servicio con ámbito. Para obtener más información, vea Inserción de dependencias en .NET: duraciones del servicio.

En este tutorial aprenderá a:

Sugerencia

Todo el ejemplo de "Trabajos en .NET" está disponible en el Explorador de ejemplos para su descarga. Para obtener más información, consulte Examinación de ejemplos de código: Trabajos en .NET.

Requisitos previos

Creación de un nuevo proyecto

Para crear un proyecto de Worker Service con Visual Studio, seleccione Archivo>Nuevo>Proyecto... . En el cuadro de diálogo Crear un proyecto, busque "Worker Service" y seleccione la plantilla Worker Service. Si prefiere usar la CLI de .NET, abra su terminal favorito en un directorio de trabajo. Ejecute el comando dotnet new y reemplace <Project.Name> por el nombre del proyecto deseado.

dotnet new worker --name <Project.Name>

Para más información sobre el comando del nuevo proyecto de Worker Service de la CLI de .NET, vea dotnet new worker.

Sugerencia

Si usa Visual Studio Code, puede ejecutar comandos de la CLI de .NET desde el terminal integrado. Para más información, vea Visual Studio Code: terminal integrado.

Creación de servicios con ámbito

Para usar servicios con ámbito en un elemento BackgroundService, cree un ámbito. No se crean ámbitos de forma predeterminada para los servicios hospedados. El servicio de tareas en segundo plano con ámbito contiene la lógica de la tarea en segundo plano.

namespace App.ScopedService;

public interface IScopedProcessingService
{
    Task DoWorkAsync(CancellationToken stoppingToken);
}

La interfaz anterior define un único método DoWorkAsync. Para definir la implementación predeterminada:

  • El servicio es asincrónico. El método DoWorkAsync devuelve un objeto Task. Para fines de demostración, se espera un retraso de diez segundos en el método DoWorkAsync.
  • ILogger se inserta en el servicio:
namespace App.ScopedService;

public sealed class DefaultScopedProcessingService(
    ILogger<DefaultScopedProcessingService> logger) : IScopedProcessingService
{
    private int _executionCount;

    public async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            ++ _executionCount;

            logger.LogInformation(
                "{ServiceName} working, execution count: {Count}",
                nameof(DefaultScopedProcessingService),
                _executionCount);

            await Task.Delay(10_000, stoppingToken);
        }
    }
}

El servicio hospedado crea un ámbito con el fin de resolver el servicio de tareas en segundo plano con ámbito para llamar a su método DoWorkAsync. DoWorkAsync devuelve Task, que se espera en ExecuteAsync:

Reescritura de la clase Worker

Reemplace la clase Worker por el siguiente código de C# y cambie el nombre del archivo a ScopedBackgroundService.cs:

namespace App.ScopedService;

public sealed class ScopedBackgroundService(
    IServiceScopeFactory serviceScopeFactory,
    ILogger<ScopedBackgroundService> logger) : BackgroundService
{
    private const string ClassName = nameof(ScopedBackgroundService);

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is running.", ClassName);

        await DoWorkAsync(stoppingToken);
    }

    private async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is working.", ClassName);

        using (IServiceScope scope = serviceScopeFactory.CreateScope())
        {
            IScopedProcessingService scopedProcessingService =
                scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();

            await scopedProcessingService.DoWorkAsync(stoppingToken);
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is stopping.", ClassName);

        await base.StopAsync(stoppingToken);
    }
}

En el código anterior, se crea un ámbito explícito y la implementación del IScopedProcessingService se resuelve desde el generador de ámbitos del servicio de inserción de dependencias. La instancia de servicio resuelta está limitada y se espera su método DoWorkAsync.

Reemplace el contenido del archivo Program.cs de la plantilla por el siguiente código de C#:

using App.ScopedService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<ScopedBackgroundService>();
builder.Services.AddScoped<IScopedProcessingService, DefaultScopedProcessingService>();

IHost host = builder.Build();
host.Run();

Los servicios se registran en (Program.cs). El servicio hospedado se registra con el método de extensión AddHostedService.

Para obtener más información sobre el registro de servicios, consulte Inserción de dependencias en .NET.

Comprobación de la funcionalidad del servicio

Para ejecutar la aplicación desde Visual Studio, seleccione F5 o la opción de menú Depurar>Iniciar depuración. Si usa la CLI de .NET, ejecute el comando dotnet run desde el directorio de trabajo:

dotnet run

Para más información sobre el comando run de la CLI de .NET, consulte dotnet run.

Deje que la aplicación se ejecute durante un poco para generar varios incrementos de recuento de ejecuciones. Verá un resultado similar al siguiente:

info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is running.
info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is working.
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 1
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: .\scoped-service
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 2
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 3
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 4
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is stopping.

Si ejecuta la aplicación desde dentro Visual Studio, seleccione Depurar>Detener depuración... . Como alternativa, seleccione Ctrl + C en la ventana de consola para indicar la cancelación.

Vea también