目次
概要
ASP.NET Coreで標準化されている「汎用ホスト(Generic Host)」の仕組みをコンソールアプリケーションに適用し、依存性の注入(DI)、構成ファイル(appsettings.json)の読み込み、ロギング機能を統合管理する実装パターンです。 これにより、コンソールアプリであってもテスト容易性が高く、保守しやすい疎結合なアーキテクチャを実現できます。
仕様(入出力)
- 入力
- 起動引数(args)
- 構成ファイル(appsettings.jsonなど)
- 出力
- ホスト経由で起動されたサービスクラスによる処理実行
- コンソールおよび設定されたプロバイダへのログ出力
- 動作フロー
- ホストビルダーによる初期化(DIコンテナ生成)。
IHostedServiceを実装したメインサービスの起動。- ビジネスロジックの実行。
- 処理完了後のアプリケーション停止(シャットダウン)。
基本の使い方
NuGetパッケージ Microsoft.Extensions.Hosting をインストールし、Program.cs でホストを構築します。
dotnet add package Microsoft.Extensions.Hosting
コード全文
機能ごとにクラスを分割した構成です。エントリーポイント、設定クラス、ホスト管理サービス、ビジネスロジックの4つに分かれています。
1. Program.cs (エントリーポイント)
ホストの構築と実行を行います。
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
namespace BatchApplication
{
class Program
{
static async Task Main(string[] args)
{
// ホストを構築し、実行を開始する
// RunConsoleAsyncを使うとCtrl+Cでのキャンセルにも対応しやすい
await CreateHostBuilder(args).Build().RunAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// Startupクラスへサービス登録処理を委譲
new Startup(hostContext.Configuration).ConfigureServices(services);
});
}
}
2. Startup.cs (DI設定)
サービスの登録処理を集約します。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace BatchApplication
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// 実際の処理を行うクラスをDIコンテナに登録
services.AddTransient<IBatchProcessor, SampleBatchProcessor>();
// ホストのライフサイクルで実行されるメインサービス
services.AddHostedService<BatchExecutorService>();
}
}
}
3. BatchExecutorService.cs (実行管理)
アプリの起動と終了を制御するホストサービスです。
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BatchApplication
{
public class BatchExecutorService : IHostedService
{
private readonly IHostApplicationLifetime _appLifetime;
private readonly IBatchProcessor _processor;
private readonly ILogger<BatchExecutorService> _logger;
// コンストラクタインジェクションで依存関係を受け取る
public BatchExecutorService(
IHostApplicationLifetime appLifetime,
IBatchProcessor processor,
ILogger<BatchExecutorService> logger)
{
_appLifetime = appLifetime;
_processor = processor;
_logger = logger;
}
// アプリ起動時に呼ばれるメソッド
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("バッチ処理を開始します...");
try
{
// ビジネスロジックの実行
await _processor.ExecuteAsync();
}
finally
{
// 重要: 処理が終わったらアプリケーションを明示的に終了させる
_appLifetime.StopApplication();
}
}
// アプリ終了時に呼ばれるメソッド
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("バッチ処理を終了します。");
return Task.CompletedTask;
}
}
}
4. SampleBatchProcessor.cs (ビジネスロジック)
実際の処理内容を記述するクラスです。
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace BatchApplication
{
// インターフェース定義
public interface IBatchProcessor
{
Task ExecuteAsync();
}
// 実装クラス
public class SampleBatchProcessor : IBatchProcessor
{
private readonly ILogger<SampleBatchProcessor> _logger;
public SampleBatchProcessor(ILogger<SampleBatchProcessor> logger)
{
_logger = logger;
}
public Task ExecuteAsync()
{
// ここに実際の業務ロジックを記述
_logger.LogInformation("メイン処理を実行中...");
// 擬似的な処理待機
Console.WriteLine("データの変換処理を行っています...");
return Task.CompletedTask;
}
}
}
カスタムポイント
- 設定ファイルの読み込み
Host.CreateDefaultBuilderはデフォルトでappsettings.json、appsettings.{Environment}.json、環境変数を読み込みます。設定値が必要な場合はIConfigurationをインジェクトして取得してください。
- ログ出力先の変更
- 標準ではコンソールログが出力されます。ファイルへの出力などが必要な場合は、
SerilogやNLogなどのライブラリを追加し、ConfigureLoggingで設定を行います。
- 標準ではコンソールログが出力されます。ファイルへの出力などが必要な場合は、
注意点
- StopApplicationの重要性
- コンソールアプリ(バッチ)の場合、処理が終わってもホストプロセスは自動的には終了しません。必ず
IHostApplicationLifetime.StopApplication()を呼び出して、アプリを正常終了させる必要があります。
- コンソールアプリ(バッチ)の場合、処理が終わってもホストプロセスは自動的には終了しません。必ず
- 非同期の実装
- すべてのメソッドチェーンは
Taskベースの非同期処理となります。ブロッキングなコード(Task.Wait()や.Result)を書くとデッドロックの原因になるため、必ずawaitを使用してください。
- すべてのメソッドチェーンは
応用
環境変数の利用
環境変数 DOTNET_ENVIRONMENT を Development や Production に設定することで、読み込む設定ファイルを自動的に切り替えることができます。
// 実行時の環境変数設定例 (PowerShell)
// $env:DOTNET_ENVIRONMENT = "Development"
// dotnet run
まとめ
汎用ホストを導入することで、コンソールアプリの構造が「起動処理」「構成設定」「ビジネスロジック」に明確に分離されます。これによりコードの見通しが良くなるだけでなく、単体テストの導入や機能拡張が容易になり、長期的にメンテナンス可能なアプリケーションを構築できます。
