ViewModel、ViewData、ViewBag、TempData、Session之間的區別和各自的使用方法
ViewModel
ViewModel 是一個用來渲染 ASP.NET MVC 檢視的強型別類,可用來傳遞來自一個或多個檢視模型(即類)或資料表的資料。可將其看做一座連線著模型、資料和檢視的橋樑。其生命期為當前檢視。檢視模型屬於強型別,所以在VS中便有智慧提示並且可以進行靜態檢測。
在ASP.NET Core 中使用ViewModel:
先建立一個用於呈現檢視的檢視模型類:
1public class Student 2{ 3public int ID { get; set; } 4public string Name { get; set; } 5public DateTime Birth { get; set; } 6}
在控制器中定義該類:
1public IActionResult Index() 2{ 3Student student=new Student() 4{ 5ID = 1, 6 Birth = new DateTime(1997,1,1), 7Name = "Nanase" 8}; 9return View(student); 10}
在檢視中使用
在Razor檢視開頭使用@model 指定強型別,使用了@model 的檢視成為強型別檢視,強型別檢視可獲得智慧提示和靜態檢查。不使用@mdoel 則後面的 @Model 為動態型別,不會獲得智慧提示和靜態檢查。
同時強型別 Model 可使用輔助器方法,而弱型別 Model 不能使用輔助器方法。
Index檢視:
1 @model Student 2 3 <div> [email protected](m=>m.ID) [email protected](m=>m.ID) 6 </div> 7 <div> 8Birth @Model.Birth 9 </div> 10 <div> 11Name @Model.Name 12 </div>
強型別檢視:
弱型別檢視缺少智慧提示:
最終結果:
ViewData
ViewData是一個Dictionary<string,object>的字典,資料以鍵值對的形式儲存在 ViewData 中。ViewData 用來在控制器和檢視之間傳遞資料,也可以在檢視和分部檢視之間傳遞資料。其生存期為當前檢視渲染結束。由於在使用 ViewData 時返回的是一個object物件,所以應用實際型別的屬性或值時需要使用強制型別轉換。當傳遞的資料作為字串在檢視中使用時不需要進行強制轉換,因為 c# 每個物件存在 ToString 方法,而在 c# 檢視中會自動呼叫該方法。
除直接定義 ViewData[""] 外,ASP.NET Core 還支援對控制器中的屬性使用 [TempData] 特性定義ViewData。
但是 ViewData 不能用於跨請求的情形,即無法在跳轉後的頁面使用跳轉前頁面定義的 ViewData ,而後續提到的 TempData 可以用於跨請求的資料傳遞。
在控制器和檢視間使用ViewData傳遞資料:
1public IActionResult ViewDataTest() 2{ 3ViewData["student"]=new Student() 4{ 5ID = 1, 6Birth = DateTime.Now, 7Name = "Nanase" 8}; 9ViewData["Greeting"] = "Hello"; 10return View(); 11}
在 ViewDataTest 檢視中使用ViewData的資料:
1 @{ 2Student student = ViewData["student"] as Student; 3ViewData["test"] = "test"; 4 } 5 6 @ViewData["greeting"] 7 <div>ID: @student.ID</div> 8 <div>Name: @student.Name</div> 9 <div>Birth: @student.Birth</div> 10 <br/> 11 @Html.ActionLink("Test","Test")
用來測試跨請求的 Test 方法和檢視:
1public IActionResult Test() 2{ 3return View(); 4}
1 @{ 2ViewData["Title"] = "Test"; 3 } 4 5 <h2>Test</h2> 6 7 @ViewData["test"]
結果:
Test檢視:
直接使用 ViewData 中資料的屬性:
在檢視和部分檢視間使用 ViewData 時,部分檢視中的 ViewData 的定義不會覆蓋原檢視的 ViewData 。在檢視和部分檢視之間傳遞 ViewData 時,主要使用的是 PartialAsync 輔助器方法以及 <partial> 標記輔助器方法。
在檢視和部分檢視間使用ViewData:
public static Task<IHtmlContent> PartialAsync(this IHtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData)
第一個引數傳入部分檢視名稱,第二個引數傳入一個 ViewDataDictionary 物件。若不指定第二個引數,則會將當前檢視的 ViewDate 傳入部分檢視。
ViewDataTest2 檢視:
1 <h2>ViewDataTest2</h2> 2 @{ 3ViewData["Student"] = new Student() 4{ 5ID = 1, 6Birth = DateTime.Now, 7Name = "Nanase" 8}; 9ViewData["Greeting"] = "Good morning"; 10 } 11 12 View: @ViewData["Greeting"] 13 <br/> 14 @await Html.PartialAsync("PartialView",new ViewDataDictionary(ViewData)) 15 16 View: @ViewData["Greeting"]
PartialView 檢視:
@{ Student student = ViewData["Student"] as Student; } Partial: @ViewData["Greeting"] <hr/> <div>ID: @student.ID</div> <div>Name: @student.Name</div> <div>Birth: @student.Birth</div> <hr/> @{ ViewData["Greeting"] = "Good afternoon"; } <div>Partial: @ViewData["Greeting"]</div>
結果:
使用 partial 標記輔助器方法:
關於 partial 標記輔助器方法:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/partial?view=aspnetcore-2.1
使用時,為 name 屬性傳遞部分檢視的名稱,為 view-data 屬性傳遞 ViewDataDictionary 物件的名稱:
<h2>ViewDataTest2</h2> @{ ViewData["Student"] = new Student() { ID = 1, Birth = DateTime.Now, Name = "Nanase" }; ViewData["Greeting"] = "Good morning"; } View: @ViewData["Greeting"] <br/> @*@await Html.PartialAsync("PartialView",new ViewDataDictionary(ViewData))*@ <partial name="PartialView" view-data="ViewData" /> View: @ViewData["Greeting"]
結果和使用 PartialAsync 輔助器方法一致。
ViewBag
ViewBag 是一個包裝了 ViewData 的動態型別,從 ControllerBase 繼承而來,因此在檢視頁中也無法使用 ViewBag。由於 ViewBag 是一個動態物件,所以可以為其新增任意屬性。也由於 ViewBag 屬於動態型別,所以可以直接呼叫其中的屬性進行操作。ViewBag 和 ViewData 的區別在於:
- 不能在檢視頁中傳遞特定的 ViewBag。
- 在檢視中雖然可以定義 ViewBag 在當前檢視使用,但不能定義 ViewBag 傳遞到部分檢視。
在檢視頁中使用 ViewBag 失敗。
1public IActionResult ViewBagTest() 2{ 3ViewBag.Student = new Student() 4{ 5ID = 1, 6 Birth = new DateTime(1997,1,1), 7Name = "Nanase" 8}; 9return View(); 10}
ViewBagTest 檢視:
1 <h2>ViewBagTest</h2> 2 3 <div>View: @(ViewBag.Student.ID+1)</div> 4 5 @await Html.PartialAsync("_ViewBagPartial")
_ViewBagPartial 檢視:
1 <div>ID: @ViewBag.Student.ID</div> 2 <div>Name: @ViewBag.Student.Name</div> 3 <div>Birth: @ViewBag.Student.Birth</div>
結果:
TempData
TempData 是一個繼承自 System.Web.Mvc.TempDataDictionary 的型別,是一個 Dictionary<string,object> 的字典。生命期為控制器中生命開始到該 TempData 被讀取結束,即使重定向到另一個控制器的方法,只要一個 TempData 未被讀取,它就仍然存在。在一個訪問過 TempData 的頁面進行重新整理會再次訪問該 TempData ,未被延長生命期的 TempData 將被刪除。TempData 通常用於在動作方法之間傳遞資料(如錯誤資訊)。
如果希望在一次訪問後保留 TempData 物件,則需要在控制器的方法中呼叫 TempData.Keep 方法,或者在檢視中使用 TempData.Peek 方法訪問 TempData 物件。但是需要注意的是 TempData.Keep 方法不會延長重定向的檢視中被訪問的 TempData 物件的生命期。重定向的檢視即指使用 RedirectXXX 抵達的檢視。
由於 TempData 將它本身儲存在 ASP.NET 的 Session 中,因此需要謹慎使用,當將應用程式託管到多個伺服器時 TempData 會出現問題。為解決該問題此時可選擇在 session 的生命期內將使用者的請求全部分配給同一個伺服器,或者在伺服器之間轉發 session 資訊。
1public IActionResult TempDataTest() 2{ 3TempData["error"] = "An error"; 4TempData["greeting"] = "Hello"; 5 6//將 TempData["error"] 生存期延長一次 7TempData.Keep("error"); 8 9return View(); 10} 11 12public IActionResult ReceiveTempData() 13{ 14return View(); 15} 16 17public IActionResult ReceiveTempData2() 18{ 19return View(); 20}
TempDataTest 檢視:
1 @{ 2ViewData["Title"] = "TempDataTest"; 3 } 4 5 <h2>TempDataTest</h2> 6 7 <div>@TempData["error"]</div> 8 9 @*訪問 TempData["greeting"] 並將 TempData["greetng"] 延長一次*@ 10 <div>@TempData.Peek("greeting")</div> 11 <br/> 12 <div>@Html.ActionLink("Another", "Index", "TempData")</div> 13 @Html.ActionLink("ReceiveTempData","ReceiveTempData")
用來測試跨請求的 ReceiveTempData 檢視:
1 @{ 2ViewData["Title"] = "ReceiveTempData"; 3 } 4 5 <h2>ReceiveTempData</h2> 6 7 <div>@TempData["error"]</div> 8 9 @*將 TempData["greetng"] 生命期延長一次*@ 10 <div>@TempData.Peek("greeting")</div> 11 <br/>@Html.ActionLink("ReceiveTempData2", "ReceiveTempData2")
ReceiveTempData2 檢視:
1 @{ 2ViewData["Title"] = "ReceiveTempData2"; 3 } 4 5 <h2>ReceiveTempData2</h2> 6 7 <div>@TempData["error"]</div> 8 <div>@TempData["greeting"]</div>
結果:
使用重定向:
1public IActionResult TempDataTest() 2{ 3TempData["error"] = "An error"; 4TempData["greeting"] = "Hello"; 5TempData.Keep("error"); 6//return View(); 7return RedirectToAction("ReceiveTempData"); 8}
結果:
本應被延長的 TempData["error"] 沒有被延長。
Session
session 是從Web.SessionState 繼承的鍵值對物件。在控制器之間傳遞資料,可用於跨請求狀態下控制器和檢視之間的資料傳遞。一般Session用於保留對特定使用者的資料,但最好不要在其中放置敏感資料。生存期一直持續到 timeout 引數所限制的事件、呼叫Clear、RemoveAll、Abandon 方法或者關閉瀏覽器來明確地銷燬它。最好減少 Session 的使用因為它在伺服器叢集環境下不可靠,並且在使用時將一直佔用伺服器資源。
在使用 Session 時需要在 Startup.cs 中對其進行配置( ofollow,noindex">配置詳情 )。
使用方法:
在此我們將 Session 的過期時間設定為 5 秒,該過期時間在每次操作之後進行計算。
在 ConfigureServices 方法中:
1services.AddDistributedMemoryCache(); 2 3services.AddSession(options => 4{ 5 options.IdleTimeout = TimeSpan.FromSeconds(5); 6 options.Cookie.HttpOnly = true; 7 });
在 Configure 方法中:
1app.UseSession(); 2app.UseMvc();
中介軟體的順序很重要。 在 UseMvc
之後呼叫 UseSession
時會發生 InvalidOperationException
異常。
在預設的 ASP.NET Core 實現中,在 Session 中提供了位元組流、int 以及 string 資料的獲取和設定方法,分別為:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
為了方便,分別實現一個 Set 和 Get 的 ISession 泛型擴充套件方法:
1public static void Set<T>(this ISession session, string key, T value) 2{ 3session.SetString(key, JsonConvert.SerializeObject(value)); 4} 5 6public static T Get<T>(this ISession session, string key) 7{ 8var value = session.GetString(key); 9 10return value == null ? default(T) : 11JsonConvert.DeserializeObject<T>(value); 12}
控制器中:
1//用來獲取和設定值的鍵 2public const string SessionKeyStudent = "_Student"; 3 4public string AddSession() 5{ 6if (HttpContext.Session.Get<Student>(SessionKeyStudent)==default(Student)) 7{ 8Student student=new Student() 9{ 10Birth = new DateTime(1996,1,1), 11ID = 2, 12Name = "Ruri" 13}; 14HttpContext.Session.Set<Student>(SessionKeyStudent,student); 15} 16return "Session has been set"; 17} 18 19public IActionResult SessionTest() 20{ 21Student student = HttpContext.Session.Get<Student>(SessionKeyStudent) ?? new Student(); 22return View(student); 23}
SessionTest 檢視:
1 @model Student 2 @{ 3ViewData["Title"] = "SessionTest"; 4 } 5 6 <h2>SessionTest2</h2> 7 8 <div>ID: @Model.ID</div> 9 <div>Name: @Model.Name</div> 10 <div>Birth: @Model.Birth</div>
結果:
直接訪問 sessiontest :
設定 Session 然後再訪問 SessionTest2 :
未操作 5s 之後重新整理頁面,Session 被移除:
示例程式碼: https://github.com/NanaseRuri/Differences
參考網頁:https://www.mytecbits.com/microsoft/dot-net/viewmodel-viewdata-viewbag-tempdata-mvc
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/app-state?view=aspnetcore-2.1