[創意料理] 再深入一點點對 ViewBag、ViewData、Model 的了解

前陣子跟同事聊到 ASP.NET MVC 傳資料給 View 是用 ViewBag 好還是 Model 好? Google 了一下,沒有標準答案,既然這樣就自己來研究好了,我是從使用 ViewBag 或 Model 要付出什麼代價的角度去切入,提供給各位朋友參考。

ViewBag、ViewData、Model 的來歷

這三個的使用方式我就不多做介紹了,網路有很多文章都在說它們三個要怎麼用,有興趣的朋友請自行 Google。

ViewData

ViewData 從 ASP.NET MVC 1.0 開始就有了,它是 Controller 的一個屬性,型別是 ViewDataDictionary,顧名思義它是一個 Dictionary。

ViewBag

ViewBag 是從 ASP.NET MVC 3.0 開始才有的,它也是 Controller 的一個屬性,型別是 DynamicViewDataDictionary 繼承自 DynamicObject,它本質上也是一個 Dictionary。

疑?看到這邊可能有些人會覺得 ViewBag 不是 dynamic 嗎? 沒錯,ViewBag 是 dynamic,但是是被改寫過的 dynamic,DynamicViewDataDictionary 繼承自 DynamicObject 之後覆寫了 GetDynamicMemberNames()TryGetMember()TrySetMember() 這三個方法,這個我們從原始碼就看得出來,覆寫的這三個方法的內容是從 ViewData 取值跟賦值,所以其實 ViewBag 的內容實際上是存放在 ViewData。

Model

Model 是 View 的一個屬性,是從 ASP.NET MVC 1.0 開始就有的東西,RazorView 被編譯之後都會繼承一個 WebViewPage<TModel> 的類別,Model 是這個類別中的一個屬性,但是其實它的值是來自於 ViewData 的 Model 屬性,看 WebViewPage<TModel> 的原始碼就可以知道了。

所以實際上 ViewBag、Model 都是從 ViewData 來的,雖然不是同一份內容,但是彼此卻是鄰居。

ViewBag? or Model?

使用 ViewBag 或 Model 要付出什麼樣的代價,我從三個面向來提出我個人的看法。

效能

這個效能主要是看在 View 中取值的時候,誰相對上略優一些,那麼我的實驗方式就是在 View 裡面反覆取值一百萬次。

比較方式就在完全預熱後執行 10 個 Run,取花費時間(μs)的最少跟最多出來比,結果如下:

  最少 最多
ViewBag 99,132μs 116,609μs
Model 11,133μs 13,667μs

從結果看來使用 Model 會比 ViewBag 快了大概 7 倍,看起來很多,但是這個是執行百萬次的數據,以這個實驗結果來看,每執行一次 ViewBag 也不過慢個 0.088μs ~ 0.103μs,在不在意就看系統是不是發展到對效能敏感的程度。

我另外實驗了我個人比較常用的寫法,先把 ViewBag 屬性做強制轉型再拿來操作使用,這樣會差多少?

實驗結果讓我驚訝了一下,我以為它會跟 Model 的結果差不多,結果這種方式又比 Model 快大概了 1 倍。

  最少 最多
ViewBag 99,132μs 116,609μs
Model 11,133μs 13,667μs
ViewBag 屬性先強制轉型 4,016μs 6,106μs

這讓我覺得怪怪的,所以往原始碼追了一下,原來在 WebViewPage<TModel> 裡面的 ViewData 型別是 ViewDataDictionary<TModel>,它把原先的 ViewData 又再包了一層,在 ViewDataDictionary<TModel> 裡面的 Model 屬性是 Unboxing 來的,Unboxing 是會消耗一些效能,所以如果用 Model 取值就是不斷地在 Unboxing,這就難怪會比較慢一點了。

ViewBag 雖然是最慢的,但是其實不太明顯,除非我們的系統對於效能是極度敏感的,不然針對效能的表現上,不一定得說要選擇使用哪一種方式。

IntelliSense

IntelliSense 這個方面 Model 天生可強型別的體質完勝,ViewBag 由於是動態型別的原因,IntelliSense 就殘念了。

不過我們可以把 ViewBag 裡面的屬性強制轉型出來,這樣就有 IntelliSense 了。

所以如果單純真的為了 IntelliSense 那應該選擇用 Model,但是如果放不下 ViewBag 動態型別的彈性,那就要多寫一點程式碼了。

維護

對於後續維護方面,ViewBag 及 Model 都有各自麻煩的地方,就 Model 而言每個 View 只能指定一個 Model,我們就需要對每個 View 創建一個類別(我個人是不建議讓多個 View 共用同一個 Model)。

ViewBag 的話,我們雖然可以把資料都直接塞進去,但是可能會造成重覆賦值,或是 Runtime 取值時資料還沒到位而出現 Exception,再加上沒有 IntelliSense 容易打錯字,造成除錯上的一個障礙。

最終從這三個方面看來,選擇用 ViewBag 或 Model,或者混用,就看我們比較在意什麼? 我個人的選擇在上面就有提到了,就是把資料都塞進 ViewBag,然後在 View 裡面做強制轉型指定到新的變數上使用,以上提供給各位朋友參考。