.NET Generic Hostとは - DI・設定・ログの土台

· 更新日: · · C#, .NET, Generic Host, Worker, 設計

更新履歴(4件・最終更新 2026年08月02日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
DI(依存性注入)を含む用語を冒頭で定義し、最小例の直後に既定のコンソールロガーの出力と`Production`が既定になる理由を追記しました。`appsettings.json`の躓きについては、Generic Hostがこのファイルを省略可能として読むため、実際の症状は例外ではなく「値が取れない」であることに直しました。Web HostからGeneric Hostへの経緯の節と、採用するかどうかの目安表を追加しました。
本文中の関連記事へのリンクの文言が、リンク先の現在のタイトルと食い違っていたのを、実際のタイトルに揃えました。本文の内容は変えていません。
初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589633)

この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。

小村 豪(2026)「.NET Generic Hostとは - DI・設定・ログの土台」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589633 https://staging.comcomponent.com/blog/2026/03/14/000-dotnet-generic-host-what-is/

DOI(最新版)
10.5281/zenodo.21589633
DOI(この版)
10.5281/zenodo.21732658

.NET でコンソールアプリや worker を書き始めると、最初は Main に少し処理を書くだけで済みます。 ただ、少し育つと、だいたいこのへんが増えてきます。

  • appsettings.json を読みたい
  • 環境変数で上書きしたい
  • ILogger でログを出したい
  • サービスの生成を new だらけにしたくない
  • バックグラウンドでループを回したい
  • Ctrl+C やサービス停止できれいに終わりたい

ここで出てくるのが Generic Host です。 ただ、この名前も少し混ざりやすいです。

  • Host.CreateApplicationBuilderHost.CreateDefaultBuilder は何が違うのか
  • IHost は DI コンテナーと同じなのか
  • BackgroundService と何がつながっているのか
  • ASP.NET Core の WebApplicationBuilder と別物なのか
  • コンソールアプリでも使う価値があるのか

このへんが混ざると、Generic Host が「Web アプリ専用っぽいもの」に見えたり、逆に「何でも host にすべきもの」に見えたりします。どちらも少し雑です。

この記事では、主に .NET 6 以降の現在の実務感を前提に、次の 4 つを先に整理します。

  • Generic Host の正体
  • 何をまとめて面倒を見てくれるのか
  • Host.CreateApplicationBuilder / Host.CreateDefaultBuilder / WebApplication.CreateBuilder の関係
  • どこから入ると穏やかか

目次

  1. まず結論(ひとことで)
    • 1.1. 先に用語を決めておく
  2. まず見る整理表
    • 2.1. Generic Host が抱えているもの
    • 2.2. builder の違い
    • 2.3. なぜ入口が複数あるのか
  3. Generic Host の全体像(図)
  4. Generic Host で何がうれしいか
    • 4.1. 起動処理を 1 か所に寄せられる
    • 4.2. DI / 設定 / ログが最初からつながる
    • 4.3. 正常終了と常駐運転を扱いやすい
  5. 最小構成
    • 5.1. コンソールアプリで使う最小例
    • 5.2. appsettings.json
    • 5.3. BackgroundService を載せる
  6. 典型パターン
    • 6.1. 短命なコンソールツール
    • 6.2. worker / バックグラウンドサービス
    • 6.3. ASP.NET Core の下にもいる
  7. 向いているケース
  8. 向かない / 過剰なケース
  9. はまりどころ
  10. まとめ
  11. 参考資料

この記事の知識マップ

.NET Generic Hostは、依存性注入・設定・ロギング・IHostedServiceやBackgroundServiceによる常駐処理・Ctrl+CやSIGTERMに応じたライフタイム管理を1つの土台にまとめる仕組みで、新しい非WebアプリケーションではHost.CreateApplicationBuilderから組み立てるのが素直とされる。既存コード向けにはHost.CreateDefaultBuilderという経路も残り、ASP.NET CoreのWebApplication.CreateBuilderはかつて別に存在したWeb Hostに代わって、この考え方をWeb用に広げた入口になっている。BackgroundServiceには既定のスコープがないため、scopedなサービスを使うにはIServiceScopeFactoryで明示的にスコープを作る必要があり、代表的なつまずきどころになっている。

