SHOEISHA iD

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

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

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

japan.internet.com翻訳記事

ASP.NETサイトナビゲーションの9つの問題のソリューション:パート2

ASP.NET 2.0における高度なサイトマップソリューション

#9:ナビゲーションを最新に保つ

 ASP.NETのプロバイダモデルは、動的なコンテンツをサイトマップに追加したいときにその真価を発揮します。これまでの8つのソリューションで紹介した「Web.sitemap」アプローチのメリットをそのまま活かして、プロバイダモデルを拡張し、データベースドリブンのsiteMapNodesをサイトマップの任意のセクションに簡単に挿入することができます。また、データベースコンテンツをキャッシュに保管し、データベース変更があったときだけ更新することも簡単です。最後のソリューションでは、これらの2つの課題への取り組み方を示します。

著者メモ
 このソリューションの後半のキャッシングの説明では、SQL Server 2005とASP.NET 2.0の新しい依存関係テクノロジのみに焦点を当てます。

カスタムサイトマッププロバイダを構築する

 カスタムサイトマッププロバイダは非常に柔軟性があり保守のしやすいソリューションを提供しますが、他にも注目すべきアプローチが2つあります。第一に、「Web.sitemap」はurl属性でquerystringパラメータをサポートしているので、単一のページを複数のsiteMapNodesで使用することができます。問題は、掲示板への投稿など、変更頻度の高いページでの「Web.sitemap」の維持がWebマスターの悩みの種になることです。

 第二に、siteMapNodesiteMapFile属性をサポートしており、この属性を通じて別個の.sitemapファイルから自分のコンテンツを取得することができます。この手法を利用するには、データベーストリガの内側から.sitemapファイルを作成/上書きします。ただし、このアプローチでは、データベースとWebサーバーが同一のマシン上になければならないので、アプリケーションのスケーラビリティが制限されます。この手法をより適切に使うためには、手動で管理される「Web.sitemap」を使用して、コードジェネレータによって管理される.sitemapファイルからデータをプルする必要があります(Blue Inkはこの手法を使っています。詳しくは、著者紹介をご覧ください)。

 とにかく、最善のアプローチはカスタムサイトマッププロバイダを作成することだと言えるでしょう。このプロセスでは、SiteMapProviderを継承する新しいクラスを作成し、それを「Web.config」ファイルに認識させ、「Web.sitemap」から参照する必要があります。「Web.sitemap」から新しいプロバイダを参照するには、siteMapNodeの最後の属性providerを使います。

<siteMapNode provider="CategoriesProvider" />

 provider属性を使うことで、siteMapNodeは「Web.config」で指定されたプロバイダから自分のコンテンツを取得することができます。例に示されている値CategoriesProviderは、Northwind.CategoriesSiteMapProviderクラスを参照します。

<siteMap defaultProvider="XmlSiteMapProvider" enabled="true">
  <providers>
    <clear />
    <add name="XmlSiteMapProvider"
      description="Default SiteMap provider"
      type="System.Web.XmlSiteMapProvider"
      siteMapFile="Web.sitemap"
      securityTrimmingEnabled="true" />
    <add name="CategoriesProvider"
      description="Displays Categories From Northwind"
      connectionStringName="Northwind"
      type="Northwind.CategoriesSiteMapProvider" />
  </providers>
