Implementieren der IHostedService-Schnittstelle

Wenn Sie begrenzte Kontrolle benötigen, die über die Bereitstellung von BackgroundService hinausgeht, können Sie einen eigenen IHostedService implementieren. Die IHostedService-Schnittstelle ist Grundlage aller zeitintensiven Dienste in .NET. Benutzerdefinierte Implementierungen werden mit der Erweiterungsmethode AddHostedService<THostedService>(IServiceCollection) registriert.

In diesem Tutorial lernen Sie Folgendes:

  • Implementieren der Schnittstellen IHostedService und IAsyncDisposable
  • Erstellen eines timerbasierten Diensts
  • Registrieren der benutzerdefinierten Implementierung mit Abhängigkeitsinjektion und Protokollierung

Tipp

Der gesamte Quellcode des Beispiels „Worker in .NET“ steht im Beispielbrowser zum Download zur Verfügung. Weitere Informationen finden Sie unter Durchsuchen von Codebeispielen: Worker in .NET.

Voraussetzungen

Erstellen eines neuen Projekts

Um ein neues Workerdienstprojekt mit Visual Studio zu erstellen, wählen Sie Datei>Neu>Projekt... aus. Suchen Sie im Dialogfeld Neues Projekt erstellen nach „Workerdienst“, und wählen Sie die Workerdienstvorlage aus. Wenn Sie lieber die .NET-CLI verwenden möchten, öffnen Sie Ihr bevorzugtes Terminal in einem Arbeitsverzeichnis. Führen Sie den Befehl dotnet new aus, und ersetzen Sie <Project.Name> durch den gewünschten Projektnamen.

dotnet new worker --name <Project.Name>

Weitere Informationen zum .NET-CLI-Befehl für ein neues Workerdienstprojekt finden Sie unter dotnet new worker.

Tipp

Wenn Sie Visual Studio Code verwenden, können Sie .NET CLI-Befehle über das integrierte Terminal ausführen. Weitere Informationen finden Sie unter Visual Studio Code: Integriertes Terminal.

Erstellen eines Timerdiensts

Der timerbasierte Hintergrunddienst verwendet die System.Threading.Timer-Klasse. Der Timer löst die DoWork-Methode aus. Der Timer wird durch IHostLifetime.StopAsync(CancellationToken) deaktiviert und freigegeben, wenn der Dienstcontainer durch IAsyncDisposable.DisposeAsync() freigegeben ist:

Ersetzen Sie den Inhalt von Worker in der Vorlage durch den folgenden C#-Code, und benennen Sie die Datei in TimerService.cs um:

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;
    }
}

Wichtig

Worker ist eine Unterklasse von BackgroundService. Nun implementiert TimerService die Schnittstellen IHostedService und IAsyncDisposable.

TimerService ist sealed und kaskadiert den Aufruf von DisposeAsync von seiner _timer-Instanz. Weitere Informationen zum „kaskadierenden Dispose-Muster“ finden Sie unter Implementieren einer DisposeAsync-Methode.

Wenn StartAsync aufgerufen wird, wird der Timer instanziiert und anschließend gestartet.

Tipp

Timer wartet nicht auf den Abschluss vorheriger Ausführungen von DoWork. Die veranschaulichte Vorgehensweise eignet sich also möglicherweise nicht für alle Szenarios. Interlocked.Increment wird zum Erhöhen des Ausführungszählers mit einem atomischen Vorgang verwendet, wodurch sichergestellt wird, dass _executionCount nicht durch mehrere Threads gleichzeitig aktualisiert wird.

Ersetzen Sie den vorhandenen Inhalt von Program durch den folgenden C#-Code:

using App.TimerHostedService;

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

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

Der Dienst wird in (Program.cs) mit der Erweiterungsmethode AddHostedService registriert. Dies ist die gleiche Erweiterungsmethode, die Sie beim Registrieren von Unterklassen von BackgroundService verwenden, da beide die Schnittstelle IHostedService implementieren.

Weitere Informationen zum Registrieren von Diensten finden Sie unter Abhängigkeitsinjektion in .NET.

Überprüfen der Dienstfunktionalität

Zum Ausführen der Anwendung aus Visual Studio drücken Sie F5, oder wählen Sie die Menüoption Debuggen>Debuggen starten aus. Wenn Sie die .NET-CLI verwenden, führen Sie den Befehl dotnet run im Arbeitsverzeichnis aus:

dotnet run

Weitere Informationen zum Ausführungsbefehl in der .NET-CLI finden Sie unter dotnet run.

Lassen Sie die Anwendung eine Zeit lang laufen, um mehrere Abstufungen der Anzahl der Ausführungen zu generieren. Eine Ausgabe ähnlich der folgenden wird angezeigt:

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.

Wenn Sie die Anwendung in Visual Studio ausführen, wählen Sie Debuggen>Debuggen beenden... aus. Alternativ können Sie im Konsolenfenster STRG + C drücken, um den Abbruch zu signalisieren.

Siehe auch

Es gibt mehrere damit zusammenhängende Tutorials, die Sie berücksichtigen sollten: