工作階段狀態

ASP.NET 為 Web 應用程式所需的跨要求狀態資訊 (購物車、資料捲動等等) 基礎結構提供內建工作階段狀態功能,允許您採取下列動作:

  • 將來自單一瀏覽器用戶端的要求自動識別並分類成伺服器上的邏輯應用程式工作階段。

  • 在伺服器上儲存工作階段範圍的資料以供跨多個瀏覽器要求之用。

  • 引發可以在應用程式程式碼中處理的適當工作階段存留期管理事件 (Session_OnStartSession_OnEnd 等等)。

    **注意   **僅在同處理序 (In-Process) 工作階段狀態模式中支援 Session_OnEnd 事件。如果使用狀態伺服器 (State Server) 或 SQL Server 模式,就不會引發此事件。

  • 自動釋放工作階段資料,只要瀏覽器不會在指定逾時期間內再度造訪應用程式。

這個主題提供工作階段狀態的概觀、描述如何辨識和追蹤作用中 ASP.NET 工作階段、解釋工作階段狀態存放區和一般結構,並包括高階的程式碼範例。

工作階段狀態概觀

HTTP 是沒有狀態 (Stateless) 的通訊協定,意即它不會自動指示要求的順序是否全都來自相同用戶端,甚或單一瀏覽器執行個體是否仍在主動檢視網頁或網站。因此,若不靠額外的基礎結構幫助來建置需要保持一些跨要求狀態資訊 (購物車、資料捲動,等等) 的 Web 應用程式,可能是極具挑戰性的。

ASP.NET 為工作階段提供下列支援:

  • 易於使用、為 ASP 開發人員所熟悉,並與其他 .NET Framework API 一致的工作階段狀態設施。
  • 可靠的工作階段狀態設施,可安然通過 Internet Iformation Services (IIS) 重新啟動和工作處理序重新啟動,而不會遺失工作階段資料。
  • 可擴充的工作階段狀態設施,可用於 Web Farm (多重電腦) 和 Web Garden (多重處理) 案例,並允許系統管理員配置更多處理器給 Web 應用程式來改善其延展性。
  • 可使用不支援 HTTP Cookie 的瀏覽器之工作階段狀態設施。
  • 就核心工作階段狀態案例來說,輸送量等於 ASP (或更多) 的輸送量 (50/50 讀取/寫入,當項目放入購物車、修改最後造訪的網頁、驗證信用卡詳細資料等等)。

然而,工作階段狀態在跨越 Web 應用程式界限時不會持續。如果 Web 應用程式在執行時切換到另一個應用程式,新的應用程式便無法使用該工作階段資訊。

辨識工作階段

每一個作用中的 ASP.NET 工作階段都使用 120 位元 SessionID 字串 (只含有 URL 中所允許的 ASCII 字元) 來辨識和追蹤。SessionID 值使用保證唯一性 (以致工作階段不會相衝突) 和隨機性 (以致惡意的使用者無法使用新 SessionID 來計算現有工作階段的 SessionID) 的演算法來產生。

SessionID 字串透過 HTTP Cookie 或者將 SessionID 字串內嵌而修改的 URL,跨用戶端-伺服器要求來通訊,這取決於您如何設定應用程式設定值。

工作階段狀態存放區

ASP.NET 提供簡單和易於使用的工作階段狀態模型,您可用來跨多個 Web 要求以儲存任意資料和物件。它使用物件參考 (存留於 IIS 處理內) 之字典架構的記憶體中快取來達成這點。使用同處理序工作階段狀態模式時,請考慮下列限制:

  • 使用同處理序工作階段狀態模式時,如果 aspnet_wp.exe 或應用程式定義域重新啟動,會遺失工作階段狀態資料。這種重新啟動通常發生於下列情況:
    • 在應用程式 Web.config 檔案的 <processModel> 項目中設定屬性,Web.config 檔案在符合如 memoryLimit 條件時,會啟動新的處理序。
    • 修改 Global.asax 或 Web.config 檔案。
    • 對 Web 應用程式的 \Bin 目錄進行變更。
    • 防毒軟體掃描和修改 Global.asax 檔案、Web.config 檔案或 Web 應用程式 \Bin 目錄中的檔案。
  • 如果在應用程式 Web.config 檔案的 <processModel> 項目中啟用 Web Garden 模式,請勿使用同處理序工作階段狀態模式。否則可能會發生隨機的資料遺失。

.NET 狀態伺服器在跨處理序模式中並不保留實體物件,而只是將工作階段狀態存放於記憶體中來代替。在這個模式中,背景工作處理序直接向狀態伺服器通話。在 SQL 模式中,工作階段狀態儲存於 SQL 伺服器資料庫,並且背景工作處理序直接向 SQL 通話。ASP.NET 工作者處理序即可於每個 Web 要求結尾,在用戶端的 Session 集合中序列化並儲存 (使用 .NET 序列化服務) 所有物件,來利用這項簡單的儲存服務。當用戶端再度造訪伺服器時,相關 ASP.NET 背景工作處理序會從狀態伺服器擷取這些物件當做二進位資料流、將它們還原序列化成實體執行個體,並將它們放回到要公開給要求處理常式的新 Session 集合物件中。

