SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

ASP.NET 2.0でプロパティ永続化コントロールを作成する

ViewStateではなくPreservePropertyControlを使った永続化

ダウンロード サンプルソース (62.6 KB)

その他の永続化メカニズム

 ControlStateは有効に機能し、おそらく永続データを格納するのに最も信頼の置ける方法ですが、ASP.NET 1.1を使用している場合や、ViewStateによる永続化を(たとえ恒久的でも)好まない場合は、別のストレージメカニズムを使用することもできます。PreservePropertyControlは、次の4つの永続化モードをサポートしています。

  • ControlState
  • HiddenVariable
  • SessionVariable
  • CachePerPage

 ControlState以外のモードでは、ページパイプラインへのカスタムフックと、データをシリアル化するための多少の追加処理が必要です。これの基になるコントロールを作成したときに、私は効率が悪くてかなり多くのコードを必要とする独自のシリアル化メカニズムを使っていました。私のブログに寄せられたいくつかの提案の中で、誰かがLosFormatterを使うことを提案してくれました。LosFormatterはSystem.Web.UI名前空間にある、あまり知られていない文字列シリアライザで、オブジェクトのシリアル化に利用できます。

 このシリアライザで使われるフォーマットは、完全なバイナリフォーマッタよりも軽量です。一般的な型をより効率的にエンコードするうえに、多くの場合、コストのかかる完全な.NETシリアル化メカニズムを迂回するからです。リスト3に、PreservePropertyControlのControlState以外のモードのストレージメカニズムに関するコードを示します。

リスト3
/// <summary>
/// Read in data of preserved properties in OnInit
/// </summary>
protected override void OnInit(EventArgs e)
{
  base.OnInit(e);
  if (this.Enabled)
  {
    if (this.StorageMode == PropertyStorageModes.ControlState)
      this.Page.RegisterRequiresControlState(this);
    else if (this.Page.IsPostBack)
      this.LoadStateFromLosStorage();
  }
}

/// <summary>
/// Write out data for preserved properties in OnPreRender
/// </summary>
protected override void OnPreRender(EventArgs e)
{
  if (this.Enabled && StorageMode
       != PropertyStorageModes.ControlState)
    this.SaveStateToLosStorage();
    base.OnPreRender(e);
}

/// <summary>
/// Saves state the specified storage mechanism by
/// first serializing to a string with the LosFormatter
/// </summary>
private void SaveStateToLosStorage()
{
  string Serialized = LosSerializeObject(this.SaveControlState());
  if (this.StorageMode == PropertyStorageModes.HiddenVariable)
      this.Page.ClientScript.RegisterHiddenField(
         "__" + this.UniqueID, Serialized);
  else if (this.StorageMode == PropertyStorageModes.SessionVariable)
     HttpContext.Current.Session["__" + this.UniqueID] = Serialized;
  else if (this.StorageMode == PropertyStorageModes.CachePerPage)
  {
    if (this.PreservePropertyKey == null)
        this.PreservePropertyKey =
          Guid.NewGuid().ToString().GetHashCode().ToString("x");
     HttpContext.Current.Cache[this.PreservePropertyKey] = Serialized;
     this.Page.ClientScript.RegisterHiddenField(
       "__PreservePropertyKey",
       this.PreservePropertyKey);
   }
}

/// <summary>
/// Retrieves the serialized data from the Storage medium
/// as string using LosFormatter formatting.
/// </summary>
private void LoadStateFromLosStorage()
{
  string RawBuffer = null;
  if (this.StorageMode == PropertyStorageModes.HiddenVariable)
  {
    RawBuffer = HttpContext.Current.Request.Form["__"
                                                + this.UniqueID];
   if (RawBuffer == null)
     return;
   }
   else if (this.StorageMode == PropertyStorageModes.SessionVariable)
   {
     RawBuffer = HttpContext.Current.Session["__" + this.UniqueID]
         as string;
     if (RawBuffer == null)
        return;
   }
   else if (this.StorageMode == PropertyStorageModes.CachePerPage)
   {
     this.PreservePropertyKey =
        HttpContext.Current.Request.Form["__PreservePropertyKey"];
     if (this.PreservePropertyKey == null)
        return;
     RawBuffer = HttpContext.Current.Cache[this.PreservePropertyKey]
        as string;
   }
   if (RawBuffer == null)
      return;
  // *** Retrieve the persisted HashTable and pass to LoadControlState
  // *** to handle the assignment of property values
   this.LoadControlState(LosDeserializeObject(RawBuffer));
}

private string LosSerializeObject(object obj)
{
  LosFormatter output = new LosFormatter();
  StringWriter writer = new StringWriter();
  output.Serialize(writer, obj);
  return writer.ToString();
}
private object LosDeserializeObject(string inputString)
{
  LosFormatter input = new LosFormatter();
  return input.Deserialize(inputString);
}

 ControlState以外の永続化モードでは、OnInit()メソッドとOnPreRender()メソッドの両方がフックされます。OnInit()LoadStateFromLosStorage()を呼び出し、OnPreRender()SaveStateToLosStorage()を呼び出します。これらのメソッドは、実際のハッシュテーブルの生成と解析を上述のSaveControlState()およびLoadControlState()に委任します。次に、SaveStateToLosStorage()はLosFormatterを呼び出して、このハッシュテーブルの文字列表現を作成します。それぞれのストレージメカニズムは、この文字列を適切なストレージメカニズムで格納します。

 HiddenFormVariableでは、データは次のように格納されます。

this.Page.ClientScript.RegisterHiddenField(
   "__" + this.UniqueID, Serialized);

 この場合は、次のようなコードを使ってデータを読み込みます。