</siteMap>

 既定のサイトマッププロバイダは1つでなければなりませんが、ASP.NETは複数のサイトマッププロバイダをサポートしているので互いに参照することができます。このアプローチにより、サイトマップのセクションをコンパートメント化することも可能になります。「Web.sitemap」のエントリと「Web.config」のエントリを構成し終えたら、カスタムプロバイダを作成しなければなりません。

 前述の通り、各サイトマッププロバイダは最終的にはSiteMapProviderクラスから継承しなければなりません。ただし、SiteMapProviderの部分実装として、サイトマップ情報をメモリに読み込む処理を簡易化するStaticSiteMapProviderという中継クラスが用意されています。StaticSiteMapProviderは、基本クラスSiteMapProviderに、AddNode()RemoveNode()Clear()BuildSiteMap()などのメソッドを追加したものです。

 カスタムプロバイダでオーバーライドする必要性が高い関数は、BuildSiteMap()と、SiteMapProviderから継承したInitialize()です。ASP.NETはアプリケーションの実行期間中にInitialize()を1回だけ呼び出すので、これは「Web.config」から構成設定を取得するのにちょうど適した場所です。反対に、BuildSiteMap()は頻繁に呼び出され、ルートのsiteMapNodeを返さなければなりません。カスタムサイトマッププロバイダでは、パフォーマンスを向上させるために、可能なときにはキャッシュ内にあるサイトマップのコピーを返すようにBuildSiteMap()を実装する必要があります。この処理の一般的な流れを疑似コードの形で紹介しておきます。

private SiteMapNode mnodeRoot = null;
public override SiteMapNode BuildSiteMap() {
  if (site map has already been built)
    return mnodeRoot;

  Clear();
  SiteMapNode nodeRoot = GetRootNode();
  AddNode(nodeRoot);

  open a connection to Northwind
  select all categories

  foreach (category) {
    SiteMapNode nodeCategory = CreateCategoryNode();
    AddNode(nodeCategory, nodeRoot);
  }
}

 残念ながら、前述のアプローチは2つの大きな問題の影響を受けます。1つ目の問題は、サイトマップ情報は手動で(例えば、Webサイトの保守セクションでmnodeRootをnullに設定することで)無効化しない限り変更されないことです。サイトマップ情報は、表示されるデータが変更されたら自動的に更新されるのが理想です。ここでは、SqlCacheDependencyオブジェクトを使ってこの問題を解決する方法を簡単に説明します。

 2つ目の問題はもっと細かいことですが、極めて重要です。ASP.NET 2.0はSiteMapProvider(および実際にはすべてのプロバイダ)の実装にシングルトン(Singleton)パターンを使っています。つまり、ASP.NET 2.0はすべてのページ要求間で(すなわち複数のスレッド間で)カスタムサイトマッププロバイダの1つのインスタンスを共有しているわけです。カスタムサイトマッププロバイダのアプローチのメリットの1つは、メンバ変数に永続データを格納できることですが、サイトマッププロバイダの状態を変更するあらゆるコードでスレッドセーフを考慮しなければならないという短所もあります。従って、複数のスレッドが同時にサイトマップを作成しようとすることを念頭に置いて、前述のアプローチを変更しなければなりません。

private readonly object mobjLock = new object();

public override SiteMapNode BuildSiteMap() {
  if (site map has already been built)
    return mnodeRoot;

  lock(mobjLock) {
    Clear();
    SiteMapNode nodeRoot = GetRootNode();
    AddNode(nodeRoot);

    loop through data {
      call AddNode()
    }
  }
}

 このコードでは、2つのスレッドがサイトマッププロバイダの状態の更新を同時に試みることはありません。コードがlock()を呼び出すのは状態が更新されるときだけです。事前に呼び出すと、不必要にパフォーマンスが低下します。

次のページ
変更データを検出する

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

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

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

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

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

Lee Richardson(Lee Richardson)

Automated Architecture Inc.の創設者かつCEO。Blue InkアプリケーションおよびJAGソフトウェア開発方法論の作成者でもある。現在はHeadstrongコンサルティング社でシニアコンサルタントとして勤務し、高速アプリケーション開発(Rapid Application Development)テクノロジを専門とする。ソフトウェア開発経験は9年におよび、Microsoft認定ソリューションデベロッパー(MCSD)およびプロジェクトマネージメントプロフェッショナル(Project Management Professional:PMP)の資格を持つ。

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/585 2006/09/29 00:00

イベント

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

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

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

メールバックナンバー