.NET Generic Hostの知識マップGeneric HostがDI・設定・ログ・IHostedServiceやBackgroundService・ライフタイム管理をまとめ、Host.CreateApplicationBuilderやWebApplication.CreateBuilderとどうつながるかを示す図利用する利用する利用する利用する利用する実装を担うで構成できるで構成できるの後継利用する前提とする利用する利用する前提とする前提とする利用するGeneric Host依存性注入(DI).NETの構成システム(IConfiguration)Microsoft.Extensions.Logging(ILogger)IHostedServiceHostのライフタイム管理(IHostApplicationLifetime)BackgroundServiceHost.CreateApplicationBuilderHost.CreateDefaultBuilderWebApplication.CreateBuilderWeb Host(IWebHostBuilder)IServiceScopeFactoryオプションパターンWindowsサービス.NET(Core以降)HostApplicationBuilder

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全16件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

1. まず結論(ひとことで)

  • Generic Host は、.NET アプリの 起動と有効期間 をまとめて扱う土台です。
  • その中に、DI、設定、ログ、IHostedService / BackgroundService、アプリの停止処理が入ります。
  • 新しい非 Web アプリでは、まず Host.CreateApplicationBuilder(args) から入るのが素直です。
  • ASP.NET Core の WebApplicationBuilder も別世界ではなく、同じ host の考え方を Web 用に広げた窓口です。
  • つまり Generic Host は、DI コンテナー単体の話ではなく、アプリの組み立て地点と寿命管理をまとめる仕組み です。

要するに、アプリが「引数を読んで 1 回表示して終わり」を少し超えたあたりから、Generic Host はかなり効いてきます。 逆に、そこまで育っていない小さな道具にまで、毎回必ず持ち込むものでもありません。

1.1. 先に用語を決めておく

この記事はこのあと比喩を多めに使うので、先に正確な言い方を置いておきます。

