はじめに
.NET でアプリを作るとき、コンソールアプリ、Web アプリ、Worker Service では見た目がかなり違います。この記事は、これらのアプリを開発する .NET エンジニア向けに、共通する基盤を解説します。
たとえば、コンソールアプリでは Main を書き、Web アプリでは Program.cs の中で WebApplication.CreateBuilder を呼びます。このように、開発者の視点では書き方が大きく異なります。
しかし、実際にはこれらに共通する「基盤」があります。
それが .NET Generic Host です。Microsoft Learn では、Generic Host は次のように説明されています。
A host is an object that encapsulates an app's resources and lifetime functionality, such as:
- Dependency injection (DI)
- Logging
- Configuration
- App shutdown
IHostedServiceimplementations— .NET Generic Host - .NET | Microsoft Learn
この一文がとても重要です。
Generic Host は「DI」「ログ」「構成」「Graceful shutdown(正常な停止)」「IHostedServiceによるバックグラウンド実行」を一つのオブジェクトにまとめて管理する仕組みです。Web アプリ、コンソールアプリ、Worker Service で同じ考え方を利用できます。
つまり、.NET における大きなポイントは次の 2 つです。
- コンソール、Web、Worker Service で、アプリの起動・停止・ライフタイム管理の考え方が共通している
- その共通基盤の上に、依存解決や設定、ログといった機能が乗っている
この考え方は、単なる .NET らしい書き方ではなく、現代的なアプリ開発で欠かせない「依存性の注入」と「アプリのライフタイム管理」を設計に落とし込むための土台です。
Generic Host とは何か
Generic Host は、IHost と HostApplicationBuilder / HostBuilder を中心に構成される仕組みです。
Microsoft Learn の Generic Host ドキュメントでは、IHostApplicationBuilder と IHostBuilder の 2 つの構成方法が紹介されています。
-
Host.CreateApplicationBuilder(...)- 新しい .NET の推奨スタイル -
Host.CreateDefaultBuilder(...)- 既存コードとの互換性重視のスタイル
どちらも最終的にはIHostを生成し、Build()とRun()でアプリを起動します。
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<Greeter>();
IHost host = builder.Build();
host.Run();
このコードだけを見るとコンソールアプリのように見えますが、Web アプリの WebApplicationBuilder も最終的には同じホストの思想を持っています。
The .NET Generic Host is responsible for app startup and lifetime management.
— .NET Generic Host - .NET | Microsoft Learn
つまり、Web アプリでUseRoutingやMapGetによる「HTTP 処理」の部分だけを見ていても、実際には同じIHostを土台にしているのです。
また、Web アプリやコンソールアプリに限りません。長時間動くサービスやデスクトップアプリのような非 HTTP 系のアプリでも、Generic Host を手動で組み込めば、同じ「ホストとしての責務」を持つ設計にできます。WPF のようなアプリでは HTTP そのものは使わないものの、DI / 構成 / ログ / ライフタイム管理を同じ感覚で持てるのが、Generic Host の大きな強みです。
ここからは、この共通基盤が具体的に提供する機能を一つずつ見ていきます。
1. DI: 標準で依存性の注入を提供する
現代的なアプリでは、依存性の注入(Dependency Injection, DI)は当たり前の設計です。new で直接インスタンスを作るのではなく、必要なものを外から渡す設計にすることで、テストしやすさや変更しやすさが格段に上がります。
Microsoft Learn でも DI は「ASP.NET Core の中核機能」として扱われています。
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
— Dependency injection in ASP.NET Core | Microsoft Learn
Generic Host はこの DI をサービスコンテナとして提供します。登録したサービスはIServiceProviderが解決し、クラスのコンストラクタへ自動的に注入します。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<WeatherService>();
IHost host = builder.Build();
var weather = host.Services.GetRequiredService<WeatherService>();
さらに、実際にはこんな書き方が普通です。
public sealed class WeatherService
{
public string GetToday() => "晴れ";
}
public sealed class AppService
{
private readonly WeatherService _weather;
public AppService(WeatherService weather)
{
_weather = weather;
}
public void Run()
{
Console.WriteLine(_weather.GetToday());
}
}
AppService は WeatherService を new で作っていません。サービスコンテナに登録された WeatherService を、Generic Host が解決してくれます。
この仕組みがとても重要なのは、Web アプリでどのサービスをどこで追加するかがアプリの構造を決めるからです。
- 認証サービス
- データアクセス層のサービス
- ロガー
- キャッシュ
- API クライアント
- ドメイン処理サービス
これらを一箇所にまとめて登録し、必要な箇所で注入します。これが現代的なアプリ設計の基本です。
DI を「自分で手作業で作る」のではなく、ホストが持つコンテナで管理します。これは Web アプリだけでなく、コンソールアプリにも非常に大きな恩恵をもたらします。
次に、この DI と並ぶもう一つの土台である設定管理を見ていきます。
2. Configuration: 設定を決まった形で一元管理できる
Generic Host のもう一つの価値は、設定を一貫した方法で取り扱えることです。
Microsoft Learn の Configuration の説明では、代表的な設定ソースとして次のものが列挙されています。
appsettings.json- 環境変数
- Azure Key Vault
- コマンドライン引数
- カスタムプロバイダー
- インメモリの .NET オブジェクト
App configuration in ASP.NET Core is performed using one or more configuration providers. Configuration providers read configuration data from key-value pairs using a variety of configuration sources.
— Configuration in ASP.NET Core | Microsoft Learn
この仕組みは Generic Host のbuilder.Configurationに乗っています。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
string? connectionString = builder.Configuration["ConnectionStrings:Default"];
string? environment = builder.Configuration["Environment"];
また、Host.CreateApplicationBuilder で作成した Generic Host は既定で次の設定ソースを扱います(.NET Generic Host - .NET | Microsoft Learn)。
appsettings.jsonappsettings.{Environment}.json- User secrets(Development 環境)
- 環境変数
- コマンドライン引数
アプリはこの順序で設定ソースを読み込み、後から読み込んだ設定ほど優先します。そのため、環境ごとの設定を簡単に切り替えられます。
特に大事なのは、アプリの設定が「アプリ固有の値」だけでなく、「ホストの構成」も含めて一貫したIConfigurationとして扱えることです。
たとえば、開発環境専用の設定ファイルを追加で読み込むには、次のように書きます。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true);
これはコンソールアプリ、Web アプリ、Worker Service でも同じです。
環境ごとに appsettings.Development.json や appsettings.Production.json を切り替えられるのは、A/B 環境、ローカル/本番、CI/CD などの現実的な運用にとても便利です。
設定を統一できたところで、次はもう一つの重要な基盤であるログを見ていきます。
3. Logging: 既定のログ機構と拡張性が揃う
もう一つの大きな利点は、.NET に標準で入っているログ基盤が Generic Host に乗っていることです。
Microsoft Learn のログ記事は、ILogger と ILogger<T> を中心に構成されており、特に以下のログプロバイダーを紹介しています。
- Console
- Debug
- EventSource
- EventLog(Windows のみ)
ASP.NET Core supports high performance, structured logging via the
ILoggerAPI to help you monitor app behavior and diagnose problems. Logs are written to different destinations by configuring logging providers.— Logging in .NET and ASP.NET Core | Microsoft Learn
Generic Host ではbuilder.Loggingに対してログプロバイダーやログレベル設定を追加できます。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Information);
そして、サービスでは ILogger<T> をコンストラクタで受け取るだけでログを出せます。
public sealed class AppService
{
private readonly ILogger<AppService> _logger;
public AppService(ILogger<AppService> logger)
{
_logger = logger;
}
public void Run()
{
_logger.LogInformation("アプリを開始しました");
}
}
この設計が非常に便利なのは、ログの出力先を切り替えても、コード側の利用方法は変わらないことです。
- ローカル開発では Console
- 本番では Application Insights
- 監視ツールでは OpenTelemetry
- 企業環境では Syslog や Azure Monitor
といったように、プロバイダーを差し替えれば、コードの変更なしに運用を変えられます。
さらに .NET のログモデルはMicrosoft.Extensions.Loggingの上に成り立っていて、サードパーティ製のログプロバイダーもほぼ同じインターフェースを使います。
そのため、Generic Host のログ機構は「標準のログ」だけではなく、既存の運用基盤に乗せるときにも自然に接続できます。
ここまでは DI・設定・ログという基盤を見てきました。次は、アプリの動的なライフタイム管理を担う Hosted Services を見ていきます。
4. Hosted Services: アプリの開始と停止を管理できる
Generic Host は、単に DI やログのコンテナではありません。アプリのライフタイムも管理します。
Microsoft Learn ではかなり明確に、IHostedService と IHostApplicationLifetime が取り扱われています。
When a host starts, it calls IHostedService.StartAsync on each implementation of IHostedService registered in the service container's collection of hosted services.
— .NET Generic Host - .NET | Microsoft Learn
IHostedServiceを登録しておくと、ホストの開始時にはStartAsyncを、停止時にはStopAsyncを呼び出します。
BackgroundServiceはIHostedServiceを実装した抽象クラスです。StartAsyncとStopAsyncの詳細を自分で実装する代わりに、ExecuteAsyncを実装して長時間実行する処理を記述できます。次のコードは Worker Service で使われる基本形です。
using Microsoft.Extensions.Hosting;
public sealed class WorkerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("処理中...");
await Task.Delay(5000, stoppingToken);
}
}
}
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<WorkerService>();
IHost host = builder.Build();
host.Run();
この書き方は、長時間動く処理をまとめて管理したいときに非常に強力です。Web アプリの BackgroundService でも、コンソールアプリの定期処理でも、考え方はほぼ同じです。
起動時の処理を管理できるなら、次に気になるのはアプリを正しく止める方法です。
5. Graceful shutdown: 正しく止める仕組みがある
Generic Host の大きな利点の一つは、graceful shutdown に対応していることです。
IHostApplicationLifetime を注入すれば、アプリが停止する直前にクリーンアップや保存処理を走らせられます。
using Microsoft.Extensions.Hosting;
public sealed class AppLifetimeHandler
{
private readonly IHostApplicationLifetime _lifetime;
public AppLifetimeHandler(IHostApplicationLifetime lifetime)
{
_lifetime = lifetime;
_lifetime.ApplicationStopping.Register(OnStopping);
}
private void OnStopping()
{
Console.WriteLine("アプリの停止を開始します");
}
}
このパターンが重要なのは、停止時に「すぐ落とす」のではなく、処理したいことを整えてから止める設計ができる点です。
Web アプリでは HTTP 接続の終了やリクエストの完了待ち、コンソールアプリでは作業の保存やリソースの解放に使えます。サービスが長く動くシステムでは、止め方の設計が実運用の信頼性を大きく左右します。
Microsoft Learn は Generic Host の説明の中で、アプリの開始と停止を一元管理することを重視しています。
By including all of the app's interdependent resources in a single object, the host enables control of application startup and graceful shutdown.
— .NET Generic Host in ASP.NET Core | Microsoft Learn
ここまでの5つの機能がどのように一つのIHostにまとまるかを、最後に整理します。
まとめ: Web・コンソールアプリ・Worker Service を同じ設計思想で作れる
ここまで見てきた DI・Configuration・Logging・Hosted Services・Graceful shutdown は、すべて IHost という一つの土台の上に乗っています。この土台の考え方は、Web アプリ、コンソールアプリ、Worker Service で共通しています。
Web アプリの WebApplicationBuilder は、見た目こそ HTTP サーバー向けですが、内部では Generic Host と同じ仕組みを使っています。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<WeatherService>();
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();
builder.Services にサービスを登録し、builder.Configuration で設定を読み、builder.Logging でログを設定し、app.Run() でアプリを起動します。この流れは、コンソールアプリの HostApplicationBuilder の使い方とほとんど変わりません。
Worker Service では、Host.CreateApplicationBuilder に BackgroundService を登録し、ホストのライフタイムに合わせて長時間実行する処理を管理します。
The Generic Host can be used with other types of .NET applications, such as Console apps.
— .NET Generic Host - .NET | Microsoft Learn
もちろん、Web アプリには HTTP サーバーやミドルウェアなど、Web 特有の機能があります。しかし、その土台は Generic Host であり、Web で便利な仕組みのほとんどは、その上に乗っているものだと理解するとわかりやすいです。
冒頭で挙げた「起動・停止・ライフタイム管理の共通化」と「その上に乗る DI・設定・ログといった機能」という 2 つのポイントは、ここまでの章がそのまま対応しています。Generic Host がもたらす具体的な利点をあらためて整理すると、次のとおりです。
- 依存解決を一貫して扱える(
IServiceCollection/IServiceProvider) - 設定の読み込み順と環境差分を統一できる(
IConfiguration) - 出力先を変えながら、同じ API でログを扱える(
ILogger<T>) - アプリの開始と停止を制御できる(
IHostApplicationLifetime) -
BackgroundServiceやIHostedServiceで長時間実行を管理できる - Worker Service / コンソールアプリ / Web アプリのように、性質の違うアプリでも同じ設計思想で開発できる
エンジニアにとって、Generic Host の価値は「アプリの再利用可能な土台を持てること」に尽きます。単なる ASP.NET Core の一部ではなく、.NET アプリを構築するための共通基盤として理解しておくとよいです。そうすれば、コンソールアプリでも Web アプリでも、そして Worker Service のようなサービス型アプリでも、同じ土台の上に設計している感覚を持てるようになります。