動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

ASP.NET MVC の ViewBag / ViewData / TempData / Session 使い分け — 業務SEが踏む寿命と型安全の3つの罠

バイブス父さん
現役の業務SE
2026年7月3日9 min read
ASP.NET MVC の ViewBag / ViewData / TempData / Session 使い分け — 業務SEが踏む寿命と型安全の3つの罠

みなさんこんにちは!ヒロポンです!

WinForms から ASP.NET MVC に来て、最初に「??」ってなったのがこれ。Controller から View に値を渡す方法が、ViewBagViewDataTempDataSession4つもある

ん?どれ使えばええんや、と。

見た目は似てるのに、寿命が違ったり、片方は読むと消えたり。知らずに使うと地味にハマるやつです。

この記事は、ASP.NET ViewBag ViewData 違いを軸にしてます。4つの箱をスコープ・型安全・寿命で整理して、業務SEが踏む3つの罠まで一気に潰す。.NET Framework 4.7.2 / MVC 5 前提です。

⏱ 4つの箱の早見表(先に結論)

迷ったらここだけ見れば足ります!!

ASP.NET MVC の値受け渡し4手段(ViewBag/ViewData/TempData/Session)を、スコープ(寿命)・型安全・リダイレクト跨ぎ・中身の実体・本命用途で比較した表

ざっくり言うと、同じリクエスト内なら ViewBag/ViewData、リダイレクトを跨ぐなら TempData、ずっと持つなら Session。これが基本の振り分けです。

こんな感じで覚えとくと、もう迷いません。

それぞれの使いどころ

ViewBag と ViewData(同じ箱の別 API)

Controller から View に少量の値を渡す、一番よく使うやつ。

// Controller
public ActionResult Index()
{
    ViewBag.Message = "こんにちは";   // dynamic でアクセス
    ViewData["Count"] = 10;            // 辞書でアクセス
    return View();
}
@* View (Razor) *@
<p>@ViewBag.Message</p>
<p>@ViewData["Count"]</p>

ここ大事なんですが、ViewBagViewData は中身が同じ箱です。ViewData["Message"] で入れたものを ViewBag.Message で取り出せる。

ViewBagViewDatadynamic でラップしただけなんですよね。

だから「どっちを使うか」は完全に好みの問題。両方使うと混乱するので、プロジェクトで片方に寄せるのが楽です。

TempData(リダイレクトを跨ぎたい時)

「保存しました」みたいなメッセージを、リダイレクトの後に1回だけ出したい。そういう時に使います。

TempData["Flash"] = "保存しました";
return RedirectToAction("Done");

ViewBag/ViewData はリクエストをまたぐと消えます。だからリダイレクト後に出したいなら TempData の出番。

ただし、これに罠があります(後述)。

Session(セッション中ずっと持ちたい時)

ログインユーザー情報みたいに、リクエストをまたいでずっと保持したいもの。これは Session に入れます。

Session["UserId"] = 100;
var id = Session["UserId"];   // ログアウトまで残る

便利です。便利なんですが、何でも詰めるとスケールで頭打ちになります(これも後述)。

業務SEが踏む3つの罠

ここからが本題。4つの箱、それぞれに地雷があります。

罠1: TempData は読むと「次のリクエストで」消える

正直に言うと、俺もこれでハマりました。「リダイレクト先では出たのに、その次の画面で null になる」やつです。

TempData一度読み取ると、その値に「次のリクエストで消す」マークが付く仕様です。リダイレクト後に1回だけ表示する用途に最適化されてる。

値そのものはそのリクエストの終わりまで残るので、同じリクエスト内なら2回読んでも返ってきます。消えるのは読んだ次のリクエストから。ここを「2回目で消える」と勘違いすると、シナリオを読み違えます。

// set 側 → リダイレクト
TempData["Flash"] = "保存しました";
return RedirectToAction("Done");

// Done アクション(次のリクエスト)で読む
var msg = TempData["Flash"];   // 読める。ここで「消費」マークが付く
// → さらにその次のリクエストでは TempData["Flash"] は null

// 次のリクエストまで残したいなら Peek / Keep
var safe = TempData.Peek("Flash");   // 読んでも消費しない(次も残る)
TempData.Keep("Flash");              // 読んだ値を次のリクエストまで延命

Peek は「読むけど消費マークを付けない」、Keep は「読んだ値を次のリクエストまで延命する」。こんな感じで使い分けます。