RawBuffer = HttpContext.Current.Request.Form[
   "__" + this.UniqueID];

 取得されたローバッファは、シリアル化解除されてハッシュテーブルに戻り、LoadControlState()に渡されます。このメソッドは値をコントロールに再割り当てします。

 セッション変数ストレージは少し異なるため、少々説明が必要です。セッションストレージでは、保持されたプロパティの状態を、すべてのページに再利用されるセッション変数に格納します。従って、すべてのページが同じセッション変数インスタンスを取得し、ユーザーごとに1つの変数があります。

 セッションを使うことには大きな利点があります。永続データをSessionオブジェクトに格納すると、データがネットワーク経由で送信されないので、ページのサイズが小さくなります。また、InProcストレージを使用するとシリアル化が発生しないので、セッションストレージはいかなる種類のViewStateエンコードよりもはるかに高速になる傾向があります。

 この手法を使うときは注意が必要です。同じブラウザセッションで2つのウィンドウを開いたり、同じセッションが同時にアクティブになるフレームページを使用したりすると、誤った状態が復元されるという問題が発生します。サンプルのデモページでこれを試すことができます。ページを実行し、色を設定し、送信します。

 次にCtrl+nキー(またはタブがある場合はCtrl+tキー)を押して、同じブラウザセッションで新しいブラウザウィンドウを作成します。同じページを開き、別の色を選択して[Show]をクリックします。

 今度は最初のページに戻り、ポストバックボタンをクリックします。2番目のインスタンスの色が最初のインスタンスに表示されるはずです。これは明らかに誤りです。最初のインスタンスが2番目のインスタンスの永続データを取得してしまっています。

 複数のブラウザウィンドウやフレームを実行する必要がない内部アプリケーションでは、これが問題にならないこともありますが、そのような場合でもこのオプションの使用には十分に注意してください。セッション変数に対して一意のページ単位のIDを生成することで、この問題を解決できる場合もありますが、それによってセッション状態が大量の永続メモリでいっぱいになる可能性があります。セッション変数ストレージの使用は、大量の永続データが含まれるページを扱う場合で、ポストバックのたびにネットワーク経由でデータを送信するのを避けたい場合にのみ検討してください。

 同様に、キャッシュオブジェクトを使用することもできます。セッションの手法と異なり、キャッシュの手法ではセッションに書き込み、ページごとに新しいGUIDを割り当てます。そのため、すべてのユーザーのすべての新しいページが新しいキャッシュエントリを作成します。

 セッションの場合と同様に、トレードオフに注意してください。Cacheオブジェクトを使うと多数のキャッシュエントリが生成されるため、多くのページから成るアクセス数が非常に多いサイトでは、この手法はおそらく現実的ではありません。また、キャッシュは複数のマシン間では保持されないため、Webファーム環境ではキャッシュの手法を利用できません。

 それでも、キャッシュやセッションの手法によってページが軽量になり、状態ストレージのパフォーマンスが向上するので、これらのオプションを試してみることをお勧めします。

 ControlStateとHiddenFormVariableの手法は、どちらもページ自体にPOSTデータとして格納されるので、こうした問題に見舞われることはありません。この2つでは、ControlStateの方が信頼性に勝ります。ControlStateにはページパイプラインの一部として発生する独自のイベントがあり、他のコントロールとのタイミングの問題が発生しかねない既存のイベントのフックが不要だからです。HiddenFormVariableの実装は、主にASP.NET 1.xをサポートするために提供しています。

無駄を省いて環境保全

 最近開発したいくつかのアプリケーションでPreservePropertyControlを使ってみて、これまで状態ストレージを検討すらしなかったような場面でこのコントロールが大いに役立つことが分かりました。永続化する対象を正確に制御できると、永続化された状態のサイズを小さく抑えつつ、永続化された値への真のプロパティアクセスを提供する柔軟なページを簡単に作成できます。私は自分のページでViewStateを全部まとめてオフにする傾向があるため、いくつかのアイテムを宣言的に永続化できるコントロールがあると非常に助かります。全体として、このコントロールはさまざまな形で役に立ちます。

 私の場合、最もよくあるシナリオは、グリッドやリストのリストコントロール、ページング操作、および並べ替えの設定によるSelectedValueプロパティなどのリスト状態の設定を管理することです。現在のレコードIDなどのページレベルの状態の格納も非常にうまく機能します。

 このコントロールの良い点は、保持されたプロパティを操作するのがはるかに自然になることです。永続化メカニズムを一切気にせずに、プロパティを参照するだけで済むからです。いったんPreservePropertyControlに追加してしまえば、処理は完全に透過的です。基本的に、このコントロールを使うとASP.NETの無差別なViewStateポリシーを根本から変えることができます。何もかも自動的にViewStateに格納する代わりに、明確に格納する必要があるものだけを格納します。これにより、ページ内でViewStateに要求されるサイズを激減させることができます。このコントロールでページのサイズを大幅に削減できないかどうか、各自で試してみてください。

 このコントロールが読者の役に立ち、本稿がさまざまなページレベルの状態維持メカニズムを理解する手がかりになることを願っています。実生活でもWebコードでも、無駄を省いて環境保全に努めましょう!

 本稿に関するご意見、ご質問、ご提案などがあれば、こちらに投稿してください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Rick Strahl(Rick Strahl)

ハワイのマウイ島にあるWest Wind Technologies社の社長。同社はWebおよび分散アプリケーションの開発とツールを専門にしており、Windowsサーバー製品、. NET、Visual Studio、およびVisual FoxProに主軸を置いている。RickはWest Wind Web Connection、West Wind Web Store、およびWest Wind HTML Help Bui...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1327 2007/05/22 08:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー