ASP.NET(System.Web)アプリと ASP.NET Core アプリを段階的に共存させていると、「ログイン後の状態を引き継ぎたい」「セッション値を共有したい」という要求がほぼ必ず出てきます。SystemWebAdapters で構成したのに、Session_Start の値は読めるのにコントローラーで設定した値が Core 側で null になる――その原因の当たりを付け、すぐ試せる直し方と現実的な回避策を整理します。
結論から:最短で疑うべきポイント
この手の「ASP.NET(System.Web)と ASP.NET Core でセッション値を共有したいのに、Core 側で null になる」問題は、まず次の順で当たりを付けると遠回りしません。
- SystemWebAdapters とターゲットフレームワーク(.NET バージョン)の組み合わせで既知の不具合・非互換が踏んでいないか(例:SystemWebAdapters 1.3.0 × .NET 8 で
HttpContext.Sessionが null になる報告) - 「共有できていない」のではなく、共有はできているが“更新が反映されていない”(セッション保存タイミング/読み込みキャッシュ/同一リクエスト内の順序)状態ではないか
- どうしても不安定なら、“セッションそのものを共有”から発想を変え、共有したい値だけ Cookie/共有ストレージに逃がす
背景:ASP.NET(System.Web)と ASP.NET Core でセッション共有が必要になる場面
典型的なのは、既存の ASP.NET Framework(System.Web)アプリをすべて作り直すのではなく、新規機能だけ ASP.NET Core で作りつつ、段階的に移行するケースです。共存期間が長いほど、次のような要求が発生します。
- ログイン状態(ユーザー識別)を引き継ぎたい
- カート、入力途中のフォーム、ウィザード進捗などの状態を共有したい
- System.Web 側の資産を残しつつ、Core 側に新機能を追加したい
このとき「今のコードが System.Web.HttpContext.Current.Session 前提だから、セッションをそのまま共有できれば楽」という発想になりがちですが、移行期の“セッション共有”は、実は一番ハマりやすい領域です。今回の現象も、その典型です。
今回の症状を整理:何が起きているのか
まずは現象を言語化します。ここが曖昧だと、設定変更をしても「直ったのか・偶然なのか」が判別できません。
| 操作 | System.Web 側での設定 | ASP.NET Core 側での結果 | 読み解き |
|---|---|---|---|
| 新規セッション開始 | Global.asax の Session_Start() で値をセット | 参照できる | 少なくとも「同一セッションとして見える経路」は成立している可能性が高い |
| 通常の画面遷移・API 呼び出し | 別コントローラー/別アクションで値をセット | null になる/反映されない | 更新が保存されていない、または Core 側が更新前の状態を参照している |
ここで重要なのは、「Session_Start で入れた値が読める」という事実は、セッション共有が完全に崩壊している状態とは少し違うという点です。
つまり「セッション ID がまったく別」「Cookie が飛んでいない」だけが原因とは限らず、“更新の同期”だけが壊れている可能性を疑う価値があります。
最初の落とし穴:Core 側で参照している「Session」がどれか
ASP.NET Core には、少なくとも次の 2 種類の“セッションっぽいもの”が登場します。移行期はここが混ざりやすく、別物を読んでいて null になるケースが現実に多いです。
| 種類 | API 例 | 何を指しているか | 移行期の注意点 |
|---|---|---|---|
| System.Web のセッション | System.Web.HttpContext.Current.Session | 従来 ASP.NET の HttpSessionState | Core 側でこれを使うには SystemWebAdapters の仕組み(共有/互換 API)が必要 |
| ASP.NET Core のセッション | Microsoft.AspNetCore.Http.HttpContext.Session | Core の ISession(バイト列ベース) | 設定(AddSession() / UseSession())がないと利用できず、System.Web のセッションとは別物 |
「System.Web 側で Session["X"] = ... したのに、Core 側で HttpContext.Session.GetString("X") を見ている」ようなパターンだと、設計上別セッションなので当然 null になります。
まずは、Core 側でどちらの API を読んでいるのかをログで固定してください。
前提を揃える:セッション共有でまず確認すべきチェックリスト
原因の切り分けを速くするために、先に“前提条件”を機械的に潰します。特に移行途中は、IIS 設定・ドメイン・Cookie・フレームワークバージョンが混ざりやすいので、チェックリスト化しておくと後で効きます。
| チェック項目 | 確認方法 | NG だと起きること |
|---|---|---|
| 同じブラウザで、同じホスト名(ドメイン)にアクセスしている | アドレスバーのホスト名を揃える(example.com と www.example.com は別扱い) | Cookie が共有されず別セッション扱いになり、当然 null になる |
| セッションクッキーが送受信できている | 開発者ツールで Cookie を確認(Secure/SameSite/Domain/Path) | Core 側で毎回新規セッションになり、Session_Start 以外が読めないように見える |
| ロードバランサ配下の場合、スティッキーか共有ストアになっている | 複数台構成・複数ワーカーの有無を確認 | 片方のノードにだけ値があり、別ノードでは null(再現が不安定) |
| System.Web 側のセッションモード(InProc/StateServer/SQLServer 等) | Web.config の <sessionState> を確認 | InProc だと「プロセスを跨ぐ共有」が成立しにくく、構成によっては更新が見えない |
| セッションに入れている型が“共有”に向く | 文字列や数値など単純型か、シリアライズ可能かを確認 | 片側では持てても、もう片側で復元できず null 相当の挙動になることがある |
| 更新した直後に別アプリへ“内部転送”していない | リバースプロキシ/転送ミドルウェア/Server.Transfer の有無を確認 | 保存前のスナップショットを Core 側が見てしまい「Session_Start だけ見える」状態になることがある |
上の項目がすべて OK でも起きるのが、今回のような SystemWebAdapters のバージョン・.NET バージョン起因の問題です。次で最優先に潰します。
まず疑うべきは「SystemWebAdapters と .NET バージョンの組み合わせ」
提供されている事例として、SystemWebAdapters 1.3.0 × .NET 8 の組み合わせで HttpContext.Session が null になる(またはセッション連携が期待どおり動かない)ケースが報告されています。
この手の“移行支援系”ライブラリは、フレームワークの差分(ホスティング、ミドルウェア、暗号化、Cookie、互換 API の実装差)に影響されやすく、パッケージ更新=機能改善だけでなく、挙動変更や未対応領域が出ることがあります。
そこでおすすめの切り分けは、「いったん動く組み合わせ」に寄せて再現性を見ることです。たとえば次のように検証します。
| 検証パターン | 狙い | 期待する観察結果 |
|---|---|---|
| (現状)SystemWebAdapters 1.3 系+.NET 8 | 問題が発生している構成を固定 | コントローラーで設定したセッションが Core 側で null |
| SystemWebAdapters 1.2 系+.NET 6(最小構成で) | バージョン起因かを切り分け | ここで正常化するなら、構成・コードよりも組み合わせ起因の可能性が上がる |
| SystemWebAdapters 1.2 系+.NET 6(本番相当の構成で) | 環境依存(LB/複数台/サブドメイン)を加味 | 最小構成では OK なのに本番相当で NG なら、Cookie/ドメイン/ストアが疑わしい |
ポイントは、「最新版に上げて直す」ではなく「まず原因を固定する」ことです。
もし 1.2+.NET 6 で再現しないなら、次にやるべきは設定の見直しよりも、既知不具合・制約の確認と、問題が起きる最小条件の特定です。
ログで確かめる:セッションが「同じ」か「更新されている」か
「本当に同じセッションを見ているのか」「更新が保存されているのか」を、体感ではなくログで判断します。おすすめは次の 2 点です。
- System.Web 側:セッションに値を書き込んだ直後と、レスポンス直前で同じキーを読み直してログ出力する
- Core 側:同じリクエストで、受け取った Cookie(セッション ID 相当)と取得した値をログ出力する
これで「書き込めていない(System.Web 側で消えている)」のか、「書き込めているのに Core 側で見えない(連携側の問題)」のかが切り分けられます。
フォーラム/Issue に相談するときの“最短セット”
SystemWebAdapters 側の制約や既知不具合の可能性がある以上、最終的にはコミュニティ/Issue を当てるのが近道になることがあります。その際、次の情報を最初から揃えておくと、往復が激減します。
- SystemWebAdapters のバージョン(Core 側/System.Web 側それぞれ。関連パッケージがある場合はそれも)
- ターゲットフレームワーク(例:.NET 6 / .NET 8、.NET Framework のバージョン)
- ホスティング形態(IIS、アプリプール、プロセス外/内、リバースプロキシの有無)
- セッションストア(InProc / StateServer / SQLServer / Redis 等)
- 再現手順(どの URL で、どのキーをセットして、どの URL で読むと null になるか)
- 最小構成(「Session_Start では読めるが Controller では読めない」を再現する最短コード)
特に“最小構成”は強力です。実案件のコードのままでは、Cookie ポリシーや認証、モジュール、例外処理が絡んで原因が見えなくなります。
「セッションに文字列を 1 個入れる→遷移→読む」だけに絞った上で、再現するかどうかを確認しましょう。
現実解:共有したい値だけ Cookie で渡す(セッション共有をやめる)
移行プロジェクトでは、技術的に“できる”よりも、運用で安定して事故らないことが重要です。
その観点で一番現実的な回避策が、「セッションをそのまま共有」ではなく“共有したい値だけ”を Cookie に載せて渡す方式です。
たとえば「Core 側で必要なのはユーザー ID(または一時キー)だけ」というケースは多いです。セッション全体を共有しようとすると、型互換・保存タイミング・ストア・バージョン差分の影響を受けますが、Cookie で 最小限の値だけ渡すと問題の面積が激減します。
Cookie 方式の設計ポイント
| 項目 | 推奨 | 理由 |
|---|---|---|
| Cookie 名 | SharedSession など用途が分かる名前 | 既存 Cookie と衝突しにくく、運用で判別しやすい |
| 格納する値 | 機微情報は入れず、ID か一時トークンのみ | 漏えい・改ざん時の被害を最小化できる |
| HttpOnly | 有効 | JavaScript から読めないようにして XSS 時の被害を下げる |
| Secure | HTTPS 前提で有効 | 平文通信での盗聴リスクを下げる |
| SameSite | 基本は Lax、クロスサイト要件があるなら None + Secure | CSRF と互換性のバランスを取る |
| Domain | 同一ドメイン/サブドメイン設計に合わせて設定 | ここがズレると送信されず、結局 null になる |
実装例:ASP.NET(System.Web)側で Cookie を発行する
System.Web 側のコントローラー(または認証後の処理)で、共有したい値を Cookie に入れます。ここでは「ユーザーを識別するための一時キー」を例にします。
public ActionResult Login()
{
// 例:サーバー側で生成・管理する一時キー(推測されにくいランダム値)
var sharedKey = Guid.NewGuid().ToString("N");
// 例:sharedKey とユーザー情報の対応は DB/キャッシュに保存しておく(後述)
// SaveSharedKey(sharedKey, userId);
var cookie = new HttpCookie("SharedSession", sharedKey)
{
HttpOnly = true,
Secure = true, // HTTPS 前提
Path = "/",
Expires = DateTime.UtcNow.AddMinutes(30)
// Domain = ".example.com" // サブドメイン間で共有する場合のみ検討
// SameSite は .NET Framework のバージョンやパッチで扱いが変わるため環境に合わせて
};
Response.Cookies.Add(cookie);
return RedirectToAction("Index");
}
実装例:ASP.NET Core 側で Cookie を読む
Core 側では、受け取った Cookie の値を取り出し、そのキーでサーバー側の共有ストア(DB/Redis 等)から必要な情報を引きます。Cookie に機微情報を入れない設計にすることで、安全性と運用性が上がります。
public IActionResult Index()
{
if (!Request.Cookies.TryGetValue("SharedSession", out var sharedKey) || string.IsNullOrEmpty(sharedKey))
{
// Cookie がない=未ログイン扱い、または System.Web 側へ誘導
return Redirect("/Account/Login");
}
// 例:sharedKey を使って共有ストアからユーザー情報を取得
// var userId = LoadUserId(sharedKey);
// if (userId == null) { ... }
return View();
}
同一ドメイン(またはサブドメイン)設計が前提である点だけは注意してください。ドメイン設計がバラバラだと、Cookie 方式は成立しません。
より堅牢な方法:DB/Redis など共有ストレージに寄せる
Cookie は「共有したい値が少ない」場合に強力ですが、共有したい情報が増えるほど設計が難しくなります。
その場合は、思い切って “セッション共有”をやめ、共有ストレージに状態を置く設計が運用上安定します。
考え方はシンプルです。
- ブラウザには キー(ID)だけを持たせる(Cookie など)
- 実データは DB/Redis/分散キャッシュに保存する
- System.Web と Core の両方が、同じキーで同じストアを引く
これなら、SystemWebAdapters の挙動差分や、セッション保存タイミングの罠を避けやすくなります。また、ロードバランサ配下でも安定しやすいです。
共有ストア方式が向いているケース
- 複数台構成(スケールアウト)で、セッション不整合が致命的
- 共有したい値が増えがち(画面状態、権限、ウィザード途中状態など)
- 移行が長期化し、System.Web と Core の共存期間が長い
- 将来的にマイクロサービス化・分割を見据えている
手段の比較:どれを選ぶべきか
最後に、現場で選びやすいように、代表的な手段を比較しておきます。結論としては、移行初期は Cookie(最小値)で逃がし、落ち着いたら共有ストアに寄せるのが安定しやすいです。
| 手段 | メリット | デメリット/ハマりどころ | おすすめ度 |
|---|---|---|---|
| SystemWebAdapters でセッションを共有 | 既存コードの変更が少ない/段階的移行に沿う | バージョン差分や制約の影響を受けやすい。環境要因で不安定になりやすい | 移行の短期決戦なら有力 |
| 共有したい値だけ Cookie で渡す | 仕組みが単純。影響範囲が小さく、切り戻しもしやすい | ドメイン設計が前提。機微情報を入れると危険(署名/暗号化設計が必要) | まず試す現実解 |
| DB/Redis 等の共有ストレージに寄せる | スケールアウトしやすく、運用が安定。将来の分割にも強い | 実装が増える(ストア設計、TTL、削除、障害時の挙動) | 長期運用で最有力 |
移行を成功させるための実務的アドバイス
最後に、セッション共有まわりで移行が詰まるチームをよく見ます。そこで、実務的に効く方針をまとめます。
- セッションを“設計負債”として扱う:共有できたら勝ちではなく、最終的に減らす計画を立てる
- 共有したい値を棚卸しする:「本当に必要なのは何か」を 3 つ以内に絞ると一気に簡単になる
- ユーザー識別は認証に寄せる:セッションでログイン状態を表現している場合、まずは認証基盤(Cookie 認証/トークン等)を揃える
- 不具合は“構成の固定→最小再現”で潰す:闇雲に設定をいじらない
まとめ
Session_Start の値は見えるのに、コントローラーで設定した値が Core 側で null になる場合、最優先で疑うべきは SystemWebAdapters と .NET バージョンの組み合わせです。特定バージョンで問題が報告されているなら、まずは 1.2 系+.NET 6 など“動く組み合わせ”で切り分けるのが近道になります。
それでも安定しない場合は、仕組み上の制約や既知不具合の可能性があるため、再現条件を揃えてフォーラム/Issue で相談するのが効率的です。運用上の現実解としては、共有したい値だけを Cookie で渡す、または DB/Redis 等の共有ストレージに寄せる設計に切り替えると、移行途中でも事故りにくくなります。

コメント