これを知らないと、リダイレクト先で読めた TempData を次の画面でも使えると思い込んで null を踏みます。「さっきは出たのに??」となるやつです。

罠2: ViewBag は dynamic で、型安全じゃない

これもやられました。ViewBag.Mesage(スペルミス)と書いても、コンパイルは通るんですよ。

ViewBagdynamic なので、プロパティ名のタイプミスや型の間違いをコンパイラが検出してくれません。実行して View を開いて初めて「値が出ない」と気付く。これが地味にしんどい。

ViewBag.Message = "hello";
// View 側で @ViewBag.Mesage と書いてもコンパイルエラーにならない(実行時に空)

対策は、渡す値が多いなら ViewModel(専用クラス)を使うこと。@model UserViewModel で型付きにすれば、スペルミスはコンパイルで弾けます。

ViewBag は「ページタイトルみたいな少量の補助情報だけ」に留めるのが安全です。

罠3: Session に何でも詰めると、スケールアウトで詰まる

これは現場で何度か見ました。Session に大きなオブジェクトを大量に詰めてると、サーバーを増やした時(スケールアウト)に動かなくなる

既定の Session は、そのサーバーのメモリに保存されます。ロードバランサで複数サーバーに振り分ける構成だと、「1台目で入れた Session が2台目で読めない」事故が起きるわけです。

対策は2つ。Session を SQL Server や Redis などの共有ストアに逃がす(SessionStateMode の設定)か、そもそも Session に持たせる量を最小限にすること。

1台構成のうちは平気でも、増設の話が出たら必ず効いてきます。

WinForms のフォーム間値共有との対比

WinForms 出身だと、ここが一番混乱するところ。

WinForms では、フォーム間で値を渡すのにコンストラクタ引数や public プロパティ、static なクラスを使ってました。メモリ上のオブジェクトを直接共有する世界ですね。

ASP.NET はリクエストごとに状態が消える(ステートレス)。だから「どのスコープまで値を生かすか」を毎回選ぶ必要があります。

WinForms の「フォームを閉じるまで生きてる変数」の感覚で Session に何でも入れると、罠3を踏みます。

この「画面遷移と値の寿命」の対応関係は WinForms の Form と Razor View の対応関係 でも整理してるので、合わせて見ると腑に落ちます。

まとめ・チートシート

使い分けを1枚に圧縮すると、こうです。

  • 同じリクエスト内で View へViewBag / ViewData(中身は同じ箱・片方に寄せる)
  • リダイレクトを跨いで1回だけTempData(読むと次のリクエストで消える・Peek / Keep で延命)
  • セッション中ずっとSession(詰めすぎ注意・スケールアウトで詰まる)
  • 値が多いなら、ViewBag ではなく ViewModel(型付き) に寄せる

ここを押さえると、状態管理まわりのバグがいい感じにガクッと減ります。

動作確認メモ: ここで挙げたコードは ASP.NET MVC 5(System.Web.Mvc)前提で、構文と API の妥当性は確認済みです。MVC ランタイムは開発機の Linux では動かせないので、実機(Windows + IIS / Visual Studio)での動作は別途お試しください。

ASP.NET MVC の状態管理を体系的に押さえたいなら、MVC の解説書を1冊持っておくといいです。この手の「どれを使う?」を調べる時間が、まるっと浮きます。

この早見表、ブックマークして使ってください。

よくある質問

Q1. ViewBag と ViewData、どちらを使うべきですか?

中身は同じものなので、機能差はありません。ViewBagdynamicViewBag.Name)、ViewData は辞書(ViewData["Name"])でアクセスするだけの違いです。チーム内でどちらかに統一して、混在させないのが読みやすさのコツです。

Q2. TempData の値が View で消えるのを防ぐには?

読むと「次のリクエストで消える」マークが付くので、次のリクエストでも値を使いたい場合は TempData.Peek("キー名") で消費せずに読むか、いったん読んだ後に TempData.Keep("キー名") で次のリクエストまで延命します。同じリクエスト内で複数回読むだけなら、どちらも要りません(値はそのリクエスト終了まで残るため)。

Q3. ログイン情報は Session に入れていいですか?

ユーザーIDなど少量なら問題ありません。ただし、大きなオブジェクトや大量のデータを入れるのは避けてください。スケールアウト構成で詰まる原因になります。認証情報そのものは Forms 認証の Cookie 側で持つのが基本で、Session は補助的な保持に留めるのが安全です。

次に読むべき記事

以上!

同じ TempData 消滅で詰まった人いたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について