在 SQL 模式中,工作階段狀態也可以設定為在容錯移轉叢集中工作。容錯移轉叢集是兩個以上的相同多餘 Web 伺服器,將其工作階段資料存放在 SQL Server 資料庫的個別電腦上。如需如何設定這項組態的詳細資訊,請參閱設定 SQL Server 模式。

透過從應用程式對工作階段資料儲存體的使用中將其乾淨俐落地分別出來,ASP.NET 支援數個 ASP 先前版本尚未提供的強大案例:

  • 自應用程式毀損中復原,因為供工作階段狀態使用的記憶體不在 ASP.NET 背景工作處理序內。

    因為所有狀態都與個別背景工作處理序分開存放,如果處理序因存取違規而毀損,或在死結或記憶體遺漏 (Memory Leak) 事件中被 IIS 管理服務強制重新啟動的話,它是不會遺失的。

  • 跨多個背景工作處理序分割應用程式。

    因為所有狀態都與背景工作處理序分開存放,您可以跨多個處理序來乾淨俐落地分割應用程式。這樣的分割可以在多重處理電腦上大幅改善應用程式的可用性和延展性。此外,因為它將每一個背景工作處理序與單一電腦產生關聯,ASP.NET 即能夠排除跨處理器的鎖定爭用,這是 ASP 先前版本中主要的延展性瓶頸之一。

  • 跨多個 Web Farm 電腦分割應用程式。

    因為所有狀態都與背景工作處理序分開存放,您可以跨多個執行於多個電腦的背景工作處理序來乾淨俐落地分割應用程式。執行於不同電腦的背景工作處理序和狀態服務之間通訊狀態的模型幾乎與執行於相同電腦的處理序和伺服器一樣。在任一情形中,每一個 Web Farm 只可以有一個狀態伺服器。

工作階段狀態結構

ASP.NET 架構應用程式使用事件架構執行組織,啟用多個.NET Framework 類別模組以參與單一 Web 要求的處理。

SessionState 模組

.NET Framework 透過 SessionStateModule 類別 (衍生自 IHttpModule) 來實作工作階段狀態,這類別會參與 .NET 架構應用程式所接收的各個要求的執行。SessionStateModule 負責產生或者取得唯一 SessionID 字串,並負責儲存和擷取來自外部狀態提供者 (Provider) 的狀態資料。

工作階段狀態集合

SessionState 類別公開兩個狀態集合:ContentsStaticObjectsContents 集合公開所有已經透過程式碼直接加入工作階段狀態集合的變數項目。例如:

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

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

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

StaticObjects 集合公開所有已經透過 Global.asax 檔案內範圍為 "Session" 的 <object runat="server"> 標記加入工作階段狀態集合的變數項目。例如:

' Global.asax definition.
<OBJECT RUNAT="SERVER" SCOPE="SESSION" ID="MyInfo" PROGID="Scripting.Dictionary">
</OBJECT>

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

**注意   **ASP.NET 網頁編譯器自動在網頁編譯時間將成員參考插入所有儲存於 StaticObjects 集合內的物件。

網頁開發人員可以在網頁要求時間直接存取 Session 物件,而不必經過 StaticObjects 集合,如下列範例所示:

<html>
   </body>
      Number of entries: <%= MyInfo.Count %>
   <body>
</html>

工作階段狀態組態和啟動

ASP.NET.中有三種工作階段狀態模式。您可以在同處理序 (In-Process)、狀態伺服器和 SQL 伺服器之間選擇。無論選擇了哪一種模式,基本的組態過程都是相同的。

ASP.NET 以兩個階段來設定工作階段狀態。首先,工作階段狀態模組要插入 HTTP 要求中。根據預設,這是在全電腦 Machine.config 檔案中組態階層架構的根 (Root) 處完成的。

下列範例顯示 Machine.config 檔案中的範例項目。為了讓組態檔正確運作,您必須提供 System.Web.SessionState.SessionStateModule 組件適當版本的完整組件名稱。此版本通常是與您應用程式使用的 .NET Framework 版本關聯的版本。如需如何取得完整組件名稱的詳細資訊,請參閱組件名稱

<httpmodules>
   ...
    <!-- You must supply a valid fully qualified assembly name here. -->
    <!-- For this example to work correctly, the version number for -->
    <!-- the referenced assemby must match the version installed on -->
    <!-- your computer by the .NET Framework. -->
    <add name="sessionState" type="System.Web.SessionState.SessionStateModule, Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" />
   ...
</httpmodules>

然後,依據您要使用的工作階段狀態模式,在 <sessionState> 組態項目中設定適當的工作階段狀態服務屬性。

設定同處理序模式

同處理序為預設的工作階段狀態模式。若要使用同處理序模式,請將 <sessionState> 項目的 mode 屬性設定為 Inproc

下面顯示同處理序模式的範例組態設定。

<configuration>
    <system.web>
        <sessionState mode="Inproc"
                      cookieless="false"
                      timeout="20"/>
        </sessionState>
    </system.web>
</configuration>

設定狀態伺服器模式

若要使用狀態伺服器,您必須首先確定 ASP.NET 狀態服務正在作為工作階段存放區的遠端伺服器上執行。這項服務已與 ASP.NET 和 Visual Studio .NET 一起安裝在以下位置:

systemroot\Microsoft.NET\Framework\versionNumber\aspnet_state.exe

接著,在應用程式的 Web.config 檔中將 <sessionState> 項目的 mode 屬性設定為 StateServer。最後,將 connectionString 屬性設定為 **tcpip=serverName:**portNumber。

下列顯示狀態伺服器模式的範例組態設定。

<configuration>
    <system.web>
        <sessionState mode="StateServer"
                      stateConnectionString="tcpip=dataserver:42424"
                      cookieless="false"
                      timeout="20"/>
        </sessionState>
    </system.web>
</configuration>

設定 SQL Server 模式

若要使用 SQL Server,在要用來存放工作階段狀態且安裝有 SQL Server 的電腦上,先執行 InstallSqlState.sql 或 InstallPersistSqlState.sql。兩個指令碼都會建立名為 ASPState 的資料庫,其中包括數個預存程序。指令碼之間的差異在於放置 ASPStateTempApplications 和 ASPStateTempSessions 資料表的位置。InstallSqlState.sql 指令碼會將這些資料表加入 TempDB 資料庫,如果重新啟動電腦,資料庫就會遺失工作階段資料。另一方面,InstallPersistSqlState.sql 指令碼會將這些資料表加入 ASPState 資料庫,這樣在重新啟動電腦時資料庫便可以保留工作階段資料。

依預設,這些指令碼檔案都安裝在下列位置:

systemroot\Microsoft.NET\Framework\versionNumber

接著,在應用程式的 Web.config 檔中將 <sessionState> 項目的 mode 屬性設定為 SQLServer。最後,將 sqlConnectionString 屬性設定為 Integrated Security=SSPI;data source=serverName;

下面顯示 SQL Server 模式的範例組態設定。

<configuration>
    <system.web>
        <sessionState mode="SQLServer"
                      sqlConnectionString=" Integrated Security=SSPI;data source=dataserver;"
                      cookieless="false"
                      timeout="20"/>
        </sessionState>
    </system.web>
</configuration>

在 SQL Server 模式中,工作階段狀態也可以設定為在容錯移轉叢集中工作。容錯移轉叢集是兩個以上的相同多餘 Web 伺服器,將其工作階段資料存放在 SQL Server 資料庫的個別電腦上。如果 Web 伺服器失敗,叢集中的其他伺服器可以將之取代並服務要求,而不會遺失工作階段資料。若要設定容錯移轉叢集,請在 Web 伺服器的 Web.config 檔中將 <machinekey> 項目設定為相同的值。然後將 Web 伺服器的 SQL 連接字串設為指向存放工作階段資料電腦上的 SQL Server 資料庫。

高階程式碼範例

下列範例示範如何以唯讀方式存取現有工作階段狀態資料,動態產生含有使用者資訊和個人證券投資組合資訊的網頁。

<%@ Language=VB EnableSessionState=true %>
<html>
   <head>
      <script runat="server">
         Sub Page_Load(ByVal Sender as Object, ByVal E as EventArgs)
              ' Obtain data table of user's personal stock data.
              Dim MyStocks as DataTable
              Dim Stock as DataRow
              MyStocks = _
                 CType(Session("PersonalStockData"), DataTable)
              ' Update HTML output with session values.
              Name.InnerText = Session("FirstName").ToString()
              SpouseVal.InnerText = Session("SpouseName").ToString()
              For Each Stock In MyStocks.Rows
                 StockList.AddItem(Stock("Symbol") & ": " & Stock("Name"))
              Next
         End Sub
      </script>
   </head>
   <body>
      Hi <span id="Name" runat=server/>, your spouse is: <span id="SpouseVal" runat="server"/>.
      Here are the stocks you and your spouse currently own:
      <acme:listbox id="StockList" runat="server">
         <! — List box is dynamically populated from code. -->
      </acme:listbox>
   </body>
</html>
[C#]
<%@ Language=C# EnableSessionState=true %>
<html>
   <head>
      <script runat=server>
         void Page_Load(Object Sender, EventArgs E) {
              // Obtain data table of user's personal stock data.
              DataTable MyStocks =
                 (DataTable)Session["PersonalStockData"];
              // Update HTML output with session values.
              Name.InnerText = Session["FirstName"].ToString();
              SpouseVal.InnerText = Session["SpouseName"].ToString();
              foreach (DataRow Stock in MyStocks.Rows) {
                 StockList.AddItem(Stock["Symbol"] + ": "
                                 + Stock["Name"]);
              }
         }
      </script>
   </head>
   <body>
      Hi <span id="Name" runat="server"/>, your spouse is: <span id="SpouseVal" runat="server"/>.
      Here are the stocks you and your spouse currently own:
      <acme:listbox id="StockList" runat="server">
         <! — List box is dynamically populated from code. -->
      </acme:listbox>
   </body>
</html>

請參閱

ASP.NET 狀態管理