ASP.NET提供了4個層面上的狀態管理工具:應用程式、會話、頁面和請求,每層都有專用的容器物件。
應用程式的狀態
下表總結了各狀態物件的主要功能:
儘管HttpApplicationState和HttpSessionState對象與ASP內部對象Application和Session的名稱不同,但在狀態方面,它們的功能完全一致。
HttpApplicationState對象可使應用程式中的所有請求處理常式獲得一種字典形式的儲存物件。所有HTTP處理常式和模組都能夠儲存和檢索應用程式字典中的值。應用程式狀態只在當前應用程式的上下文中可見,位於同一系統中的其他應用程式無法訪問或更改其中的值。
當用戶端首次請鹽濃度某個虛擬目錄中的資源時,HttpApplicationState類的執行個體會被建立。每個運行中的應用程式都持有自身的全書狀態物件。訪問應用程式狀態最常見的方法是通過Page對象的Application屬性。應用程式狀態不能在Web Farm和Web Garden中共用。
HttpApplicationState類的屬性
HttpApplicationState類是封閉的,繼承於NameObjectCollectionBase類。為方便使用,HttpApplicationState類被設計為鍵/值對集合,其中鍵為String類型,值為Object類型。在內部,基類會使用HashTable,初始容量為0,並按需要自動增加。
下表列出了HttpApplicationState類的屬性:
注意,靜態對象和實際的狀態值儲存在不同的集合中,該靜態集合的類型為HttpStaticObjectsCollection。
HttpApplicationState類的方法
HttpApplicationState類的方法大多是普通鍵/值集合相應方法的特殊化版本。如下表所示,其顯著的擴充涉及一種鎖定機制,旨在應對狀態值的連續訪問:
注意,GetEnumerator方法繼承於一個集合類,但這個類沒有實現鎖定機制。如果通過該方法對集合進行枚舉,傳回值只是通過調用NameObjectCollectionBase的get方法來擷取的。因此,這種枚舉過程不是安全執行緒的。枚舉集合內容更好的方式是,通過while語句和Get方法來訪問每一項。此外,我們還可以在執行枚舉前以手動方式對集合進行鎖定。
狀態同步
HttpApplicationState上的所有操作都需要某種同步措施,以便確保同一應用程式中啟動並執行多個線程能安全的訪問集合中的值,而不會造成死結和存取違規。寫入方法(如Set和Remove)和Item屬性的set訪問器會在執行操作前隱式地應用寫入鎖。Lock方法能夠確保只有當前線程能夠更改應用程式狀態。
我們不必用Lock/Unlock方法將單個Set、Clear或Remove封裝起來,這些方法已經是安全執行緒的了。但如果需要屏蔽多個不能並發寫入的指令,則需顯式地使用Lock方法。
//鎖定
Application.Lock();
int val = (int)Application["MyValue"];
if(val < 10)
Application["MyValue"] = val + 1;
Application.UnLock();
我們應該成對使用Lock和UnLock。然而,如果遺漏了UnLock,導致死結的可能性並不大。因為,.NET Framework會在請求結束、逾時或出現未處理異常時自動將鎖移除。因此,為處理該異常,可考慮使用finally塊來清除鎖。
應用程式狀態的權衡
若不使用HttpApplicationState對象,則可以在global.asax檔案中添加公用成員。與HttpApplicationState集合中的資料項目相比,全域成員有一定的優越性,因為全域成員是強型別的,且不需要HashTable來定位某個值。但另一方面,全域變數本身不具備同步功能,必須以手動方式進行保護。我們不得不使用Lock來保護對這些成員的訪問。
記憶體佔用
在儲存全域資料時,無論選擇何種方式來儲存應用程式的全域狀態,都應注意一些基本事項。
全域資料儲存會導致持久性的記憶體佔用,如果不通過代碼顯式地將其移除,那麼應用程式全域狀態中儲存的資料只有在應用程式關閉後才會被移除。因此,在需要全域共用資料時,有必要使用Cache對象。儲存在ASP.NET Cache中的資料會被自動清理,能夠確保在資料對虛擬記憶體待佔用率較高時自動將其清除。
資料的並發訪問
由於鎖定機制,儲存全域資料還有很多問題有待解決。對應用程式狀態的鎖定,很容易變成效能瓶頸。應用程式全域狀態儲存在記憶體中,而不會逾越電腦的邊界。在多電腦和多處理器的環境下,應用程式全域狀態僅局限在運行於某台電腦或某個CPU的背景工作處理序中。因此,它並不是名符其實的“全域”。此外,由於進程運行被終止(簡單地講是ASP.NET進程被回收),那些記憶體中的資料也會受到影響。如果要在Web Farm或Web Garden環境下使用應用程式狀態功能,最好將全域狀態儲存在資料庫表中,至少應將全域資料封裝在某個“智能”的代理對象中。通過該對象對資料的存在性進行檢查,若出於某種原因而導致資料丟失,該對象要重新填充。如下所示:
public object GetGlobalData(string entry)
{
object o = Application[entry];
//檢查資料是否存在
if(o == null)
{
//重新擷取資料
......
//返回資料
return Application[entry];
}
return o;
}