ViewStateをセッションに保存する

ASP.NETアプリケーションでは、ページの状態(プロパティや動的に作成されたコントロール)をViewStateに自動的に保持し、ポストバック時に復元することによって内容の復元や項目変更(イベント発生)の判断を行っている。
しかし、ViewStateに格納した内容はデフォルトではhidden要素としてHTMLに出力されるため、クライアント間のデータ量が増大し、特に帯域が限られている場合にはラウンドトリップ時間への影響が大きくなる。したがって、ViewStateに状態を格納せず、自前で状態の保持および復元を実装することが選択されることがままある。
これを避けるために、ASP.NET1.1までの場合であってもPage.SavePageStateToPersistenceMediumメソッドおよびPage.LoadPageStateFromPersistenceMediumメソッドを実装することによって独自のViewState保持方法を実装することができる。これにより、前述のViewStateによる状態の自動保持を生かしたままHTMLのサイズ増加を防ぐことができる。

protected override object LoadPageStateFromPersistenceMedium()
{
  return Session["ViewState"];
}

protected override void SavePageStateToPersistenceMedium(object state)
{
  Session["ViewState"] = state;
}

このくらいの実装で万事うまくいくのかというと、やはりそんなに簡単ではない。これだと、セッションが共有されている複数のウィンドウを扱う際に、ViewStateが上書きされてしまう。したがって、ページごとに一意なIDを生成して、セッションの読み書きを行う際のキー名はそのIDを使用するという対処が必要になる。この場合、そのIDをどこに保持するかも問題となるが、これは元々のViewStateを使用することになるのだろうか。

private Guid key;

protected override object LoadPageStateFromPersistenceMedium()
{
  Pair p = (Pair)base.LoadPageStateFromPersistenceMedium();
  key = new Guid((string)p.Second);

  return Session["ViewState" + key.ToString()];
}

protected override void SavePageStateToPersistenceMedium(object state)
{
  if (!IsPostBack)
    key = Guid.NewGuid();

  Session["ViewState" + key.ToString()] = state;
  base.SavePageStateToPersistenceMedium(new Pair(null, key.ToString()));
}

しかし、ASP.NET 2.0ではこれを行うためのクラスが標準で提供されている。
SessionPageStatePersisterクラスが、前述のようにViewStateをSessionに(自動的に衝突しないキーを生成して)保持してくれる。
しかも、前述のようにページを変更することなく、ViewStateの保持方法を設定することが可能である。これは、ASP.NETのブラウザごとのコントロールの描画方法を外部から指定する機能を使用している。
App_Browsers/BrowserFile.browser

<browsers>
  <browser refID="Mozilla">
    <controlAdapters>
      <adapter 
        controlType="System.Web.UI.Page"                            
        adapterType="Adapter.SamplePageAdapter" />
    </controlAdapters>
  </browser>
</browsers>

SamplePageAdapter.cs

[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
public class SamplePageAdapter : PageAdapter
{
  public override PageStatePersister GetStatePersister()
  {
    return new SessionPageStatePersister(Page);
  }
}

これで、あとは通常通りにViewStateを使用すれば、ViewStateの内容がSessionを用いて保持されるという仕組み。