Implementare l'interfaccia IHostedService

Quando è necessario un controllo finito oltre il BackgroundServicefornito, è possibile implementare il proprio IHostedService. L'interfaccia IHostedService è la base per tutti i servizi a esecuzione prolungata in .NET. Le implementazioni personalizzate vengono registrate con il metodo di estensione AddHostedService<THostedService>(IServiceCollection).

In questa esercitazione apprenderai a:

  • Implementare le interfacce IHostedService e IAsyncDisposable.
  • Creare un servizio basato su timer.
  • Registrare l'implementazione personalizzata con inserimento e registrazione delle dipendenze.

Suggerimento

Tutto il codice sorgente di esempio di "Servizi ruolo di lavoro in .NET" è disponibile per il download in Esplorazione campioni. 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 il modello 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 <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 un servizio timer

Il servizio in background basato su timer usa la classe System.Threading.Timer. Il timer attiva il metodo DoWork. Il timer viene disabilitato con IHostLifetime.StopAsync(CancellationToken) ed eliminato quando il contenitore dei servizi è eliminato con IAsyncDisposable.DisposeAsync():

Sostituire il contenuto di Worker dal modello con il codice C# seguente e rinominare il file in TimerService.cs:

namespace App.TimerHostedService;

public sealed class TimerService(ILogger<TimerService> logger) : IHostedService, IAsyncDisposable
{
    private readonly Task _completedTask = Task.CompletedTask;
    private int _executionCount = 0;
    private Timer? _timer;

    public Task StartAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("{Service} is running.", nameof(TimerHostedService));
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

        return _completedTask;
    }

    private void DoWork(object? state)
    {
        int count = Interlocked.Increment(ref _executionCount);

        logger.LogInformation(
            "{Service} is working, execution count: {Count:#,0}",
            nameof(TimerHostedService),
            count);
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Service} is stopping.", nameof(TimerHostedService));

        _timer?.Change(Timeout.Infinite, 0);

        return _completedTask;
    }

    public async ValueTask DisposeAsync()
    {
        if (_timer is IAsyncDisposable timer)
        {
            await timer.DisposeAsync();
        }

        _timer = null;
    }
}

Importante

Worker era una sottoclasse di BackgroundService. TimerService implementa ora le interfacce IHostedService e IAsyncDisposable.

TimerService è sealed e propaga a catena la chiamata DisposeAsync dalla relativa istanza di _timer. Per altre informazioni sul "criterio Dispose a catena", vedere Implementare un metodo DisposeAsync.

Quando viene chiamato StartAsync, viene creata un'istanza del timer, avviando quindi il timer.

Suggerimento

Timer non attende il completamento delle esecuzioni precedenti di DoWork, quindi l'approccio illustrato potrebbe non essere adatto per ogni scenario. Interlocked.Increment viene usato per incrementare il contatore di esecuzione come operazione atomica, assicurando che più thread non aggiornino contemporaneamente _executionCount.

Sostituire i contenuti Program esistenti con il codice C# seguente:

using App.TimerHostedService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<TimerService>();

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

Il servizio viene registrato in (Program.cs) con il metodo di estensione AddHostedService. Si tratta dello stesso metodo di estensione usato per la registrazione di sottoclassi BackgroundService, poiché implementano entrambe l'interfaccia IHostedService.

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.TimerHostedService.TimerService[0]
      TimerHostedService is running.
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: .\timer-service
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 1
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 2
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 3
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 4
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.TimerHostedService.TimerService[0]
      TimerHostedService 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

Esistono diverse esercitazioni correlate da considerare: