在看TempData的說明時,有人說用一次就刪除,有人說一個Request就結束,在道聽途說下,有一次我的Code就出了Bug,一直死在TempData,最後看Source Code才發現,我對TempData的認知出了錯誤。
原理
在ASP.NET MVC中資料傳遞主要有ViewData與TempData,ViewData主要是Controller傳遞Data給View,存留期只有一個Action,要跨Action要使用TempData,而TempData依TempDataProvider的不同,會有不同的存留期,預設的TempDataProvider是SessionStateTempDataProvider,你沒有看錯,預設是用Session來存放TempData,Session不是使用者存放資料,而且存留時間預設在20分鐘的嗎?
所以SessionStateTempDataProvider有做一些手段,Controller起來時,從Session載入TempData,然後刪除Session,所以在Action時是不會看到TempData的Session,在讀取TempData時,會記錄用了那些Key,在Controller結束時,會把沒有過的TempData在存回Session中,所以一直沒有讀取的TempData是會存在到Session消失的。
Note:
ViewData的存留期測試
123456789101112HomeController.cs 片段
public
ActionResult Index()
{
this
.ViewData[
"Data"
] =
"Index"
;
return
View();
}
public
ActionResult List()
{
//什麼Data都沒有輸出
return
View();
}
12345678910111213141516171819Index.aspx 片段
<
div
>
Partial:
<%
//ViewData是使用Index,不會執行List的Action
Html.RenderPartial("List");
%>
</
div
>
<
div
>
Action:
<%
//ViewData是使用List,會執行List的Action
Html.RenderAction("List");
%>
</
div
>
List.ascx 片段
<%:this.ViewData["Data"] %>
結果
![]()
執行Partial或RanderPartial是在同一個Action中直接呼叫View,共用同一個ViewData。
執行Action或RanderAction會呼叫另一個Action,那一個Action再呼叫View,使用不用的ViewData。
如果要在不同的Action中傳遞資料,要使用TempData。
錯誤重現
下列這段Code,猜猜有什麼Bug。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public ActionResult Index() { this .TempData[ "UseDefault" ] = "true" ; return View(); } public ActionResult List() { //在Index的View,會使用RanderAction呼叫List,但那一個區塊是會用Ajax重載 if ( this .TempData.ContainsKey( "UseDefault" )) { //從Index的View,使用RanderAction呼叫,使用預設值 .......... } else { //從Ajax呼叫 ........... } return View(); } |
答案是
呼叫this.TempData.ContainsKey("UseDefult")一直都是True,因為ContainsKey不是使用,所以TempData["UseDefult"]會一直保留在Session,直到Session消失前都是true,所以從Ajax呼叫一直都是使用預設值。
原始碼分析
載入與儲存時機
1 2 3 4 5 6 7 8 9 10 11 12 | System.Web.Mvc.Controller.cs 片段 protected override void ExecuteCore() { //載入TempData PossiblyLoadTempData(); try { //呼叫Action ........... } finally { //儲存TempData PossiblySaveTempData(); } |
TempData的一些操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | System.Web.Mvc.TempDataDictionary.cs 片段 //_data 是放Keys + Values //_initialKeys 是放Keys,使用時移除Key //_retainedKeys 是放有呼叫,Keep的Keys public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { //載入放在Provider的資料 IDictionary< string , object > providerDictionary = tempDataProvider.LoadTempData(controllerContext); _data = (providerDictionary != null ) ? new Dictionary< string , object >(providerDictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary< string , object >(StringComparer.OrdinalIgnoreCase); _initialKeys = new HashSet< string >(_data.Keys, StringComparer.OrdinalIgnoreCase); _retainedKeys.Clear(); } public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { //keysToKeep = _initialKeys + _retainedKeys string [] keysToKeep = _initialKeys.Union(_retainedKeys, StringComparer.OrdinalIgnoreCase).ToArray(); //keysToRemove = _data - keysToKeep string [] keysToRemove = _data.Keys.Except(keysToKeep, StringComparer.OrdinalIgnoreCase).ToArray(); //刪除使用過且不保留的Keys foreach ( string key in keysToRemove) { _data.Remove(key); } //將沒有使用的TempData存起來 tempDataProvider.SaveTempData(controllerContext, _data); } public object this [ string key] { get { object value; if (TryGetValue(key, out value)) { //讀取時刪除Key,在Save時用來比較 _initialKeys.Remove(key); return value; } return null ; } set { _data[key] = value; _initialKeys.Add(key); } } public void Keep( string key) { //保留Key _retainedKeys.Add(key); } |
Note:
我曾經想過寫一個Provider,資料是存放在HttpContext.Items,因為我習慣Temp的資料,在一個Request結束後就消失,不過專案成員們都覺得太多此一舉了,而作罷。
12345678910111213//自訂的TempDataProvider,沒辦法用設定改變(至少我沒找到),只能用繼承來覆寫CreateTempDataProvider作到統一使用
public
class
MyControllerBase : Controller
{
protected
override
ITempDataProvider CreateTempDataProvider()
{
return
new
HttpContextItemsTempDataProvider();
}
}
//使用
public
class
HomeControllerBase : MyControllerBase
{
}
參考資料
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
2007-10-02 檔案 "C:\Program Files\Microsoft SQL Server\MSSQL.2\MSSQL\DATA\master.mdf" 是壓縮檔,但不在唯讀資料庫或檔案群組中。必須解壓縮該檔案 的解決方法