セッション状態
ASP.NET には、次の操作を実行できるようにする組み込みのセッション状態機能と共に、Web アプリケーションに必要な要求間の状態情報 (買い物カゴ、データ スクロールなど) のインフラストラクチャが用意されています。
単一のブラウザ クライアントから着信した要求を自動的に識別し、サーバーの論理アプリケーション セッションに分類する操作
複数のブラウザ要求で使用するために、セッション スコープのデータをサーバーに格納する操作
アプリケーション コードで処理できるセッション有効期間管理イベンそト (Session_OnStart、Session_OnEnd など) を発生させる操作
メモ Session_OnEnd イベントがサポートされるのは、インプロセス セッション状態モードだけです。State Server モードや SQL Server モードを使用する場合、このイベントは発生しません。
ブラウザが指定されたタイムアウト期間内にアプリケーションに再アクセスしなかった場合に、セッション データを自動的に解放する操作
ここでは、セッション状態の概要、アクティブな ASP.NET セッションを識別および追跡する方法、およびセッション状態のストアと一般的な構造について説明し、最後に高水準のコード例を示します。
セッション状態の概要
HTTP は状態を持たないプロトコルであるため、連続する要求がすべて同じクライアントからの要求かどうか、単一のブラウザ インスタンスがアクティブにページまたはサイトを表示しているかどうかを自動的に示しません。その結果、複数の要求間で状態情報 (買い物カゴ、データのスクロールなど) を維持する必要がある Web アプリケーションを構築する場合は、別のインストラクチャの補助を利用しないと、かなり困難になる場合があります。
ASP.NET には、セッションに対する次のサポートが用意されています。
- ASP 開発者にとって使い慣れた、他の .NET Framework API と互換性のある、使いやすいセッション状態機能
- インターネット インフォメーション サービス (IIS: Internet Information Services) やワーカー プロセスの再起動時にもセッション データを失わずに存続できる信頼性の高いセッション状態機能
- Web ファーム (マルチコンピュータ) と Web ガーデン (マルチプロセス) の両方で使用でき、管理者がより多くのプロセッサを Web アプリケーションに割り当ててアプリケーションのスケーラビリティを向上させることができるようにするスケーラブルなセッション状態機能
- HTTP cookies をサポートしないブラウザに対応したセッション状態機能
- セッション状態のシナリオの中心的な部分 (1 つの商品を買い物カゴに入れるとき、最後に訪問したページを修正するとき、クレジット カードの詳細を確認するときなどの一対一の読み取り/書き込み) に対する、ASP と同等またはそれ以上のスループット
ただし、セッション状態は Web アプリケーション境界を越えて持続しません。Web アプリケーションの実行中に別のアプリケーションに切り替わった場合、セッション情報は新しいアプリケーションでは利用できません。
セッションの識別
アクティブな各 ASP.NET セッションは、URL で使用できる ASCII 文字だけを含む 120 ビットの SessionID 文字列を使用して、識別および追跡されます。SessionID の値は、セッションが衝突しないように一意性を確保し、新しい SessionID が既存のセッションの SessionID を計算するために悪用されないようにランダム性を確保するアルゴリズムを使用して生成されます。
SessionID 文字列はクライアントとサーバー間の要求にわたって伝達されますが、その手段はアプリケーション設定の構成方法によって異なり、HTTP cookie で伝達されるか、URL に埋め込まれた形で伝達されます。
セッション状態ストア
ASP.NET には、任意のデータやオブジェクトを複数の Web 要求にまたがって格納するために使用できる、単純で使いやすいセッション状態モデルが用意されています。このモデルは、IIS プロセス内に存在する、ディクショナリ ベースのオブジェクト参照のメモリ内キャッシュを使用して、複数の Web 要求にまたがった格納を実現します。インプロセス セッション状態モードを使用している場合は、次の制限事項に注意してください。
- インプロセス セッション状態モードを使用している場合、aspnet_wp.exe またはアプリケーション ドメインを再起動するとセッション状態データは失われます。この再起動は、一般的に次のような場合に発生します。
- アプリケーションの Web.config ファイルの <processModel> 要素で、条件が満たされたときに新しいプロセスを起動する memoryLimit などの属性を設定している場合。
- Global.asax ファイルまたは Web.config ファイルを変更した場合。
- Web アプリケーションの \Bin ディレクトリに変更を加えた場合。
- ウィルス対策ソフトウェアが Global.asax ファイル、Web.config ファイル、または Web アプリケーションの \Bin ディレクトリ内のファイルをスキャンして変更した場合。
- アプリケーションの Web.config ファイルの <processModel> 要素で Web ガーデン モードを有効にしている場合は、インプロセス セッション状態モードを使用しないでください。このモードを使用すると、ランダムにデータが失われる可能性があります。
.NET State Server は、アウトプロセス モードでは実際のオブジェクトを維持する代わりに、単にセッション状態をメモリに格納します。このモードでは、ワーカー プロセスは直接 State Server と対話します。SQL モードでは、セッション状態は SQL Server データベースに格納され、ワーカー プロセスは直接 SQL と対話します。その後で、ASP.NET ワーカー プロセスは、各 Web 要求の終了時にクライアントの Session コレクション内にあるすべてのオブジェクトを (.NET シリアル化サービスを使用して) シリアル化し、保存することによって、この単純なストレージ サービスを利用できます。クライアントがサーバーに再アクセスすると、関連する ASP.NET ワーカー プロセスがこれらのオブジェクトを State Server からバイナリ ストリームとして取得し、ライブ インスタンスに逆シリアル化して、要求ハンドラに公開される新しい Session コレクション オブジェクトに戻します。
SQL モードでは、フェール オーバー クラスタで機能するようにセッション状態を構成できます。フェール オーバー クラスタは、複数の同一で冗長な Web サーバーで、セッション データを個々のコンピュータ上の SQL Server データベースに格納します。この構成の設定方法の詳細については、「SQL Server モードの構成」を参照してください。
ASP.NET は、アプリケーションが使用するストレージと、セッション データのストレージを明確に分離することによって、ASP の以前のバージョンでは利用できなかった強力なシナリオをサポートします。
セッション状態用のメモリが ASP.NET ワーカー プロセス外に確保されるため、アプリケーション クラッシュからの回復が可能
すべての状態が個別のワーカー プロセスとは別に格納されるため、アクセス違反が原因でプロセスがクラッシュした場合でも状態が失われず、デッドロックやメモリ リークが発生した場合も IIS Admin Service によって強制的に再起動されます。
複数のワーカー プロセス間でのアプリケーションのパーティション分割
すべての状態がワーカー プロセスとは別に格納されるため、複数のプロセス間でアプリケーションを明確に分割できます。このような分割によって、マルチプロセス コンピュータ上のアプリケーションの可用性とスケーラビリティの両方を大幅に向上させることができます。さらに、各ワーカー プロセスが単一のコンピュータに関連付けられるため、ASP.NET は、ASP の旧バージョンでスケーラビリティ上の主要なボトルネックの 1 つであったプロセス間のロック競合を除去できます。
複数の Web ファーム コンピュータ間でのアプリケーションのパーティション分割
すべての状態がワーカー プロセスとは別に格納されるため、複数のコンピュータ上で実行中の複数のワーカー プロセス間でアプリケーションをパーティション分割できます。異なるコンピュータ上で実行中のワーカー プロセスと状態サービスの間で状態を通信するモデルは、同じコンピュータ上で実行中のプロセスとサービスの間で状態を通信するモデルとほとんど同じです。どちらの場合も、Web ファームごとに State Server は 1 つだけです。
セッション状態の構造
ASP.NET ベースのアプリケーションは、イベント ベースの実行構造を使用して、複数の .NET Framework クラス モジュールが単一の Web 要求の処理に参加できるようにします。
SessionState モジュール
.NET Framework は、.NET ベースのアプリケーションが受け取った各要求の実行に参加する SessionStateModule クラス (IHttpModule から派生) を通じてセッション状態を実装します。SessionStateModule は、一意の SessionID 文字列を生成または取得したり、外部状態プロバイダからの状態データを格納および取得したりします。
セッション状態コレクション
SessionState クラスは、Contents および StaticObjects という 2 つの状態コレクションを公開します。Contents コレクションは、コードを通じて直接セッション状態コレクションに追加されたすべての変数項目を公開します。次に例を示します。
' 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 コレクションに追加できます。StaticObjects コレクションは、コードを通じて直接オブジェクトを追加しようとすると、NotSupportedException をスローします。
メモ ASP.NET ページ コンパイラは、ページ コンパイル時に、メンバ参照を、StaticObjects コレクション内に格納されたすべてのオブジェクトに自動的に挿入します。
ページ開発者は、次の例で示すとおり、ページ要求時に StaticObjects コレクションを参照せずに直接 Session オブジェクトにアクセスできます。
<html>
</body>
Number of entries: <%= MyInfo.Count %>
<body>
</html>
セッション状態の構成とスタートアップ
ASP.NET には 3 つのセッション状態モードがあり、インプロセス、State Server、および SQL Server のいずれかのモードを選択できます。選択したモードに関係なく、基本的な構成の手順は同じです。
ASP.NET は、セッション状態を 2 段階で構成します。まず、セッション状態モジュールが HTTP 要求に挿入されます。既定では、この挿入は、コンピュータ全体の Machine.config ファイル内の構成階層構造のルートで実行されます。
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>
State Server モードの構成
State Server を使用するには、まず、ASP.NET 状態サービスがセッション ストアに使用されるリモート サーバーで実行されていることを確認する必要があります。ASP.NET および Visual Studio .NET では、ASP.NET 状態サービスは次の場所にインストールされます。
systemroot\Microsoft.NET\Framework\versionNumber\aspnet_state.exe
次に、アプリケーションの Web.config ファイルで <sessionState> 要素の mode 属性を StateServer に設定します。最後に、connectionString 属性を **tcpip=serverName:**portNumber に設定します。
State Server モードを設定する構成の例を次に示します。
<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 という名前のデータベースが作成されます。これら 2 つのスクリプトの違いは、ASPStateTempApplications テーブルと ASPStateTempSessions テーブルが配置される場所です。InstallSqlState.sql スクリプトでは、これら 2 つのテーブルが TempDB データベースに追加され、コンピュータを再起動するとセッション データは失われます。一方、InstallPersistSqlState.sql スクリプトの場合は、これら 2 つのテーブルが ASPState データベースに追加されるので、コンピュータを再起動してもセッション データは保持されます。
既定では、これら 2 つのスクリプトは次の場所にインストールされています。
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 サーバーの 1 台に障害が発生した場合は、クラスタ内の別のサーバーが引き継いで、セッション データを失わずに要求を処理します。フェール オーバー クラスタを構成するには、各 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>