用語 正確に言うと この記事で使う比喩
DI(依存性注入 / Dependency Injection) クラスが必要とする相手を、自分で new せずに外から渡してもらう作り方です。渡す相手をまとめて登録しておく置き場が DI コンテナー(IServiceProvider)で、Generic Host はこれを最初から持っています 配線
Builder(HostApplicationBuilder host を 組み立てるための オブジェクトです。ServicesConfigurationLogging といったプロパティを持ち、ここへ登録していきます。Build() を呼ぶまでアプリは動きません 組み立て台
Host(IHost Build() の結果として得られる、組み立て済みのアプリ本体 です。DI コンテナー、構成、ログ、hosted service を抱え、Run() / RunAsync() で開始してから停止までを面倒見ます 土台
Hosted service(IHostedService / BackgroundService host の開始・停止に合わせて動く処理の入れ物です。host が起動すると StartAsync が呼ばれ、BackgroundService なら ExecuteAsync が走ります 常駐の仕事
Lifetime アプリの開始から停止までの管理です。Ctrl+C、SIGTERM、サービス停止などの合図を受けて、止め方をそろえます 寿命

いちばん混ざりやすいのは Builder と Host です。Builder は組み立てる側、Host は組み立てられた結果 で、Build() がその境目です。ここさえ押さえておけば、以降に出てくる「土台」「箱」「窓口」「入口」といった言い方も、どれを指しているか迷わずに読めます。

DI が初めてなら、こう考えると外しません。new の連鎖を自分で書く代わりに、「この型が必要になったら、この実装を渡してください」と起動時に登録しておき、受け取る側はコンストラクターの引数で受け取るだけにする。その登録先が builder.Services です。

2. まず見る整理表

2.1. Generic Host が抱えているもの

最初に、この箱の中身を分けておくとかなり楽です。

要素 Generic Host が面倒を見ること 何がうれしいか
DI IServiceCollection からサービスを組み立てる new の連鎖を減らしやすい
Configuration appsettings.json、環境変数、コマンドライン引数などをまとめる 環境ごとの差分を扱いやすい
Logging ILogger<T> を使うための基盤を作る ログ出力先を後から差し替えやすい
Hosted service IHostedService / BackgroundService の起動と停止を扱う 常駐処理をアプリ本体と分けやすい
Lifetime IHostApplicationLifetimeIHostEnvironment などを通じて開始・停止を扱う Ctrl+C、SIGTERM、サービス停止で終わり方をそろえやすい

ここで大事なのは、Generic Host が「便利な DI ラッパー 1 個」ではないことです。 実際には、アプリの入口まわりをまとめて配線する箱、くらいの見方がいちばん外しにくいです。

2.2. builder の違い

ここも最初に 1 枚で見るほうが早いです。

入口 主な用途 書き味 まずの選択
Host.CreateApplicationBuilder(args) コンソール / worker などの新規非 Web アプリ builder.Services / builder.Configuration / builder.Logging に直接書く 新規ならこれ
Host.CreateDefaultBuilder(args) 既存コードや古い拡張メソッド主体の構成 ConfigureServices などをチェーンする 既存資産があるならこれ
WebApplication.CreateBuilder(args) ASP.NET Core Web アプリ / API Generic Host に Web 用の都合を足した入口 Web ならこれ

CreateApplicationBuilderCreateDefaultBuilder は、 片方が新機能で片方が別物、という話ではありません。

どちらも同じコア機能と既定動作を持っています。 違うのは主に 書き方の流儀 です。

新しい非 Web アプリなら、いまは Host.CreateApplicationBuilder(args) から入るのが素直です。 WebApplication.CreateBuilder(args) は、その流れを Web 用に広げた入口だと思っておくと整理しやすいです。

2.3. なぜ入口が複数あるのか

入口が複数あるのは、Web 側と非 Web 側が別々に育ってから合流した、という経緯があるためです。

  • もともと ASP.NET Core には Web 専用の Web Host(IWebHostBuilder)があり、非 Web アプリ向けの Generic Host(IHostBuilder)は別に用意されていました。
  • その後 ASP.NET Core 側が Generic Host に寄せられ、Web も非 Web も同じ host の考え方の上に載るようになりました。
  • さらに、コールバックを繋いでいく書き方(ConfigureServices など)に加えて、プロパティへ直接書く書き方(builder.Services など)の入口が増えました。Host.CreateApplicationBuilderWebApplication.CreateBuilder はこちら側です。

現在の公式ドキュメントでは、Host.CreateApplicationBuilder 系(IHostApplicationBuilder)は 新規プロジェクト向けで、現行テンプレートの既定Host.CreateDefaultBuilder 系(IHostBuilder)は 既存コードとの互換のために残っている従来のやり方 と整理されています。どちらも同じコア機能と既定動作を持つ、という説明も明記されています。

.NET Framework や .NET Core 3.1 時代のコードから来ると「なぜ書き方が 2 つもあるのか」と感じますが、新旧の別物が並んでいるのではなく、合流の過程で入口が増えた と見ると納得しやすいと思います。既存資産に合わせる事情がなければ、新規は Host.CreateApplicationBuilder で構いません。

3. Generic Host の全体像(図)

全体像をざっくり図にすると、こうです。

args / 環境変数 / appsettings.jsonHost.CreateApplicationBuilder(args)builder.Configurationbuilder.Servicesbuilder.LoggingIHostedService / BackgroundServicebuilder.Build()IHostRun / RunAsync開始・停止・Ctrl+C・SIGTERM

ふつうは Program.cs で builder を作り、 builder.Services にサービスを足し、 builder.Configurationbuilder.Logging を必要に応じて調整し、 最後に Build() して IHost を得て、Run() / RunAsync() で走らせます。

地味に大きいのは、Host.CreateApplicationBuilder(args) の時点でかなりのものが既に載っていることです。 既定では、たとえば次のようなものが入ります。

  • コンテンツルートは現在のディレクトリ
  • ホスト構成は DOTNET_ プレフィックス付き環境変数とコマンドライン引数
  • アプリ構成は appsettings.jsonappsettings.{Environment}.json、Development の user secrets、環境変数、コマンドライン引数
  • ログは Console / Debug / EventSource / EventLog(Windows のみ)
  • Development 環境では scope 検証と依存関係検証

つまり、何も考えずに 0 から配線しているわけではなく、 最初から「普通に使うぶんにはかなり足りる土台」が置かれています。

4. Generic Host で何がうれしいか

4.1. 起動処理を 1 か所に寄せられる

Generic Host のいちばん地味で大きい効き目は、アプリの入口が散らばりにくくなることです。

アプリが少し育つと、Main のまわりに増えてくるのがこのあたりです。

  • 設定ファイルの読み込み
  • 環境ごとの差し替え
  • ロガーの初期化
  • HttpClient や repository や service の組み立て
  • バックグラウンド処理の起動
  • 終了シグナル時の後片づけ

これを host なしで全部手でつないでいくと、 最初は軽くても、あとからだんだん入口が粘ってきます。

Generic Host を使うと、Program.cs が「依存関係をまとめて組み立てる場所」としてはっきりします。 この整理だけで、コードレビューのしやすさがかなり変わります。

4.2. DI / 設定 / ログが最初からつながる

Generic Host を使うと、DI、設定、ログが最初から同じ土台に乗ります。

たとえばクラスの側では、こんなものを普通に受け取れます。

  • ILogger<T>
  • IConfiguration
  • IHostEnvironment
  • IOptions<T>

ここで効くのは、設定の読み方とサービスの作り方が別々の流儀になりにくい ことです。

設定が 1 個や 2 個なら、IConfiguration["Section:Key"] を直接読むだけでも動きます。 ただ、実務で設定が増えてきたら、IOptions<T> で section ごとにクラスへ束ねたほうが安全です。目安は、キー文字列が 5 個を超えたあたりです。この規模になると、打ち間違いが実行時まで気づけない失敗として出はじめ、どのキーがどこで読まれているかも追いにくくなります。

同じように、ログも ILoggerFactory をあちこちで手作りするより、 必要なクラスへ ILogger<T> を注入したほうが見通しがよくなります。

Generic Host が便利なのは、これらを別々の話にせず、 アプリ全体の土台として一緒に扱える ところです。

4.3. 正常終了と常駐運転を扱いやすい

Generic Host は「どう起動するか」だけでなく、「どう止まるか」も面倒を見ます。

host が起動すると、登録された各 IHostedServiceStartAsync が呼ばれます。 worker サービスでは、BackgroundService を含む hosted service の ExecuteAsync が走ります。

ここでいう「正常終了」は、いきなり処理を切るのではなく、

  • 停止の合図を流す
  • ループや待機を抜ける
  • 接続やリソースを片づける

という順番を踏んで終わることです。

長時間動くアプリでは、ここがかなり大事です。 Ctrl+C、SIGTERM、サービス停止のようなイベントで、アプリ全体の止まり方をそろえやすくなります。

また、終了をアプリ側から要求したいときは、IHostApplicationLifetime.StopApplication() が使えます。 「もう仕事は終わったので、きれいに落ちてほしい」という合図を、host の文脈で出せます。

5. 最小構成

5.1. コンソールアプリで使う最小例

まず大事なのは、Generic Host を使うからといって、 必ず BackgroundService を作る必要はないことです。

1 回だけ走るコンソールツールでも、 DI、設定、ログが欲しいなら Generic Host は十分使えます。

ふつうの console プロジェクトに後から載せるなら、まずは Microsoft.Extensions.Hosting を参照します。

dotnet add package Microsoft.Extensions.Hosting

Program.cs の最小例は、たとえばこうです。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<JobRunner>();

using IHost host = builder.Build();

try
{
    JobRunner runner = host.Services.GetRequiredService<JobRunner>();
    await runner.RunAsync();
    return 0;
}
catch (Exception ex)
{
    ILogger logger = host.Services
        .GetRequiredService<ILoggerFactory>()
        .CreateLogger("Program");

    logger.LogError(ex, "Unhandled exception occurred during job execution.");
    return 1;
}

internal sealed class JobRunner(
    ILogger<JobRunner> logger,
    IConfiguration configuration,
    IHostEnvironment hostEnvironment)
{
    public Task RunAsync()
    {
        string message = configuration["Sample:Message"] ?? "(no message)";

        logger.LogInformation("Environment: {EnvironmentName}", hostEnvironment.EnvironmentName);
        logger.LogInformation("Message: {Message}", message);

        return Task.CompletedTask;
    }
}

dotnet run すると、コンソールにはこう出ます(Message の値は、次の 5.2 で置く appsettings.json から来ています)。

info: JobRunner[0]
      Environment: Production
info: JobRunner[0]
      Message: hello from Generic Host

info: の右にあるのはログのカテゴリ(ここでは ILogger<JobRunner> なので型名)と、イベント ID です。既定のコンソール ロガーは、この「1 行目にカテゴリ、2 行目に本文」の形で出します。EnvironmentProduction なのは、環境変数 DOTNET_ENVIRONMENTASPNETCORE_ENVIRONMENT も設定していないときの既定値だからです。開発時に切り替えるなら、DOTNET_ENVIRONMENT=Development を設定して実行します。

長時間常駐しないなら、RunAsync() まで行かなくてもよいです。 Build() して必要なサービスを解決し、仕事を終えたらそのまま終了する。 それでも Generic Host のうまみは十分使えます。

ここは意外と大事です。 短命なジョブにまで、毎回 Worker テンプレートを持ち込む必要はありません。

5.2. appsettings.json

上の例なら、設定ファイルはこんな最小形で十分です。

{
  "Sample": {
    "Message": "hello from Generic Host"
  }
}

1 つだけ、定番の躓きがあります。コンソール プロジェクトでは、appsettings.json を追加しただけでは 出力フォルダーへコピーされません。プロジェクトのプロパティで「出力ディレクトリにコピー」を「新しい場合はコピーする」にするか、csproj へ次を書きます。

<ItemGroup>
  <Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

忘れたときの症状も知っておくと早いです。Generic Host は appsettings.json省略可能なファイルとして 読むので、無くても例外は出ません。単に値が取れないだけです。上の最小例なら Message: (no message) と表示されます。「エラーは出ないのに設定が効かない」ときは、まず出力フォルダーに appsettings.json があるかを確認してください。

この例では生で configuration["Sample:Message"] を読んでいます。 値を 1 個や 2 個見るだけなら、これで十分です。

ただ、実務で設定が増えてきたら、

  • section ごとにクラスへ分ける
  • IOptions<T> で注入する
  • 起動時に検証する

という形へ寄せたほうが、キー文字列のばら撒きを避けやすいです。

また、Generic Host の既定値では appsettings.json だけでなく、 appsettings.{Environment}.json、環境変数、コマンドライン引数もつながるので、 「開発時だけ差し替える」「本番では環境変数で上書きする」がかなり自然にできます。

5.3. BackgroundService を載せる

長時間動く処理なら、BackgroundService を使うとかなり素直です。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddScoped<PollingJob>();
builder.Services.AddHostedService<PollingWorker>();

using IHost host = builder.Build();
await host.RunAsync();

internal sealed class PollingWorker(
    IServiceScopeFactory scopeFactory,
    ILogger<PollingWorker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));

        while (await timer.WaitForNextTickAsync(stoppingToken))
        {
            using IServiceScope scope = scopeFactory.CreateScope();
            PollingJob job = scope.ServiceProvider.GetRequiredService<PollingJob>();

            await job.RunAsync(stoppingToken);
            logger.LogInformation("Polling completed.");
        }
    }
}

