ASP.NET Core (.NET Core) において、アプリケーションの起動に合わせてバックグラウンドタスクを起動させる方法です。
環境
- Visual Studio 2017
- .NET Core 2.2
概要
IHostedService
を実装したサービスクラスを作成します。IHostedService
を使用するには、Nuget で Microsoft.Extensions.Hosting
を追加する必要があります。
サービスクラスを IHostedService
として DI コンテナに登録すると、Host が自動でバックグラウンドタスクとして実行してくれるようです。
Host については、作成するアプリケーションによって以下の2種類があるようです。
- ASP.NET Core で使用する Web ホスト (IWebHostBuilder)
- ASP.NET Core 以外で使用する汎用ホスト (HostBuilder)
ASP.NET Core の場合、テンプレートからプロジェクトを作成した段階で Web ホストが使用されている状態なので、特に意識はしなくても大丈夫そうですが、コンソールアプリなどの場合は汎用ホストを使用するように実装する必要があります。
ASP.NET Core の場合(Web ホスト)
ASP.NET Core のほうが話が簡単なので、先に ASP.NET Core で説明します。
コンソールアプリなどでは必要となる汎用ホストについては後述します。
IHostedService の実装
公式のドキュメントに詳しく書いてありますが、ほとんどのバックグラウンドタスクではタスクの開始や終了といった動作は同じであるため、IHostedService
を実装した抽象クラスを作成しておくと良いようです。
サンプルコードをそのまま引用します。
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
そして、この抽象クラスを継承してバックグランドで動かしたいサービスクラスを定義します。
コードの概要は以下です。
public class SampleService : BackgroundService
{
public SampleService()
{
// コンストラクタへの引数は自動で DI される
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// ここに常駐処理を定義する
}
}
ExecuteAsync
内では while
で常駐処理を定義し、タスクの終了のチェックなども行います。
以下、公式のドキュメントのサンプルを引用します。
public class GracePeriodManagerService : BackgroundService
{
private readonly ILogger<GracePeriodManagerService> _logger;
private readonly OrderingBackgroundSettings _settings;
private readonly IEventBus _eventBus;
public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
{
//Constructor’s parameters validations...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"GracePeriodManagerService is starting.");
stoppingToken.Register(() =>
_logger.LogDebug($" GracePeriod background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"GracePeriod task doing background work.");
// This eShopOnContainers method is querying a database table
// and publishing events into the Event Bus (RabbitMQ / ServiceBus)
CheckConfirmedGracePeriodOrders();
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
_logger.LogDebug($"GracePeriod background task is stopping.");
}
.../...
}
DI コンテナへのサービスの追加
Startup.cs
の ConfigureServices
メソッドで、サービスクラスを DI コンテナに登録します。
IHostedService
を実装したクラスを AddHostedService
で登録していきます。
(AddSingleton
でも可)
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddHostedService<MyHostedServiceA>();
services.AddHostedService<MyHostedServiceB>();
// または
services.AddSingleton<IHostedService, MyHostedServiceA>();
services.AddSingleton<IHostedService, MyHostedServiceB>();
}
汎用ホスト
ASP.NET Core 以外(例えばコンソールアプリなど)では汎用ホストを作成する必要があります。
基本的なコードは以下です。
public static async Task Main(string[] args)
{
var host = new HostBuilder()
.Build();
await host.RunAsync();
}
IHostedService の実装
前述の ASP.NET Core と同様のため省略します。
DI コンテナへのサービス追加
IHostedService
を DI するためにも、DI コンテナにサービスを追加する必要があります。
ConfigureServices
で行います。
var host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyHostedServiceA>();
services.AddHostedService<MyHostedServiceB>();
// または
services.AddSingleton<IHostedService, MyHostedServiceA>();
services.AddSingleton<IHostedService, MyHostedServiceB>();
})
.Build();
await host.RunAsync();
ConfigureServices
の中身については ASP.NET Core と同じです。
他にもいろいろ設定可能ですが、ここでは DI コンテナしか関係ないので、省略します。
詳細は以下を参照してください。
参考 URL
- https://docs.microsoft.com/ja-jp/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/background-tasks-with-ihostedservice
- https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2
- https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2