Start Debugging

ASP.NET Core 11 における WebApplication.CreateBuilder vs CreateSlimBuilder vs CreateEmptyBuilder

通常のアプリには CreateBuilder を、TLS プロキシの背後でトリミングまたは Native AOT を使って発行する場合は CreateSlimBuilder を、すべてのサービスを自分で登録したい場合にのみ CreateEmptyBuilder を使いましょう。ここでは機能マトリクスと、選択を左右する落とし穴を紹介します。

通常の ASP.NET Core 11 Web アプリには WebApplication.CreateBuilder(args) を使いましょう。これがデフォルトなのには理由があります。期待されるすべてのホスティング機能を組み込んでくれるからです。WebApplication.CreateSlimBuilder(args) に切り替えるのは、トリミングまたは Native AOT で発行し、かつ TLS を終端するプロキシの背後で実行する場合のみにしましょう。これはバイナリを小さくするために HTTPS、HTTP/3、IIS 統合、静的 Web アセット、そして 2 つのログプロバイダーを削除するからです。WebApplication.CreateEmptyBuilder(...) に手を伸ばすのは、ほぼゼロのベースラインを求め、サーバー、ルーティング、構成を自分で登録するというまれなケースのみにしましょう。この記事は Microsoft.NET.Sdk.Web と C# 14 を用いた .NET 11(執筆時点では Preview 6、GA は 2026 年 11 月)を対象としていますが、これら 3 つのファクトリメソッドはいずれも .NET 8 から存在するため、この指針は .NET 8 から 11 まで変わらず当てはまります。

ここでいう「デフォルト」が実際に意味するもの

3 つのメソッドの違いは、ただ 1 点だけです。あなたのコードが実行される前に、WebApplicationBuilder にどれだけのものを登録するか、という点です。それ以外はすべて、builder.Services コレクション、builder.Build()app.MapGet(...) は同一です。ですから、意思決定のすべては、どのデフォルトを渡してもらいたいか、そしてどれを自分の手で追加し直しても構わないか、に帰着します。

CreateBuilder は完全なデフォルトホストを提供します。CreateSlimBuilder は、トリムセーフで小さくなるように厳選されたサブセットを提供します。CreateEmptyBuilder はほとんど何も提供せず、各要素を自分でオプトインすることを期待します。内部的には、これらは仕組みさえ共有しています。CreateSlimBuilder は、CreateEmptyBuilder が公開するのと同じ空のホストアプリケーションビルダーの上に構築され、その上にスリムなサービスセットを再度追加しています。だからこそ、以下の順序は厳密なスーパーセットの連鎖になっています。CreateBuilderCreateSlimBuilder が行うすべてを含み、それは CreateEmptyBuilder が行うすべてを含みます。

機能マトリクス

各行は ASP.NET Core 11 のドキュメントと WebApplication.cs のソースに照らして検証済みです。「手動」は、その機能があなたのために登録されるわけではないが、示されている呼び出しで追加できることを意味します。

機能CreateBuilderCreateSlimBuilderCreateEmptyBuilder
appsettings.json + appsettings.{env}.jsonありあり手動
ユーザーシークレット(Development)ありあり手動
環境変数 + コマンドライン構成ありあり手動
コンソールログ出力ありあり手動(AddConsole
Debug / EventSource / EventLog ログ出力ありなしなし
Kestrel サーバーフルコア(UseKestrelCore手動(UseKestrelCore
Kestrel の HTTPS エンドポイントありなし(UseKestrelHttpsConfiguration手動
HTTP/3(QUIC)ありなし(UseQuic手動
IIS 統合ありなしなし
静的 Web アセットありなしなし
ホスティングスタートアップアセンブリ / UseStartupありなしなし
正規表現および alpha ルーティング制約ありなしなし
ルーティング / MapGet などありあり手動

この表から得られる最も重要なポイント:CreateSlimBuilder は依然として構成ソースとコンソールログ出力を保持します。これは、あなたが毎日使うものを取り除いているわけではありません。クラウドネイティブでプロキシを前面に置くデプロイでは通常必要とされないプロトコルおよびプラットフォーム機能、それに本番でめったに読まない 3 つのログプロバイダーを削除しているのです。

CreateBuilder を選ぶべきとき

これがデフォルトであり、ほとんどのアプリではデフォルトのままにしておくべきです。

CreateSlimBuilder を選ぶべきとき

CreateSlimBuilder は、Native AOT Web API テンプレート(dotnet new webapiaot)のデフォルトとなることを特に目的として .NET 8 で導入されました。以下があなたのデプロイに当てはまる場合に選びましょう。

スリムを選んだ後で HTTPS や HTTP/3 が必要だと分かっても、ビルダーを切り替える必要はありません。明示的に追加し直しましょう:

// .NET 11, C# 14
var builder = WebApplication.CreateSlimBuilder(args);

// Re-enable HTTPS endpoints that CreateSlimBuilder omits by default.
builder.WebHost.UseKestrelHttpsConfiguration();

// Re-enable HTTP/3 (QUIC) if a client actually needs it.
builder.WebHost.UseQuic();

var app = builder.Build();
app.MapGet("/", () => "Hello from a slim host");
app.Run();

CreateEmptyBuilder を選ぶべきとき

CreateEmptyBuilder(WebApplicationOptions) は、組み込みの振る舞いをまったく持たないビルダーを作成します。それが構築するアプリには、あなたが明示的に構成したサービスとミドルウェアだけが含まれます。これは専門的なツールであって、一般的なデフォルトではありません。可能な限り最小のサービスを構築していて、すべての登録を制御したいとき、あるいは ASP.NET Core がリクエストを処理するのに実際どれだけ少なくて済むかを実験しているときに手を伸ばしましょう。

以下は .NET 8 のリリースノートにある標準的な最小の例で、.NET 11 でも変わらずコンパイルできます:

// .NET 11, C# 14
var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());

// Nothing is registered by default, so add the server yourself.
builder.WebHost.UseKestrelCore();

var app = builder.Build();

app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Hello, World!");
    await next(context);
});

Console.WriteLine("Running...");
app.Run();

必要になったときに手作業で追加しなければならない、欠けているものに注目してください:appsettings.json の読み込みはなく、コンソールログ出力はなく、ルーティングもなく(そのため MapGet はなく、代わりに生のミドルウェアを書きます)、構成のバインドもありません。それぞれを明示的な呼び出しで追加します:builder.Configuration.AddJsonFile("appsettings.json")builder.Logging.AddConsole()builder.Services.AddRouting() などです。それこそが空のビルダーの目的そのものです。使うものに対してだけ対価を払うのです。

サイズの話、そしてそれがトリミングの話である理由

3 つすべてが存在する理由は、Native AOT のためのバイナリサイズと起動であって、生のリクエストスループットではありません。JIT コンパイルされたアプリでは、3 つのビルダーは異なるサービスグラフを登録しますが、いったんアプリがウォームになってしまえば、1 秒あたりのリクエスト数の差に価値があるわけではありません。価値が現れるのは、トリミングして AOT コンパイルするときです。

Native AOT Web API テンプレートに関する Microsoft 自身のベンチマークは、Native AOT 発行を、トリミングされたランタイムビルドおよびトリミングされていないランタイムビルドと比較し、AOT アプリが 3 つの中でアプリサイズ、メモリ使用量、起動時間が最も低いと報告しています。.NET 8 のリリースノートは、スペクトラムの空の端についての具体的な基準を示しています:上記の CreateEmptyBuilder の「Hello, World」サンプルを linux-x64 マシンで Native AOT を用いて発行すると、約 8.5 MB の自己完結型ネイティブ実行ファイルが生成されました。この数値は、AOT とトリミングが仕事を終えたときにほぼゼロのベースラインがどう見えるか、を示すものです。

発行されるフットプリントの実際の順序は、大きいものから小さいものへ、CreateBuilder、次に CreateSlimBuilder、次に CreateEmptyBuilder です。しかしそれらの差が開くのは、PublishAot または PublishTrimmed の下でのみです。プレーンなビルドを出荷すれば、スリムまたは空のビルダーの手間を払いながら、その報酬を回収しないことになります。これが最もよくある間違いです:「スリムのほうが速そう」という理由で、通常のデプロイにスリムビルダーを選んでしまうことです。それは実行時に速いのではなく、トリミングしたときに小さいのです。トリミングをしていないなら、スリムの道にコミットする前に Native AOT が実際にあなたに何を強いるか を読む価値があり、Native AOT vs ReadyToRun vs JIT は各発行モードがどこで勝つかを扱っています。

あなたの代わりに選んでくれる落とし穴

これを決めるのが好みであることはめったにありません。たいてい以下のいずれかが決めます。

結論、もう一度

デフォルトは CreateBuilder にしましょう。これは、IIS、静的 Web アセット、MVC、Blazor、正規表現のルート制約を使うすべてのアプリを含め、圧倒的多数の ASP.NET Core 11 アプリにとって正しい選択です。CreateSlimBuilder に移るのは、トリミングまたは Native AOT で発行し、かつ TLS を終端するプロキシの背後に位置するときだけです。これはまさに webapiaot テンプレートが対象とするシナリオです。HTTPS や HTTP/3 が必要なら、UseKestrelHttpsConfiguration() または UseQuic() の呼び出し 1 つで追加し直しましょう。CreateEmptyBuilder は、最後の 1 つまで自分で登録し、その下限を計測したい、本当に最小のサービスのために懐に忍ばせておきましょう。やってはいけない唯一のことは、速いという理屈で、通常の JIT デプロイにスリムまたは空のビルダーを選ぶことです。それはトリミングしたときに小さいのであって、実行時に速いのではなく、通常のビルドでは摩擦だけを得て見返りは得られません。そもそも古いホストをこのモデルに移行しようとしているなら、どのファクトリメソッドを呼ぶかを最適化する前に越えるべき関門が IWebHostBuilder から WebApplication.CreateBuilder への移行 です。

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< 戻る