ViewData vs ViewBag vs TempData

C# Advent Calendar の6日目を担当します。
このブログでまともなことを書くのは初めてです。これを期にふざけるのはやめて、ちゃんとした技術ブログにしていきたいです。エンジニアと言い張って給料泥棒して生活をしてきましたが、そろそろバレそうなので C# を勉強しようと思っています。

ASP.NET MVC で、Model を介さずに一時的にデータを保管する方法が複数あります。名前も抽象的かつ似ているので、混乱しないために整理します。初心者が半分妄想で書いており間違いが108つほどあるはずなので、ご指摘いただければ幸いです。あと、極めてさりげない形でハイキュー!!の名言を紹介しています。

ViewData vs ViewBag

両方とも、Controller/View 層でデータを渡したいときに使います。違いは型の安全性です。ViewData はキャストか型変換が必要です。どちらも System.Web.Mvc に定義されています。

namespace System.Web.Mvc
{
    // 略
    public dynamic ViewBag { get; }
    public ViewDataDictionary ViewData { get; set; }
    // 略
}

ViewData は Dictionary 、ViewBag は dynamic object なので、値の代入と取り出しはこんなかんじになります。

// Controller
public ActionResult Index()
{
    ViewData["Message1"] = "負けたくないことに理由っている?";
    ViewBag.Message2     = "今までのぜんぶ・・・全部無駄だったみたいに言うな!!";
    return View();
}
// View
@ViewData["Message1"]  // -> 負けたくないことに理由っている?
@ViewBag.Message2      // -> 今までのぜんぶ・・・全部無駄だったみたいに言うな!!

ViewBagのほうが*1自由度は高いですが、せっかく C# なのに Intellisense を使えないつらみがあります。ハイキュー!!は名言がいっぱいで熱いですし、潔子さんのことは愛しています。

(ViewData / ViewBag) vs TempData

ViewData と ViewBag は、View が描画された時点( =ActionResult が返った時点)で値が破棄されるのに対し、TempData は保持されます。そのため、リダイレクト先に値を渡したいときなどに使います。というかそういうときにのみ使いましょう。

Dictionary なので基本的に ViewData と同じですが、取り出し方によって状態が変わります。

// Controller
public ActionResult Index()
{
    TempData["Message3"] = "プライド以外に何が要るんだ!!!";
    return RedirectToAction("About");
}
// View
// 値を保持しないで取り出す(次回以降リクエストでは取得不可)
@TempData["Message3"]       // -> プライド以外に何が要るんだ!!!

// 値は保持したまま取り出す(次回以降リクエストでも取得可)
@TempData.Peek("Message3")  // -> プライド以外に何が要るんだ!!!

TempData.Keep()あるいはTempData.Keep("Message3")TempData["Message3"] と同一リクエスト内で実行すると、次回以降リクエストで取得可にすることもできます。このような振る舞いをするのは、TempData が Session(補足参照)のラッパーだからです。あの内気な山口が、月山を想うあまり出た熱い言葉、心に刺さります。泣きました。

まとめ

表にしたぞい。

ViewData ViewBag TempData
Dictionary dynamic object Dictionary
ライフタイム 現在のリクエスト 現在のリクエスト 現在のセッション
型安全性
用途となるデータ渡し ControllerからViewへ ControllerからViewへ Controller(ActionMethod)間

せっかく書いたのでぜひご活用くださいといいたいところですが、基本的には全部使ってほしくないです。嫌いです。デバッグしづらいです。 PRGパティーーーーンなど、どうしても使いたいときに限定しましょう。具体的にはサーバでバリデーションした結果のメッセージを表示したいときとかですかね。 知らない外国の人もこう言っています。

ViewData/ViewBag are evil and their usage imply a poorly designed ASP.NET MVC application.
ref. asp.net mvc 3 - If there is ViewBag for ViewData, why is there no TempBag for TempData? - Stack Overflow

原則としては横着せずに Model(ViewModel)に Property を定義して入れる is 正義です。

補足

ASP.NET 5(MVC 6)で TempData を使うとき

Nugetしてあげる必要があります。
project.json に以下を追加し、Terminal で dnu restore を実行します。

"Microsoft.AspNet.Session": "1.0.0-*"
"Microsoft.Extensions.Caching.Memory": "1.0.0-*"

Startup.cs に以下追加します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddCaching();
    services.AddSession();
}

public void Configure(IApplicationBuilder app)
{
    app.UseSession(); // AddMvc() より前に書くんやで!!!!
    app.AddMvc();
}

参考:Sessions in ASP.NET Core 1.0

Session

もうひとつ、Controller、View の間で値を一時的に保存する方法として、 Session があります。一応ご紹介しておきます。

Session
HttpSessionStateBase
ライフタイム 現在のセッション
型安全性
用途となるデータ渡し Controller(ActionMethod)間

ただし、Session は権限(Authorization)と完全に切り離されておりセキュリティ的にマヂハンパナクヤベエらしいです。まあ使わないでしょ。

参考:AppSec Street Fighter - SANS Institute | Session Attacks and ASP.NET - Part 1 | SANS Institute

及川徹の魅力

スラムダンクの仙道を髣髴とさせる「主人公のライバルのライバル」キャラクターです。イケメン・天才で飄々とした表向きとは異なり、幾度と無く立ちふさがるウシワカ・後ろから急速に追いかけてくる影山という2人の天才の間で苦悩し続けます。クールを装いながらどのキャラクターよりも貪欲に勝ちにこだわり努力し続ける姿は作品中で最も人間らしく、これまでのスポーツマンガにはない魅力的な「非天才」キャラクターに仕上がっています。インハイ予選、生まれ持った才能の差から逃げずに向き合い、勝つためにチームを作ってきた及川の言葉がかっこいいです。

─飛雄 急速に進化するお前に 俺は負けるのかもしれないね
─でもそれは今日じゃない

あ、ViewData とかは使わないでください。

*1:ViewBag はMVC 3から