BackgroundService 内でスコープ付きサービスを使用する

AddHostedService 拡張メソッドのいずれかを使用して IHostedService の実装を登録すると、サービスはシングルトンとして登録されます。 スコープ付きサービスに依存したいシナリオがある場合があります。 詳細については、.NET での依存関係の挿入のサービスの有効期間に関する記事を参照してください。

このチュートリアルでは、次の作業を行う方法について説明します。

ヒント

".NET でのワーカー" のサンプル ソース コードはすべて、サンプル ブラウザーでダウンロードできます。 詳細については、コード サンプルの参照: .NET でのワーカーに関するページをご覧ください。

前提条件

新しいプロジェクトを作る

Visual Studio を使用して新しい Worker サービス プロジェクトを作成するには、[ファイル]>[新規]>[プロジェクト] を選択します。[新しいプロジェクトの作成] ダイアログで "Worker サービス" を検索し、Worker サービス テンプレートを選択します。 .NET CLI を使用する場合は、作業ディレクトリで好みのターミナルを開きます。 dotnet new コマンドを実行し、<Project.Name> を目的のプロジェクト名に置き換えます。

dotnet new worker --name <Project.Name>

.NET CLI の new worker サービス プロジェクト コマンドの詳細については、「dotnet new worker」を参照してください。

ヒント

Visual Studio Code を使用している場合は、統合ターミナルから .NET CLI コマンドを実行できます。 詳細については、Visual Studio Code の統合ターミナルに関する記事を参照してください。

スコープ付きサービスを作成する

BackgroundService 内でスコープ サービスを使用するには、スコープを作成します。 既定では、ホステッド サービスのスコープは作成されません。 スコープ付きバックグラウンド サービスには、バックグラウンド タスクのロジックが含まれています。

namespace App.ScopedService;

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

前のインターフェイスでは、1 つの DoWorkAsync メソッドが定義されています。 既定の実装を定義するには:

  • サービスは非同期です。 DoWorkAsync メソッドは Task を返します。 デモンストレーションのために、10 秒の遅延が DoWorkAsync メソッドで待機されます。
  • ILogger がサービスに挿入されます。
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);
        }
    }
}

ホステッド サービスにより、スコープ付きバックグラウンド サービスをその DoWorkAsync メソッドの呼び出しに解決するためのスコープが作成されます。 ExecuteAsync で待機していた DoWorkAsyncTask を返します。

Worker クラスを書き換える

既存の Worker クラスを次の C# コードに置き換え、ファイルの名前を 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);
    }
}

前のコードでは、明示的なスコープが作成され、IScopedProcessingService の実装が依存関係挿入サービス スコープ ファクトリから解決されます。 解決されたサービス インスタンスにはスコープが設定されており、その DoWorkAsync メソッドが待機されています。

テンプレートの Program.cs ファイルの内容を、次の C# コードに置き換えます。

using App.ScopedService;

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

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

サービスは (Program.cs) に登録されています。 ホステッド サービスは、AddHostedService 拡張メソッドを使用して登録されます。

サービスの登録の詳細については、「.NET での依存関係の挿入」を参照してください。

サービスの機能を確認する

Visual Studio からアプリケーションを実行するには、F5 キーを押すか、[デバッグ]>[デバッグの開始] メニュー オプションを選択します。 .NET CLI を使用している場合は、作業ディレクトリから dotnet run コマンドを実行します。

dotnet run

.NET CLI の run コマンドの詳細については、「dotnet run」を参照してください。

アプリケーションをしばらく実行させて、何回か実行カウントのインクリメントを生成します。 次のような出力が表示されます。

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.

Visual Studio 内からアプリケーションを実行している場合は、[デバッグ]>[デバッグの停止] を選択します。または、コンソール ウィンドウで Ctrl + C キーを押して、キャンセルを通知します。

関連項目