Usare i servizi con ambito all'interno di una classe BackgroundService

Quando si registrano implementazioni di IHostedService usando uno dei metodi di estensione AddHostedService, il servizio viene registrato come singleton. Potrebbero esserci scenari in cui si vuole fare affidamento su un servizio con ambito. Per altre informazioni, vedere Inserimento delle dipendenze in .NET: durate dei servizi.

In questa esercitazione apprenderai a:

Suggerimento

Tutto il codice sorgente di esempio di "Servizi ruolo di lavoro in .NET" è disponibile per il download in Esplorazione esempi. Per altre informazioni, vedere Esplorare esempi di codice: Servizi ruolo di lavoro in .NET.

Prerequisiti

Crea un nuovo progetto

Per creare un nuovo progetto del servizio ruolo di lavoro con Visual Studio, selezionare File>Nuovo>Progetto.... Nella finestra di dialogo Crea un nuovo progetto cercare "Servizio del ruolo di lavoro" e selezionare Modello del servizio ruolo di lavoro. Se si preferisce usare l'interfaccia della riga di comando di .NET, aprire il terminale preferito in una directory di lavoro. Eseguire il comando dotnet new e sostituire il <Project.Name> con il nome del progetto desiderato.

dotnet new worker --name <Project.Name>

Per altre informazioni sul comando new worker service project dell'interfaccia della riga di comando di .NET, vedere dotnet new worker.

Suggerimento

Se si usa Visual Studio Code, è possibile eseguire i comandi dell'interfaccia della riga di comando di .NET dal terminale integrato. Per altre informazioni, vedere Visual Studio Code: terminale integrato.

Creare servizi con ambito

Per usare i servizi con ambito all'interno di BackgroundService, creare un ambito. Non viene creato automaticamente alcun ambito per un servizio ospitato. Il servizio in background con ambito contiene la logica dell'attività in background.

namespace App.ScopedService;

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

L'interfaccia precedente definisce un singolo metodo DoWorkAsync. Per definire l'implementazione predefinita:

  • Il servizio è asincrono. Il metodo DoWorkAsync restituisce un oggetto Task. Ai fini della dimostrazione, un ritardo di dieci secondi è atteso nel metodo DoWorkAsync.
  • Un'interfaccia ILogger viene inserita nel servizio:
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);
        }
    }
}

Il servizio ospitato crea un ambito per risolvere il servizio in background con ambito e chiamare il relativo metodo DoWorkAsync. DoWorkAsync restituisce un Task, atteso in ExecuteAsync:

Riscrivere la classe Worker

Sostituire la classe Worker esistente con il codice C# seguente e rinominare il file in 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);
    }
}

Nel codice precedente viene creato un ambito esplicito e l'implementazione IScopedProcessingService viene risolta dalla factory dell'ambito del servizio di inserimento delle dipendenze. L'istanza del servizio risolta è con ambito e il relativo metodo DoWorkAsync è atteso.

Sostituire i contenuti del file Program.cs del modello con il codice C# seguente:

using App.ScopedService;

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

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

I servizi vengono registrati in (Program.cs). Il servizio ospitato viene registrato con il metodo di estensione AddHostedService.

Per altre informazioni sulla registrazione dei servizi, vedere Inserimento delle dipendenze in .NET.

Verificare la funzionalità del servizio

Per eseguire l'applicazione da Visual Studio, selezionare F5 oppure selezionare l'opzione di menu Debug>Avvia debug. Se si usa l'interfaccia della riga di comando di .NET, eseguire il comando dotnet run dalla directory di lavoro:

dotnet run

Per altre informazioni sul comando run dell'interfaccia della riga di comando di .NET, vedere dotnet run.

Consentire l'esecuzione dell'applicazione per qualche minuto per generare diversi incrementi del numero di esecuzioni. L'output sarà simile al seguente:

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.

Se si esegue l'applicazione da Visual Studio, selezionare Debug>Arresta debug.... In alternativa, selezionare CTRL + C dalla finestra della console per segnalare l'annullamento.

Vedi anche