應用程式狀態

您可以使用 HttpApplicationState 類別 (最通常是透過 HttpContext 物件的 Application 屬性來存取),在您的應用程式全面共用資訊。這個類別公開物件的索引鍵值字典,您可以用來存放 .NET Framework 物件和來自多個用戶端的多個Web 要求相關的數值類值。

這個主題提供應用程式狀態的概觀;討論如何使用應用程式狀態;介紹應用程式狀態集合;並涵蓋應用程式狀態的同步處理。

應用程式狀態概觀

ASP.NET 應用程式是所有檔案、網頁、處理常式、模組和程式碼的總和,它們位於指定虛擬目錄和其子目錄,並且使用者可以透過那個虛擬目錄階層架構來要求。

例如,如果您要開發應用程式,替您公司的 Intranet 計算投資利益,您可以在 Web 伺服器上名為 \Invest 的虛擬目錄中發行它。這類應用程式的目錄結構可能看起來有點像這樣:

\Invest

   \bin

   \image

   \xml

HttpApplicationState 類別的執行個體 (Instance) 會在任何用戶端初次從特定 ASP.NET 應用程式的虛擬目錄命名空間 (Namespace) 內要求 URL 資源時建立。這對儲存在電腦上的各個 Web 應用程式都是一樣的情形。對每個應用程式執行個體的存取是透過名為 ApplicationHttpContext 屬性提供的。所有 HttpModules 和 HttpHandlers (例如 ASP.NET 網頁) 都可以存取內容的執行個體,因此在指定的 Web 要求期間可以存取 Application 屬性。

ASP.NET 提供下列應用程式狀態支援:

  • 易於使用的狀態設施,可與 ASP 的先前版本相容、使用所有 .NET 支援的語言,並與其他 .NET Framework API 一致。
  • 應用程式狀態字典,可供所有在應用程式內叫用 (Invoke) 的要求處理常式來使用。不像 Internet Information Services (IIS) 和 ASP 的先前版本 (只有網頁可以在其中存取應用程式狀態),所有 IHttpHandlerIHttpModule 執行個體都可以在字典內儲存並擷取全域變數。
  • 簡單和直覺的同步處理機制,讓開發人員能夠輕鬆地協調對儲存於應用程式狀態中變數的並行存取。
  • 應用程式狀態值,只可以從在原始應用程式內容裡執行的程式碼來存取。在系統上執行的其他應用程式則無法存取或修改數值。

存取應用程式狀態的最常用方式是透過 Page 物件的 Application 屬性。

使用應用程式狀態

應用程式狀態變數實際上是指定的 ASP.NET 應用程式的全域變數。就像用戶端應用程式開發人員一樣,ASP.NET 程式設計人員應該總是考慮到將任何東西儲存為全域變數的影響。

下列問題在這個內容中特別重要:

  • 儲存某些東西於應用程式狀態的記憶體影響。直到數值被移除或者取代之前,儲存於應用程式狀態的變數所佔用的記憶體不會釋出,不像個別 Web 網頁,其中所有資源在 Web 要求終結時就被移除。例如,在應用程式狀態中永久保留不常使用的 10 MB 資料錄集 (Recordset),並不是系統資源的最佳用法。對於這個極端範例,您若使用 ASP.NET 快取,即可找到較佳的方案。
  • 在多執行緒伺服器環境內儲存和存取全域變數的並行和同步隱含式。應用程式內的多個執行緒可以同時存取儲存於應用程式狀態中的值。您必須保持謹慎,確保在應用程式範圍的物件是無限制執行緒的情況時,會包含內建的同步支援。所有以 Common Language Runtime 為目標的自訂物件都是無限制執行緒。如果應用程式範圍的物件不是無限制執行緒,您必須確保外顯同步方法的程式碼是環繞它來撰寫,以避免死結 (Deadlock)、競爭情形和存取違規。
  • 在多執行緒伺服器環境內儲存和存取全域變數的延展性 (Scalability) 隱含式。無論何時進行寫入或更新檔案的嘗試,都應該使用鎖定。保護全域資源的鎖定本身就是全域的,並且執行於存取全域資源的多個執行緒的程式碼最後必然會在這些鎖定上爭奪。這會導致作業系統封鎖背景工作執行緒 (Worker Thread),直到鎖定可供使用為止。在高負荷伺服器環境中,這個封鎖可能在系統上造成嚴重的執行緒竄動。在多處理器系統上,它可能會導致處理器未充分利用 (因為處理器的所有執行緒在等待共用鎖定時,理論上可以被停止) 和整體延展性的顯著下降。
  • 儲存於應用程式狀態中的資訊之生命週期隱含式。.NET Framework 應用程式定義域或裝載 (Host) .NET 架構應用程式的處理序可以在應用程式執行期間的任何時刻被拆除並毀棄 (由於當機、程式碼更新、預定的處理序重新啟動等等)。因為儲存於應用程式狀態中的資料不是持久的,如果包含它的主應用程式 (Host) 被毀棄,它將會遺失。如果您想要狀態逃過這些失敗類型,您應該將它儲存在資料庫或其他持久性存放區中。
  • 應用程式狀態不能在 Web Farm (其中的應用程式被裝載於多個伺服器) 或 Web Garden (其中的應用程式被裝載於相同伺服器上的多個處理序) 之間共用。儲存於應用程式狀態 (上述兩個案例之一) 的變數只對應用程式執行所在的特定處理序為全域性的。各個應用程式處理序可能會有不同值。因此,您不可以依賴應用程式狀態來儲存唯一值或更新全域計數器,例如,在 Web Farm 和 Web Garden 案例中的情形。

儘管有這些問題,設計良好的應用程式層級變數在 Web 應用程式中還是可以非常強大的。您可以做一次 (或不經常) 載入和資訊計算,並接著使用應用程式狀態來快取它,以求在後來的 Web 要求中進行快速的記憶體中存取。

例如,股市網站可能要在白天每 5 分鐘從資料庫擷取大量金融股票資訊 (或許 40 MB 的資料),並接著在應用程式狀態 (所有後續的查閱要求都可以在其中存取它) 中快取它。結果將會是每個要求效能方面的大幅改善,因為輸入的要求不需要跨處理序 (Cross-Process)、跨電腦或資料庫往返。另一方面,大型暫時性 (Transient) 資料區塊資源的較佳用法可能就是使用快取區 (Cache)。

應用程式狀態集合

HttpApplicationState 類別公開兩個狀態集合:ContentsStaticObjects

Contents 集合公開所有已經直接透過程式碼加入應用程式狀態集合的變數項目。例如:

'  Visual Basic code from within a page, a handler, or Global.asax.
Application("Message") = "MyMsg"
Application("AppStartTime") = Now
[C#]
// C# code from within a page, a handler, or Global.asax.
Application["Message"] = " MyMsg";
Application["AppStartTime"] = DateTime.Now;

為了與 ASP 先前版本相容,這些變數也可以使用應用程式物件的 Contents 屬性來存取,如下列範例所示。

' Visual Basic code from within a page, a handler, or Global.asax.
Application.Contents("Message") = " MyMsg"
Application.Contents("AppStartTime") = Now
[C#]
// Visual Basic code from within a page, a handler, or Global.asax.
Application.Contents["Message"] = " MyMsg";
Application.Contents["AppStartTime"] = DateTime.Now;

StaticObjects 集合會公開所有變數項目,這些變數項目已經透過 Global.asax 檔案中範圍為應用程式的 <object runat="server"> 標記 (Tag) 加入應用程式狀態集合。例如:

' Global.asax definition.
<object runat="server" scope="application" ID="MyInfo" PROGID="MSWC.MYINFO">
</OBJECT>

物件無法從 ASP.NET 應用程式內的任何其他地方加入至 StaticObjects 集合。如果您嘗試透過程式碼直接將物件加入的話,集合會擲回 NotSupportedException

注意:.NET 網頁編譯器會在網頁編譯時,將成員參考自動插入至在 StaticObjects 集合內儲存的所有物件中,這樣一來開發人員即可在網頁要求時無需參考 Application 集合而存取這些應用程式物件。例如:

<html>
   </body>
      Application Level Title: <%= MyInfo.Title %>
   <body>
</html>

應用程式狀態同步處理

應用程式內的多個執行緒可以同時存取儲存於應用程式狀態中的值。因此,當您建立某個東西,需要存取應用程式狀態值時,您必須要一直確保應用程式狀態物件是無限制執行緒,並執行它自己的內部同步處理,要不然就執行手動的同步處理步驟來保護以防產生競爭情形、死結、和存取違規。

HttpApplicationState 類別提供 LockUnlock 兩個方法,在存取應用程式狀態變數的時候只允許一個執行緒存在。

Application 物件上呼叫 Lock 會導致 ASP.NET 封鎖程式碼 (執行於其他背景工作執行緒) 存取應用程式狀態中任何項目的嘗試。這些執行緒只有當稱為 Lock 的執行緒在 Application 物件上呼叫對應的 Unlock 方法時才會解除封鎖。

下列程式碼範例示範如何使用鎖定以防止競爭情形的產生。

' Visual Basic code from within a page, a handler, or Global.asax.
Application.Lock()
Application("SomeGlobalCounter") = _
   CType(Application("SomeGlobalCounter"), Integer) + 1
Application.UnLock()
[C#]
// C# code from within a page, a handler, or Global.asax.
Application.Lock();
Application["SomeGlobalCounter"] =
   (int)Application["SomeGlobalCounter"] + 1;
Application.UnLock();

如果您不明確呼叫 Unlock,.NET Framework 會在要求完成、要求逾時,或要求執行期間發生未處理錯誤而導致要求失敗時自動移除鎖定。這個自動解除鎖定會防止應用程式發生死結。

請參閱

ASP.NET 狀態管理 | ASP.NET 應用程式