本文へスキップ
ASP.NET Web Forms から Blazor への移行ガイドのアイキャッチ画像
Architecture

ASP.NET Web Forms から Blazor への移行ガイド

公開: 更新: 約9分で読めます

ASP.NET Web Forms は 2000 年代の業務アプリを数多く支えてきましたが、その基盤は .NET Framework 専用で、サポートは最新の .NET へ引き継がれていません。.NET 10(2025 年 11 月リリースの LTS)にも Web Forms 相当の仕組みは存在せず、プロジェクトをそのまま移すワンクリックのアップグレードパスもありません。とはいえ、これは「作り直すしかない」という話ではありません。業務ロジックを .NET ライブラリへ切り出し、UI を Blazor で段階的に置き換えていく現実的な道筋があります。本記事では、Web Forms から Blazor への移行を実務目線で整理します。

Web Forms に直接のアップグレードパスがない理由

Web Forms は System.Web、Page ライフサイクル、ViewState、サーバーコントロール(<asp:GridView> など)といった、.NET Framework の実行基盤に密結合した仕組みで成り立っています。これらは ASP.NET Core にはポートされておらず、Microsoft も新しい .NET へ移す計画がないことを明言しています。つまり Web Forms アプリは .NET Framework 4.8 の上でしか動きません。.NET Framework 4.8 自体は当面サポートされますが、新機能は追加されず、パフォーマンス改善やクラウドネイティブ機能の恩恵は受けられない保守フェーズにあります。

したがって移行とは「Web Forms を .NET 10 に変換する」ことではなく、「Web Forms が担っていた責務を、ASP.NET Core と Blazor のモデルで作り直す」ことになります。全体像は .NET モダナイゼーション完全ガイド にまとめていますので、あわせて参照してください。

Web Forms と Blazor をリバースプロキシで並走させ段階移行する構成
YARP で新旧を並走させ、ページ単位で Web Forms から Blazor へ段階的に置き換える。

基本戦略は「段階的移行」

一括の全面書き換え(ビッグバン移行)は、規模が大きいほどリスクが跳ね上がります。数か月から年単位でコードが凍結され、その間の機能追加が止まり、切り替え当日に全機能を一度に検証しなければなりません。現実解は段階的移行です。大きく次の流れで進めます。

  • 診断 — 画面数、ページ間の依存、サーバーコントロールやサードパーティ部品の使用状況を棚卸しする。
  • ロジック分離 — コードビハインドに埋もれた業務ロジックを、UI 非依存の .NET ライブラリ(net10.0 ターゲット)へ切り出す。
  • 並走 — 新旧アプリを同時に動かし、ページ単位で移し替えられる状態を作る。
  • UI 置換 — Blazor コンポーネントを 1 画面ずつ作り、旧ページと差し替える。
  • 切替 — すべての画面が移り終わったら旧アプリを撤去する。

この進め方なら、移行中も業務を止めず、問題が起きても影響範囲を移行済みの画面に限定できます。

Incremental Migration と YARP による並走

Microsoft は Web Forms や MVC の段階移行のために「incremental migration(インクリメンタル移行)」というアプローチと、それを支える System.Web アダプタや Microsoft.AspNetCore.SystemWebAdapters を提供しています。中心的な考え方は、新しい ASP.NET Core アプリを手前に置き、YARP(Yet Another Reverse Proxy)で未移行のパスを既存の Web Forms アプリへ転送することです。

ユーザーから見た入口は常に新アプリで、移行が済んだ画面は新アプリが直接処理し、まだ済んでいない画面はプロキシ経由で旧アプリが処理します。これはストラングラーフィグ(Strangler Fig)パターンそのもので、ルーティング設定を書き換えるだけで移行の進捗にあわせて少しずつ新側へ寄せていけます。

var builder = WebApplication.CreateBuilder(args);

// System.Web アダプタと Blazor を登録
builder.Services.AddSystemWebAdapters();
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

// 未移行パスの転送先(旧 Web Forms アプリ)
builder.Services.AddHttpForwarder();

var app = builder.Build();

app.UseSystemWebAdapters();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

// 移行済み以外は旧アプリへフォールバック
app.MapForwarder("/{**catch-all}", "https://legacy-webforms.internal");

app.Run();

セッションや認証 Cookie を新旧で共有する仕組みも SystemWebAdapters が提供するため、ログイン状態を保ったまま画面をまたいで移行できます。この並走の考え方は .NET Framework 全体の移行でも共通で、.NET Framework 4.x から .NET 10 への移行プレイブック でも詳しく扱っています。

Blazor のレンダリングモードと選び方

Blazor は .NET 8 以降、1 つのアプリの中でコンポーネントごとにレンダリングモードを選べる統合モデル(Blazor Web App)になりました。主なモードは 3 つです。

  • Server — コンポーネントをサーバー側で実行し、UI の差分を SignalR 接続経由でブラウザへ送る。初期表示が速く、既存のサーバーサイド資産やデータベースへ直接アクセスしやすい。常時接続とサーバーリソースが必要。
  • WebAssembly(WASM) — .NET ランタイムをブラウザで動かし、クライアント側で完結する。サーバー負荷が小さくオフライン動作も可能だが、初回ダウンロードが大きく、データアクセスは API 経由になる。
  • Auto — 初回は Server で素早く描画し、WebAssembly ランタイムのダウンロードが済んだ以降は WASM に切り替える。両者の利点を組み合わせる。

Web Forms からの移行では、まず Server モードから始めるのが素直です。既存の業務ロジックやデータベース接続をサーバー側でそのまま呼べるため、API 層を新設せずに移行を進められます。運用が安定し、クライアント負荷分散やオフライン要件が出てきた段階で、画面単位に Auto や WebAssembly へ寄せていくとよいでしょう。レンダリングモードはコンポーネント単位で指定できるので、後から見直せます。

ViewState とポストバックからコンポーネント+状態管理へ

Web Forms の中核はページ全体のポストバックと ViewState でした。ボタンを押すたびにフォーム全体がサーバーへ送られ、サーバーが HTML を再生成してページ全体を返し、コントロールの状態は ViewState としてページに埋め込まれて往復します。この「ページ中心・状態はページに付随」というモデルが、Web Forms アプリの構造を決めていました。

Blazor はこれをコンポーネント中心のモデルに置き換えます。UI は再利用可能なコンポーネントの木として構成され、状態は C# のフィールドやプロパティとしてコンポーネントが保持します。ユーザー操作はイベントハンドラで受け、状態を書き換えると、Blazor が変更のあった部分だけを差分描画します。ページ全体のポストバックは発生しません。

  • ViewState — コンポーネントのフィールド/プロパティ、および複数コンポーネントで共有する状態は DI 登録したスコープ付きサービスに置き換える。
  • ポストバックイベント(Button_Click など)— @onclick などのイベントハンドラに置き換える。
  • サーバーコントロール(GridView / Repeater)— @foreach による描画や QuickGrid などのコンポーネントに置き換える。
  • Page ライフサイクル(Page_Load)— OnInitializedAsync / OnParametersSetAsync などのライフサイクルメソッドに対応させる。

.aspx/コードビハインドから .razor コンポーネントへ

実際の対応を、シンプルな例で見てみます。ユーザー名を入力してあいさつを表示する Web Forms のページは、典型的には次のような .aspx とコードビハインドの組で書かれていました。

<%@ Page Language="C#" CodeBehind="Greeting.aspx.cs"
    Inherits="MyApp.Greeting" %>
<asp:Content runat="server">
  <asp:TextBox ID="NameBox" runat="server" />
  <asp:Button ID="GreetButton" runat="server"
      Text="あいさつ" OnClick="GreetButton_Click" />
  <asp:Label ID="ResultLabel" runat="server" />
</asp:Content>
// Greeting.aspx.cs(コードビハインド)
public partial class Greeting : System.Web.UI.Page
{
    protected void GreetButton_Click(object sender, EventArgs e)
    {
        ResultLabel.Text = $"こんにちは、{NameBox.Text} さん";
    }
}

これを Blazor コンポーネントにすると、マークアップとロジックが 1 つの .razor ファイルにまとまり、状態は C# のフィールドになります。ポストバックやサーバーコントロールは姿を消し、双方向バインドとイベントハンドラだけで表現できます。

@* Greeting.razor *@
@page "/greeting"

<input @bind="name" />
<button @onclick="Greet">あいさつ</button>

@if (!string.IsNullOrEmpty(result))
{
    <p>@result</p>
}

@code {
    private string name = string.Empty;
    private string? result;

    private void Greet()
    {
        result = $"こんにちは、{name} さん";
    }
}

この例では ViewState もポストバックも不要になり、name フィールドが入力状態を保持し、Greet が状態を更新して差分描画が走ります。実際の業務画面では、この Greet に相当する処理を先に切り出した .NET ライブラリのサービスへ委譲し、コンポーネントは呼び出しと表示に徹する形にします。

データアクセスと認証の置き換え

Web Forms アプリでよく使われていた周辺技術も、ASP.NET Core の標準へ寄せます。

  • データアクセス — SqlDataSource や ADO.NET 直書き、EF 6 は EF Core 10 へ移す。DbContext を DI 登録し、コンポーネントやサービスから非同期に呼ぶ。LINQ の挙動差異は移行時に検証が必要。
  • 認証・認可 — Membership や Forms 認証は ASP.NET Core Identity へ移行する。Blazor 側では AuthenticationStateProvider と <AuthorizeView> で認証状態を扱う。
  • 設定 — web.config の appSettings / connectionStrings は appsettings.json と環境変数へ再マッピングする。
  • DI — Web Forms にはコンテナが標準で無かったため、Microsoft.Extensions.DependencyInjection を前提にサービスを設計し直す。

データアクセスは非同期が基本になる点に注意が必要です。ASP.NET Core では同期 I/O が既定で禁止されており、Web Forms 時代の同期呼び出しは async/await へ書き換えることになります。API 分離が絡む構成では、ASP.NET Core × Next.js の BFF パターン も設計の参考になります。

移行の進め方と優先順位づけ

最後に、実際のプロジェクトでの進め方を整理します。まず診断で全画面を棚卸しし、依存の少ない末端の画面(マスタ管理や参照系の一覧など)から着手すると、副作用が小さく検証も容易です。並行して、コードビハインドに散らばった業務ロジックを .NET ライブラリへ切り出しておくと、その資産は旧 Web Forms 側からも新 Blazor 側からも呼べるため、移行の途中でロジックを二重管理せずに済みます。

  • 診断 — 画面・依存・サードパーティ部品を棚卸しし、移行順序を決める。
  • ロジック分離 — 業務ロジックを UI 非依存の net10.0 ライブラリへ抽出する。
  • 並走 — YARP と SystemWebAdapters で新旧を同一入口の下に並べる。
  • UI 置換 — 依存の少ない画面から Blazor コンポーネントへ 1 つずつ置き換える。
  • 切替 — 全画面の移行が済んだら旧アプリとプロキシ経路を撤去する。

Web Forms からの移行は、技術的には UI モデルの転換が中心ですが、成否を分けるのは「どの順序で、どこまで並走させながら移すか」という計画です。業務を止めずに進められるかどうかは、この設計にかかっています。

エンハンスド株式会社では、Web Forms を含むレガシー資産の分析から移行の実行まで支援しています。

この記事をシェア

コピーしました

関連記事