internal sealed class PollingJob(ILogger<PollingJob> logger)
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation("Do work here.");
        return Task.CompletedTask;
    }
}

この例で見ておきたいのは 2 点です。

  1. BackgroundService の本体は ExecuteAsync
  2. scoped な依存関係が欲しいなら、IServiceScopeFactory で scope を作る

BackgroundService 自体には既定の scope がありません。 たとえば DbContext のような scoped サービスを使いたいなら、 上のように job 側を scope の中で解決する形が安全です。

なお、定期実行の道具そのものの選び方は別テーマですが、 async ベースで書くなら PeriodicTimer はかなり穏やかです。 このあたりは、関連記事のタイマー記事ともつながります。

6. 典型パターン

6.1. 短命なコンソールツール

バッチ、変換ツール、メンテナンスコマンドのように、 1 回だけ仕事をして終わるアプリでも Generic Host は普通に使えます。

向いているのは、こんな場面です。

  • 設定ファイルを読みたい
  • ログを出したい
  • HttpClient や repository を注入したい
  • 終了コードを返したい

この手のアプリで、いきなり BackgroundServiceRunAsync() を持ち込むと、 少し重たいわりに、ホストの寿命管理を過剰に使うことになります。

短命ジョブなら、前の最小例のように JobRunner を解決して実行するだけで十分です。

