Verwenden bereichsbezogener Dienste in einem BackgroundService

Wenn Sie Implementierungen von IHostedService mit einer der AddHostedService-Erweiterungsmethoden registrieren, wird der Dienst als Singleton registriert. Es kann Szenarien geben, in denen Sie einen bereichsbezogenen Dienst benötigen. Weitere Informationen finden Sie unter Abhängigkeitsinjektion in .NET: Dienstlebensdauer.

In diesem Tutorial lernen Sie Folgendes:

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 bereichsbezogener Dienste

Erstellen Sie einen Bereich, um bereichsbezogene Dienste in einem BackgroundService zu verwenden. Bereiche werden für einen gehosteten Dienst nicht standardmäßig erstellt. Der bereichsbezogene Hintergrunddienst enthält die Logik der Hintergrundaufgabe.

namespace App.ScopedService;

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

Die vorangehende Schnittstelle definiert eine einzelne DoWorkAsync-Methode. So legen Sie die Standardimplementierung fest

  • Der Dienst ist asynchron. Die DoWorkAsync-Methode gibt Task zurück. Zu Demonstrationszwecken wird in der DoWorkAsync-Methode eine Verzögerung von zehn Sekunden verwendet.
  • Ein ILogger wird in den Dienst eingefügt:
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);
        }
    }
}

Der gehostete Dienst erstellt einen Bereich, um den bereichsbezogenen Hintergrunddienst so aufzulösen, dass dessen DoWorkAsync-Methode aufgerufen wird. DoWorkAsync gibt einen Task zurück, auf den in ExecuteAsync gewartet wird:

Erneutes Generieren der Worker-Klasse

Ersetzen Sie die vorhandene Worker-Klasse durch den folgenden C#-Code, und benennen Sie die Datei in ScopedBackgroundService.cs um:

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

Im vorherigen Code wird ein expliziter Bereich erstellt und die IScopedProcessingService-Implementierung von der Dienstumfangsfactory für die Abhängigkeitsinjektion aufgelöst. Die aufgelöste Dienstinstanz ist bereichsbezogen, und ihre DoWorkAsync-Methode wird erwartet.

Ersetzen Sie den Inhalt der Vorlagendatei Program.cs durch den folgenden C#-Code:

using App.ScopedService;

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

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

Die Dienste werden in (Program.cs) registriert. Der gehostete Dienst wird mit der Erweiterungsmethode AddHostedService registriert.

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.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.

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