ASP.NET網頁運行生命週期中,一閃而逝的資料儲存空間(HttpContext.Items)

如果你在ASP.NET的環境中需要儲存一種資料,只有在每一次的應用程式要求起始發生,到結束的生命週期就消失了,那麼說真的這樣的需求還真的有點難辦到。

舉例來說,Application或是某類別的statice屬性,這些都是全站共享的。Session除非特別設計,否則一般來說還是會存活在使用者整個活動的周期內。姑且不論機敏性,Cookie、ViewState、InputTypeHidden這些把浪費效能打到前端資料儲存方式也不適合拿來實作在應用程式生命週期間的暫時儲存(亦很難達到全域存取的特性)。於是開始把腦筋動到MasterPage或Page物件,在這裡面AddLiteralContorl並對它賦值,並且把可視屬性關掉,的確在最終Render輸出時期會自動地被隱藏(不會輸出到前端),但最大的缺點是,你並沒有辦法自由的在生命週期區間內的各UserControl、Global.asax...等自帶的事件中,自由去存取這些你塞進去的Control屬性,否則你會乖乖地收到這種錯誤訊息:

控制項集合無法在 DataBind、Init、Load、PreRender 或 Unload 階段期間修改。
The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.

Context.Items是你的救贖

Context.Items具備下列特性:

  1. 只有使用者可以存取,而非全站共用。
  2. 非交談區間都可以使用,而是只有Request當下的生命週期區間才存在,到頁面Render後就消失了。
  3. 在生命週期極度早期就可以開始存取,甚至連Session都還沒成形時就可以存取。(例如:global.asax的Application_BeginRequest事件)
  4. 無論是aspx、ashx、asax甚至是自己的元件中的各事件,都可以自由地存取,不會有Lock的問題。
  5. 沒有Application、Session的Lock問題。

Context.Items的使用方式

Items是System.Web.HttpContext類別下的一個屬性,它採用的資料儲存方式是System.Collections.IDictionary,也就是常見的Key/Value的存取,使用起來的方法跟大家熟悉的Application、Session或ViewState根本都一樣。例如下列的用法:

//.ASPX
Context.Items["Key"] = value;
string cTemp = (string)Context.Items["Key"];

//.CS(dll)
System.Web.HttpContext oContext = System.Web.HttpContext.Current;
oContext.Items["Key"] = value;

如果你要檢查某個Key是否存在於Items內,建議可以用下面的寫法:

if (!Context.Items.Contains(cKey) || Context.Items[cKey] == null)
{ //如果Key不存在或是Key存在但沒有內容,那就預設寫入OOO
  Context.Items[cKey] = OOO;
}

最後還是要叮嚀一下,Context.Items在頁面生成階段後會被自動清空,並等候下一次Request的來臨。但有一個特別的情況下可以把值傳向別的頁面,那就是鮮少有人使用的Server.Transfer啦!

※ 特別注意,HttpContext.Items並不可以在ASP.NET WebForm與ASP.NET MVC之間共享資料,MVC有自己實作一個包皮類別HttpContextWrapper,看起來很像、用起來也很像,但這兩者並不相通。

HttpContext.Items Context.Items System.Collections.IDictionary Key/Value RequestLifeCycle