クラシック ASP と ASP.NET の間でセッション状態を共有する方法

 

Billy Yuen
Microsoft Corporation

2003 年 2 月

適用対象:
   Microsoft® ASP.NET

概要:Microsoft .NET Framework クラスと.NET Frameworkのシリアル化機能を使用して、従来の ASP と Microsoft ASP.NET の間でセッション状態を共有する方法について説明します。 セッション状態を共有すると、アプリケーションを並行して実行しながら、既存の ASP アプリケーションを段階的に ASP.NET アプリケーションに変換できます。 (12ページ印刷)

はじめに
概念
ASP.NET 実装
ASP 実装
デモ プログラム
既存の ASP アプリケーションに COM オブジェクトを組み込む
制限/改善
まとめ

はじめに

Microsoft® ASP.NET は、Web ベースのアプリケーションを開発するための最新の Microsoft テクノロジです。 これは、従来の ASP スクリプト テクノロジよりも多くの利点を提供します。たとえば、1) UI プレゼンテーションをビジネス ロジックから分離することで、より優れた開発構造を実現できます。2) そのコードは、従来の ASP のように解釈されるのではなく、完全にコンパイルされます。3) キャッシュサポートと組み合わせたコンパイル機能は、従来の ASP で記述された同等のサイトよりも、ASP.NET で記述されたサイトのパフォーマンスが大幅に向上することを意味します。

既存の ASP アプリケーションを ASP.NET に変換する利点はありますが、多くの既存の ASP アプリケーションはミッション クリティカルで複雑です。 変換プロセスはリソースを集中的に消費し、既存のアプリケーションに追加のリスクを引き起こす可能性があります。 これらの問題に対処する方法の 1 つは、ASP と ASP.NET を並べて実行し、アプリケーションの 1 つのセクションを一度に 1 つ ASP.NET に変換することです。 新旧のアプリケーションを並行して実行するには、クラシック ASP と ASP.NET の間でセッション状態を共有するメカニズムが必要です。 この記事では、いくつかのクラスと Microsoft® .NET Frameworkのシリアル化機能を使用してセッション状態を共有する方法について説明します。

概念

Cookie は、Web アプリケーションがユーザー セッションを識別するための最も一般的な方法であり、従来の ASP と ASP.NET の両方のセッション状態を識別するために使用できます。 セッション状態情報は ASP スクリプトのメモリに格納され、ASP.NET などの他のアプリケーションと共有することはできません。 セッション状態が Microsoft® SQL Server の共通形式で格納されている場合、セッション状態には、従来の ASP と ASP.NET の両方からアクセスできます。

この例では、mySession という名前の Cookie を使用してユーザー セッションを識別します。 ユーザーが Web アプリケーションに対して要求を行うと、セッションを識別するための一意の Cookie がユーザーに発行されます。 以降の要求では、ブラウザーは一意の Cookie をサーバーに送信してセッションを識別します。 要求された Web ページが読み込まれる前に、カスタム オブジェクトは、一意の Cookie を使用してSQL Serverからユーザー セッション データを再読み込みします。 セッション状態は、カスタム オブジェクトを介して Web ページでアクセスできます。 Web 要求が完了すると、要求が終了すると、セッション データはSQL Serverに永続化されます (図 1 を参照)。

Aa479313.converttoaspnet_fig1(en-us,MSDN.10).gifAa479313.converttoaspnet_fig1

図 1. サンプル データ フロー

ASP.NET 実装

ASP.NET では、すべての Web ページは System.Web.UI.Page クラスから派生します。 Page クラスは、セッション データの HttpSession オブジェクトのインスタンスを集計します。 この例では、SessionPage というカスタム Page クラスを System.Web.UI.Page から派生し、Page クラスと同じ機能をすべて提供します。 派生ページとの唯一の違いは、既定の HttpSession がカスタム セッション オブジェクトでオーバーライドされる点です。 (インスタンス変数に 新しい 修飾子を使用すると、C# では派生クラスで基底クラスのメンバーを非表示にすることができます)。

   public class SessionPage : System.Web.UI.Page
   {
      ...
      public new mySession Session = null;
      ...
   }

カスタム セッション クラスは、 HybridDictionary オブジェクトを使用してセッション状態をメモリに格納します。 (HybridDictionary では、任意の数のセッション要素を効率的に処理できます)。カスタム セッション クラスでは、従来の ASP との相互運用性のためにのみ、セッション データ型を文字列に制限します。 (既定の HttpSession では、任意の種類のデータをセッションに格納できます。これは、従来の ASP と相互運用されません)。

   [Serializable]
public class mySession 
   {
      private HybridDictionary dic = new HybridDictionary();

      public mySession()
      {
      }

      public string this [string name]
      {
         get
         {
            return (string)dic[name.ToLower()];
         }
         set
         {
            dic[name.ToLower()] = value;
         }
      }
   }

Page クラスは、カスタマイズのためにさまざまなイベントとメソッドを公開します。 特に、 OnInit メソッドは Page オブジェクトの初期化状態を設定するために使用されます。 要求に mySession Cookie がない場合は、新しい mySession Cookie が要求者に発行されます。 それ以外の場合、セッション データは、カスタム データ アクセス オブジェクト SessionPersistence を使用してSQL Serverから取得されます。 dsn と SessionExpiration の値は、web.configから取得されます。

      override protected void OnInit(EventArgs e)
      {
         InitializeComponent();
         base.OnInit(e);
      }
      private void InitializeComponent()
      {    
         cookie = this.Request.Cookies[sessionPersistence.SessionID];

         if (cookie == null)
         {
            Session = new mySession();
            CreateNewSessionCookie();
            IsNewSession = true;
         }
         else
            Session = sessionPersistence.LoadSession(
Server.UrlDecode(cookie.Value).ToLower().Trim(), 
dsn, 
SessionExpiration
);
            
         this.Unload += new EventHandler(this.PersistSession);
      }
      private void CreateNewSessionCookie()
      {
         cookie = new HttpCookie(sessionPersistence.SessionID, 
            sessionPersistence.GenerateKey());
         this.Response.Cookies.Add(cookie);
      }

SessionPersistence クラスは、Microsoft .NET Frameworkの BinaryFormatter を使用して、最適なパフォーマンスを得るために、セッション状態をバイナリ形式でシリアル化および逆シリアル化します。 結果として得られるバイナリ セッション状態データは、イメージ フィールド型としてSQL Serverに格納できます。

      public  mySession LoadSession(string key, string dsn, 
                                    int SessionExpiration)
      {
         SqlConnection conn = new SqlConnection(dsn);
         SqlCommand LoadCmd = new SqlCommand();
         LoadCmd.CommandText = command;
         LoadCmd.Connection = conn;
         SqlDataReader reader = null;
         mySession Session = null;

         try
         {
            LoadCmd.Parameters.Add("@ID", new Guid(key));
            conn.Open();
            reader = LoadCmd.ExecuteReader();
            if (reader.Read())
            {
               DateTime LastAccessed =
 reader.GetDateTime(1).AddMinutes(SessionExpiration);
               if (LastAccessed >= DateTime.Now)
                  Session = Deserialize((Byte[])reader["Data"]);
            }
         }
         finally
         {
            if (reader != null)
               reader.Close();
            if (conn != null)
               conn.Close();
         }
         
         return Session;
      }
private mySession Deserialize(Byte[] state)
      {
         if (state == null) return null;
         
         mySession Session = null;
         Stream stream = null;

         try
         {
            stream = new MemoryStream();
            stream.Write(state, 0, state.Length);
            stream.Position = 0;
            IFormatter formatter = new BinaryFormatter();
            Session = (mySession)formatter.Deserialize(stream);
         }
         finally
         {
            if (stream != null)
               stream.Close();
         }
         return Session;
      }

要求の最後に、Page クラス Unload イベントが発生し、Unload イベントに登録されているイベント ハンドラーによってセッション データがバイナリ形式にシリアル化され、結果のバイナリ データがSQL Serverに保存されます。

      private void PersistSession(Object obj, System.EventArgs arg)
      {      sessionPersistence.SaveSession(
               Server.UrlDecode(cookie.Value).ToLower().Trim(), 
               dsn, Session, IsNewSession);
      }
      public void SaveSession(string key, string dsn, 
mySession Session, bool IsNewSession)
      {
         SqlConnection conn = new SqlConnection(dsn);
         SqlCommand SaveCmd = new SqlCommand();         
         SaveCmd.Connection = conn;
         
         try
         {
            if (IsNewSession)
               SaveCmd.CommandText = InsertStatement;
            else
               SaveCmd.CommandText = UpdateStatement;

            SaveCmd.Parameters.Add("@ID", new Guid(key));
            SaveCmd.Parameters.Add("@Data", Serialize(Session));
            SaveCmd.Parameters.Add("@LastAccessed", DateTime.Now.ToString());
      
            conn.Open();
            SaveCmd.ExecuteNonQuery();
         }
         finally
         {
            if (conn != null)
               conn.Close();
         }
      }
private Byte[] Serialize(mySession Session)
      {
         if (Session == null) return null;

         Stream stream = null;
         Byte[] state = null;

         try
         {
            IFormatter formatter = new BinaryFormatter();
            stream = new MemoryStream();
            formatter.Serialize(stream, Session);
            state = new Byte[stream.Length];
            stream.Position = 0;
            stream.Read(state, 0, (int)stream.Length);
            stream.Close();
         }
         finally
         {
            if (stream != null)
               stream.Close();
         }
         return state;
      }

SessionPage クラスとそれに関連付けられているクラスは、SessionUtility アセンブリにパッケージ化されます。 新しい ASP.NET プロジェクトでは、SessionUtility アセンブリへの参照が行われ、すべてのページは、従来の ASP コードとセッションを共有するために、Page クラスではなく SessionPage から派生します。 移植が完了すると、新しいアプリケーションは、SessionPage クラスの Session 変数宣言をコメントアウトして基本 HttpSession を再表示することで、ネイティブ HttpSession オブジェクトを使用するように切り替えることができます。

ASP 実装

ネイティブ ASP セッションでは、セッション データのみをメモリに格納できます。 セッション データをSQL Serverに格納するために、カスタムの Microsoft® Visual Basic® 6.0 COM オブジェクトは、ネイティブ セッション オブジェクトを使用するのではなく、セッション状態を管理するために書き込まれます。 この COM オブジェクトは、各 Web 要求の開始時にインスタンス化され、SQL Serverからセッション データが再読み込みされます。 ASP スクリプトが完了すると、このオブジェクトは終了し、セッション状態はSQL Serverに保持されます。

Visual Basic 6 COM Session オブジェクトの主な目的は、Microsoft® インターネット インフォメーション サーバーの組み込みオブジェクトへのアクセスを提供することです。 Visual Basic 6.0 COM Session オブジェクトは、SessionUtility アセンブリの mySession クラスを使用してセッション状態を保持し、SessionUtility の SessionPersistence クラスを使用してセッション データを読み込み、SQL Serverで保存します。 mySession クラスと SessionPersistence クラスは、regasm.exe ユーティリティを使用して COM オブジェクトとして公開されます。 regasm.exe ユーティリティは、COM クライアントが Framework クラスを使用するためのタイプ ライブラリを登録および作成できます。

セッション状態情報は、オブジェクトの構築中に再読み込みされます。 コンストラクター (class_initialize) は、最初に Application オブジェクトからセッション Cookie、セッション タイムアウト (SessionTimeOut)、およびデータベース接続文字列 (SessionDSN) を取得し、セッション データを保持するクラス mySession のインスタンスを作成します。 その後、コンストラクターは、指定された Cookie を使用してSQL Serverからセッション データを再読み込みしようとします。 SQL Serverにセッション情報がない場合、またはセッションの有効期限が切れている場合は、新しい Cookie が発行されます。 SQL Sever がセッション状態データと共にを返す場合、セッション状態は mySession オブジェクトに格納されます。

Private Sub Class_Initialize()
On Error GoTo ErrHandler:
    Const METHOD_NAME As String = "Class_Initialize"
    Set mySessionPersistence = New SessionPersistence
    Set myObjectContext = GetObjectContext()
    mySessionID = ReadSessionID()
    myDSNString = GetConnectionDSN()
    myTimeOut = GetSessionTimeOut()
    myIsNewSession = False
    Call InitContents
    
    Exit Sub
ErrHandler:
    Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description
End Sub

Private Sub InitContents()
On Error GoTo ErrHandler:
    Const METHOD_NAME As String = "InitContents"    
    If mySessionID = "" Then
        Set myContentsEntity = New mySession
        mySessionID = mySessionPersistence.GenerateKey
        myIsNewSession = True
    Else
        Set myContentsEntity = 
        mySessionPersistence.LoadSession(mySessionID, myDSNString, myTimeOut)
    End If
        
    Exit Sub
ErrHandler:
    Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description
End Sub

オブジェクト インスタンスがスクリプトのスコープ外になると、デストラクター (class_terminate) が実行されます。 デストラクターは、 SessionPersistence.SaveSession() メソッドを使用してセッション データを保持します。 これが新しいセッションの場合、デストラクターは新しい Cookie もブラウザーに送り返します。

Private Sub Class_Terminate()
On Error GoTo ErrHandler:
    Const METHOD_NAME As String = "Class_Terminate"
    Call SetDataForSessionID
    Exit Sub 
ErrHandler:
 Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description  
End Sub

Private Sub SetDataForSessionID()
On Error GoTo ErrHandler:
    Const METHOD_NAME As String = "SetDataForSessionID"
    Call mySessionPersistence.SaveSession(mySessionID, 
myDSNString, myContentsEntity, myIsNewSession)
    
    If myIsNewSession Then Call WriteSessionID(mySessionID)
    
    Set myContentsEntity = Nothing
    Set myObjectContext = Nothing
    Set mySessionPersistence = Nothing
    Exit Sub
ErrHandler:
    Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description
End Sub

SessionUtility プロジェクト、COM セッション マネージャー、デモ コードのソース コード ASP.NET ダウンロードするには、記事の上部にあるリンクをクリックします。

デモ プログラム

デモ プログラムは、数値をインクリメントして表示するように設計されています。 読み込まれるページに関係なく、数値の値はSQL Serverに格納され、従来の ASP と ASP.NET 間で共有されるため、数値は増加し続けます。

デモ プログラムを設定する手順

  1. SessionDemoDb という名前の新しいデータベースを作成します。
  2. SessState テーブルを作成します (osql.exe –E –d SessionDemoDb –i Session.sql)。
  3. Demo という名前の新しい仮想ディレクトリを作成します。
  4. [ASP 構成] タブの [ASP セッション] をオフにします。
  5. web.config、testPage.aspx、Global.asa、testPage.asp、GlobalInclude.asp を仮想ディレクトリにコピーします。
  6. Global.asa と web.configの DSN 文字列設定を更新します。セッション タイムアウト設定は省略可能です。 既定値は 20 分です。 
  7. グローバル アセンブリ キャッシュ (gacutil /i SessionUtility.dll) にSessionUtility.dllをインストールします。
  8. regasm.exe (regasm.exe SessionUtility.dll /tlb:SessionUtility.tlb) を使用して、SessionUtility.dllを COM オブジェクトとして公開します。
  9. SessionManager.dllをローカル ディレクトリにコピーし、regsvr32.exeを使用して登録します (regsvr32 SessionManager.dll)。
  10. IUSR_<machine_name アカウントにSessionMgr.dll> への読み取りと実行のアクセス権を付与します。

デモ プログラムを実行する手順

  1. Microsoft® Internet エクスプローラーを起動します。
  2. クラシック ASP の testPage.asp を読み込みます。 Web ページに数値 "1" が表示されます。
  3. [インターネット エクスプローラーの更新] をクリックして、ページを再読み込みします。 数値はインクリメントする必要があります。
  4. ASP.NET の URL を testPage.aspx に変更します。 数値は増え続ける必要があります。
  5. 最初に testPage.aspx ページを開始することで、同じプロセスを繰り返すことができます。

既存の ASP アプリケーションに COM オブジェクトを組み込む

ASP アプリケーションを開発する一般的な方法は、共通のコードと定数を共有するために、各スクリプトの先頭に ファイルを含める方法です。 カスタム セッション オブジェクトを組み込む最善の方法は、共通インクルード ファイルにインスタンス化コードを追加することです。 最後の手順は、セッション オブジェクトへのすべての参照をカスタム セッション変数名に置き換えるだけです。

制限/改善

このソリューションでは、 Session オブジェクトに COM オブジェクトを格納する既存の ASP アプリケーションはサポートされません。 この場合、カスタム セッション オブジェクトを使用するには、状態をシリアル化または逆シリアル化するためにカスタム マーシャラーが必要です。 さらに、このソリューションでは、文字列の型配列の格納はサポートされていません。 この機能を実装するには、Microsoft® Visual Basic® 6.0 Join 関数を使用して、すべての配列要素を 1 つの文字列に結合してからセッション オブジェクトに格納します。 逆に行うには、Visual Basic 6.0 Split 関数を使用して、文字列を個々の配列要素に分割します。 .NET Framework側では、Join メソッドと Split メソッドは String クラスのメンバーです。

まとめ

ASP.NET は、新しいプログラミング パラダイムとアーキテクチャを表し、従来の ASP よりも多くの利点を提供します。 ASP から ASP.NET への移植は単純なプロセスではありませんが、プログラミング モデルの向上と ASP.NET のパフォーマンスの向上により、変換プロセスは価値のあるものになります。 Session オブジェクトに COM オブジェクトを格納する場合を除き、この記事で説明する方法では、移行プロセスを簡単にするソリューションが提供されます。

著者について

ビリー・ユエン は、Microsoft テクノロジ センター シリコン バレーの北カリフォルニアで働いています。 このセンターでは、Microsoft .NET Framework ソリューションの開発に重点を置いています。 彼 は で billyy@microsoft.com到達できます。

© Microsoft Corporation. All rights reserved.