Session 是 ASP.NET 狀態管理的基石,但許多開發者僅停留在「存取值」層面。本文從值/引用類型行為、生命周期、Web Service 調用、過期機制、序列化要求、內存釋放、SessionID 行為七個維度,深度剖析 Session 內部機制,助你避開生產環境坑點。
Session["count"] = 1;
int i = (int)Session["count"] + 1; // i = 2
int j = (int)Session["count"]; // j = 1 (原值不變)
本質:值類型在 Session 中經過裝箱存儲,讀取時拆箱產生新副本。修改局部變量不影響 Session 內原值。
DataSet ds1 = (DataSet)Session["data"];
DataSet ds2 = (DataSet)Session["data"];
ds1.Tables[0].Rows[0][0] = "999";
// ds2 同樣變為 "999",因指向同一堆對象
陷阱:多頁面共享同一 DataSet 時,一處修改波及全局。需用 ds.Copy() 創建副本隔離。
Session_StartSession_Start每次調用 Web Service 默認創建新 Session。解決方案:
// 客戶端代理類設置共享 CookieContainer
CookieContainer sharedCookie = new CookieContainer();
Service1 proxy1 = new Service1 { CookieContainer = sharedCookie };
Service1 proxy2 = new Service1 { CookieContainer = sharedCookie };
// 兩個代理實例共享同一 Session
生產環境建議用單例模式管理 CookieContainer。
DataView、未標記 [Serializable] 的自定義類無法在 Out-of-Process 模式下存儲。檢查方法:
// 編譯時檢查
[Serializable]
public class MyClass { }
// 運行時檢查
bool canSerialize = typeof(MyClass).IsSerializable;
調用 Session.Clear() 或 Session.Abandon() 後,aspnet_wp.exe/w3wp.exe 內存不一定立即下降,因 .NET GC 非確定性回收。大對象(如 DataSet)建議顯式置 null 並調用 GC.Collect()(慎用)。
兩個必要條件(合為充分條件):
Session_Start)// Global.asax 陷阱:定義空 Session_Start 會導致 SessionID 固定,掩蓋真實問題
void Session_Start(object sender, EventArgs e) { } // 若無實際邏輯,請刪除!
| 現象 | 根因 | 對策 |
|---|---|---|
| SessionID 每請求變化 | 未寫入 Session 數據 | Page_Load 中寫入任意值:Session["init"]=1 |
| Session_End 不執行 | 非 InProc 模式 / 未滿足兩條件 | 確認模式、確保有完整請求且寫入數據 |
| Web Service 每次新 Session | 無共享 CookieContainer | 單例管理 CookieContainer |
| SQLServer 模式報錯 | 存儲非序列化對象 | 標記 [Serializable] 或轉存可序列化 DTO |
Session 看似簡單,實則涉及 HTTP 無狀態協議、進程模型、序列化、GC 等核心機制。理解這七大認知,能從根源上解決「Session 丟失、內存洩漏、Web Service 狀態不同步」等棘手問題,編寫更健壯的 Web 應用。