6.2. worker / バックグラウンドサービス

常駐 worker、ポーリング、キュー消費、監視、定期実行のような処理では、 Generic Host と BackgroundService の組み合わせがかなり素直です。

特にうれしいのはこのあたりです。

  • 起動と停止の流れが host 側でそろう
  • ログ、設定、DI が最初から使える
  • Ctrl+C や停止シグナルでキャンセルを流しやすい
  • 常駐処理の本体を Program.cs から分離しやすい

さらに、Windows Service やコンテナの文脈ともつなぎやすいです。 常駐アプリとして育てるなら、Generic Host はかなり自然な土台です。

Windows Service 化する場面では、現在のディレクトリ前提でファイルを探すより、 IHostEnvironment.ContentRootPath を起点に考えたほうが事故が少ないです。 host の文脈で「アプリの基準パス」が決まるからです。

6.3. ASP.NET Core の下にもいる

Web アプリ / API では WebApplication.CreateBuilder(args) を使うので、 ぱっと見では Generic Host と別世界に見えるかもしれません。

でも、感覚としてはかなりつながっています。

  • builder.Services
  • builder.Configuration
  • builder.Logging

の書き味が似ているのは、そのためです。

ASP.NET Core では、HTTP サーバーの起動も host の lifetime の中に入っています。 つまり、Web 側の Program.cs を読んだときに「なぜここで DI や設定やログを触っているのか」が分かりやすくなる、という意味でも Generic Host の理解は効きます。

7. 向いているケース

Generic Host が気持ちよくハマりやすい場面を挙げてみます。

  • 設定、ログ、DI を使うコンソールアプリ
  • queue consumer、poller、watchdog、scheduler 的な worker
  • Ctrl+C や SIGTERM で後片づけしたい長時間実行アプリ
  • 将来的に Windows Service / コンテナ常駐へ育つ可能性があるアプリ
  • ASP.NET Core と同じ拡張群の流儀にそろえたいアプリ

共通しているのは、 「アプリの入口と寿命管理を雑にしたくない」 ことです。

とはいえ、これだけだと線が引きにくいので、目安も置いておきます。次のうち 2 つ以上当てはまるなら、最初から Generic Host に乗せておくと後で楽になることが多いです。

目安 具体的なライン
設定の数 環境ごとに変わる設定が 3 個以上ある(接続先、しきい値、出力先など)
ログ ファイルや Event Log に残す必要がある。標準出力へ書いて終わりではない
実行の形 常駐する。または 1 日 1 回以上、決まった間隔で動く
依存関係 コンストラクターで受け取りたい相手が 3 個以上ある。テストで差し替えたい相手がいる
寿命 Ctrl+C やサービス停止のときに、途中の後片づけが必要
将来 Windows Service かコンテナで動かす可能性がある

8. 向かない / 過剰なケース

逆に、最初から Generic Host を主役にしなくてよい場面もあります。

  • 引数を 1 回読んで 1 回出力して終わるだけの小さなツール
  • 数十分だけ使う雑な検証コード
  • ライブラリプロジェクト
  • 設定を 1 つ読むだけで、DI やログや寿命管理まではいらないケース

ここでは、host を立てるより Main に直接書いたほうが、読む量もファイル数も少なくて済みます。目安としては、7 章の表に 1 つも当てはまらないなら、Generic Host は不要と考えて差し支えありません。

大事なのは、 Generic Host が強いからといって、全部の executable に必須ではない ということです。

9. はまりどころ

最後に、Generic Host 初手で踏みやすい点をまとめます。

  • Generic Host を DI コンテナーとだけ見る
    • 実際には、起動・停止・設定・ログ・hosted service を含む土台です。
  • 新規アプリなのに惰性で Host.CreateDefaultBuilder から始める
    • 既存コードに合わせる事情がなければ、まずは Host.CreateApplicationBuilder のほうが素直です。
  • BackgroundService に scoped サービスを直接入れる
    • hosted service には既定の scope がありません。IServiceScopeFactory で scope を作るほうが安全です。
  • 1 回だけで終わる worker なのに停止を host へ伝えない
    • Worker テンプレートで「run once」をやるなら、仕事が終わった時点で IHostApplicationLifetime.StopApplication() を呼ばないと、host はそのまま走り続けます。
  • 正常終了したいのに Environment.Exit で切る
    • host を使っているなら、きれいに止めたい場面では StopApplication() のほうが筋がよいです。
  • Windows Service で current directory を前提にする
    • ファイル探索は IHostEnvironment.ContentRootPath 起点で考えたほうが安定します。
  • 短命な CLI なのに最初から BackgroundService で囲う
    • 1 回きりの仕事なら、普通のサービスクラスを解決して実行するだけで十分です。
  • BackgroundService の定期実行に callback タイマーを雑に入れる
    • async な流れで書くなら PeriodicTimer のほうが読みやすく、荒れにくいことが多いです。

Generic Host では、 「短命ジョブなのか、常駐ジョブなのか」を最初に分ける だけで、かなり迷いにくくなります。

10. まとめ

Generic Host をひとことで言うと、 .NET アプリの入口と寿命管理をまとめる土台 です。

見ておきたいポイントを振り返っておきます。

  1. Generic Host は DI だけでなく、設定、ログ、停止処理、hosted service を含む
  2. 新しい非 Web アプリなら、まず Host.CreateApplicationBuilder(args) が素直
  3. 短命なジョブなら、BackgroundService を使わず build して実行するだけでもよい
  4. 常駐処理なら、BackgroundService と host の lifetime 管理がかなり効く
  5. BackgroundService には既定の scope がないので、scoped サービスは明示的に scope を作る
  6. ASP.NET Core の WebApplicationBuilder も、考え方としては同じ流れの上にいる

Generic Host は、重たい儀式のための道具ではありません。 設定、ログ、依存関係、起動、終了が少しでも増えてきたら、 それらを壁の中へ逃がさず、入口にまとめておくための道具です。

逆に、まだそこまで要らない小さなツールなら、持ち込まなくてもよいです。 この見分けができると、Generic Host は「なんとなく入れるもの」ではなく、 使いどころのはっきりした実務の土台になります。

11. 参考資料

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

Generic Hostとは何ですか?
.NETアプリの起動と有効期間をまとめて扱う土台です。その中にDI、設定(Configuration)、ログ、IHostedService / BackgroundService、アプリの停止処理が含まれます。単なるDIコンテナーのラッパーではなく、アプリの組み立て地点と寿命管理をまとめる仕組みと捉えると外しにくいです。設定、ログ、依存関係、起動、終了が少しでも増えてきたアプリで効果を発揮します。
Host.CreateApplicationBuilderとHost.CreateDefaultBuilderはどちらを使うべきですか?
新しい非Webアプリなら、Host.CreateApplicationBuilder(args) から入るのが素直です。どちらも同じコア機能と既定動作を持っており、片方が新機能で片方が別物という関係ではありません。違うのは主に書き方の流儀で、CreateApplicationBuilder は builder.Services などに直接書くスタイル、CreateDefaultBuilder は ConfigureServices などをチェーンするスタイルです。既存コードや古い拡張メソッド主体の構成に合わせる事情があるなら CreateDefaultBuilder を選びます。
コンソールアプリでもGeneric Hostを使う価値はありますか?
DI、設定、ログが欲しいなら、1回だけ走るコンソールツールでも十分使えます。必ずBackgroundServiceを作る必要はなく、Build()して必要なサービスを解決し、仕事を終えたらそのまま終了する形でもGeneric Hostのうまみは得られます。逆に、引数を1回読んで1回出力して終わるだけの小さなツールや雑な検証コードには過剰なので、毎回必ず持ち込むものでもありません。
BackgroundServiceでscopedサービスを使うにはどうすればよいですか?
BackgroundServiceには既定のscopeがないため、scopedサービスを直接コンストラクター注入するのは安全ではありません。IServiceScopeFactoryを注入し、ExecuteAsyncの中で明示的にscopeを作って、その中でジョブ側のサービスを解決する形が安全です。DbContextのようなscopedサービスを使いたい場合は特にこの形を意識する必要があります。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る