カスタム データベース駆動型サイト マップ プロバイダーを構築する (C#)
ASP.NET 2.0 の既定のサイト マップ プロバイダーは、静的 XML ファイルからデータを取得します。 XML ベースのプロバイダーは、多くの小規模および中規模の Web サイトに適していますが、大規模な Web アプリケーションでは、より動的なサイト マップが必要です。 このチュートリアルでは、ビジネス ロジック レイヤーからデータを取得し、データベースからデータを取得するカスタム サイト マップ プロバイダーを構築します。
はじめに
ASP.NET 2.0 s サイト マップ機能を使用すると、ページ開発者は、XML ファイルなどの永続的なメディアで Web アプリケーションのサイト マップを定義できます。 定義すると、サイト マップ データは、名前空間のSystem.Web
クラスを介してSiteMap
、または SiteMapPath、Menu、TreeView コントロールなどのさまざまなナビゲーション Web コントロールを介してプログラムでアクセスできます。 サイト マップ システムはプロバイダー モデルを使用して、さまざまなサイト マップのシリアル化実装を作成し、Web アプリケーションに接続できるようにします。 ASP.NET 2.0 に付属する既定のサイト マップ プロバイダーは、サイト マップ構造を XML ファイルに保持します。 マスター ページとサイト ナビゲーションのチュートリアルに戻ると、この構造を含む という名前Web.sitemap
のファイルが作成され、新しいチュートリアル セクションごとに XML が更新されています。
これらのチュートリアルのように、サイト マップの構造がかなり静的である場合、既定の XML ベースのサイト マップ プロバイダーは適切に機能します。 ただし、多くのシナリオでは、より動的なサイト マップが必要です。 図 1 に示すサイト マップについて考えてみます。各カテゴリと製品は、Web サイトの構造のセクションとして表示されます。 このサイト マップでは、ルート ノードに対応する Web ページにアクセスするとすべてのカテゴリが一覧表示される場合があります。一方、特定のカテゴリの Web ページにアクセスすると、そのカテゴリの製品が一覧表示され、特定の製品の Web ページを表示すると、その製品の詳細が表示されます。
図 1: サイト マップの構造のカテゴリと製品の構成 (フルサイズの画像を表示する をクリックします)
このカテゴリベースの構造と製品ベースの構造はファイルに Web.sitemap
ハードコーディングできますが、カテゴリまたは製品が追加、削除、または名前変更されるたびにファイルを更新する必要があります。 その結果、サイト マップの構造がデータベースから取得された場合、またはアプリケーションのアーキテクチャのビジネス ロジック レイヤーから取得された場合、サイト マップのメンテナンスが大幅に簡略化されます。 これにより、製品とカテゴリが追加、名前変更、または削除されると、サイト マップは自動的に更新され、これらの変更が反映されます。
ASP.NET 2.0 s サイト マップのシリアル化はプロバイダー モデルの上に構築されているため、データベースやアーキテクチャなどの代替データ ストアからデータを取得する独自のカスタム サイト マップ プロバイダーを作成できます。 このチュートリアルでは、BLL からデータを取得するカスタム プロバイダーを構築します。 始めましょう。
注意
このチュートリアルで作成したカスタム サイト マップ プロバイダーは、アプリケーションのアーキテクチャとデータ モデルと密接に結び付けられます。 Jeff Prosise はサイト マップを SQL Server に格納し、SQL Site Map Provider You ve waiting For articles では、サイト マップ データをSQL Serverに格納するための一般化されたアプローチを調べます。
手順 1: カスタム サイト マップ プロバイダー Web ページの作成
カスタム サイト マップ プロバイダーの作成を開始する前に、まず、このチュートリアルに必要な ASP.NET ページを追加しましょう。 まず、 という名前 SiteMapProvider
の新しいフォルダーを追加します。 次に、次の ASP.NET ページをそのフォルダーに追加し、各ページをマスター ページに Site.master
関連付けます。
Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx
また、 フォルダーに CustomProviders
サブフォルダーを App_Code
追加します。
図 2: サイト マップ Provider-Related チュートリアルの ASP.NET ページを追加する
このセクションにはチュートリアルが 1 つしかないため、セクションのチュートリアルを一覧表示する必要 Default.aspx
はありません。 代わりに、 Default.aspx
GridView コントロールにカテゴリが表示されます。 これについては、手順 2 で説明します。
次に、 を更新 Web.sitemap
してページへの参照を Default.aspx
含めます。 具体的には、キャッシュの後に次のマークアップを <siteMapNode>
追加します。
<siteMapNode
title="Customizing the Site Map" url="~/SiteMapProvider/Default.aspx"
description="Learn how to create a custom provider that retrieves the site map
from the Northwind database." />
を更新した Web.sitemap
後、ブラウザーを使用してチュートリアル Web サイトを表示します。 左側のメニューに、唯一のサイト マップ プロバイダーチュートリアルの項目が含まれるようになりました。
図 3: サイト マップにサイト マップ プロバイダーのチュートリアルのエントリが含まれるようになりました
このチュートリアルメイン焦点は、カスタム サイト マップ プロバイダーを作成し、そのプロバイダーを使用するように Web アプリケーションを構成する方法を説明することです。 具体的には、図 1 に示すように、ルート ノードと各カテゴリと製品のノードを含むサイト マップを返すプロバイダーを構築します。 一般に、サイト マップ内の各ノードで URL を指定できます。 サイト マップの場合、ルート ノードの URL は になります ~/SiteMapProvider/Default.aspx
。これにより、データベース内のすべてのカテゴリが一覧表示されます。 サイト マップ内の各カテゴリ ノードには、 を ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
指す URL があり、指定された categoryID 内のすべての製品が一覧表示されます。 最後に、各製品サイト マップ ノードが を ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID
指します。これにより、特定の製品の詳細が表示されます。
開始するには、および ページをDefault.aspx
ProductsByCategory.aspx
作成するProductDetails.aspx
必要があります。 これらのページは、それぞれ手順 2、3、4 で完了しています。 このチュートリアルの推力はサイト マップ プロバイダー上にあり、過去のチュートリアルではこのようなマルチページ マスター/詳細レポートの作成について説明しているので、手順 2 から 4 まで急いで進みます。 複数のページにまたがるマスター/詳細レポートを作成する際にリフレッシャーが必要な場合は、 マスター/詳細フィルターの 2 ページにわたるフィルター処理 に関するチュートリアルを参照してください。
手順 2: カテゴリの一覧を表示する
フォルダー内のDefault.aspx
ページをSiteMapProvider
開き、GridView をツールボックスからDesignerにドラッグし、その を ID
に設定しますCategories
。 GridView のスマート タグから、 という名前CategoriesDataSource
の新しい ObjectDataSource にバインドし、クラス s GetCategories
メソッドを使用してCategoriesBLL
データを取得するように構成します。 この GridView ではカテゴリが表示されるだけで、データ変更機能は提供されないため、UPDATE、INSERT、DELETE タブのドロップダウン リストを (None) に設定します。
図 4: メソッドを使用して GetCategories
カテゴリを返すように ObjectDataSource を構成する (フルサイズの画像を表示するをクリックします)
図 5: UPDATE、INSERT、DELETE タブの Drop-Down Lists を (なし) に設定します (フルサイズの画像を表示する場合はクリックします)
データ ソースの構成ウィザードが完了すると、Visual Studio によって、、NumberOfProducts
CategoryName
Description
および BrochurePath
の CategoryID
BoundField が追加されます。 GridView を編集して、 と Description
BoundFields のみが含まれるCategoryName
ようにし、BoundField の HeaderText
プロパティを CategoryName
Category に更新します。
次に、HyperLinkField を追加し、左端のフィールドになるように配置します。 DataNavigateUrlFields
プロパティを CategoryID
に、DataNavigateUrlFormatString
プロパティを ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}
にそれぞれ設定します。 プロパティを [製品の Text
表示] に設定します。
図 6: GridView に HyperLinkField を追加するCategories
ObjectDataSource を作成し、GridView のフィールドをカスタマイズすると、2 つのコントロールの宣言型マークアップは次のようになります。
<asp:GridView ID="Categories" runat="server" AutoGenerateColumns="False"
DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource"
EnableViewState="False">
<Columns>
<asp:HyperLinkField DataNavigateUrlFields="CategoryID"
DataNavigateUrlFormatString=
"~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}"
Text="View Products" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
SortExpression="CategoryName" />
<asp:BoundField DataField="Description" HeaderText="Description"
SortExpression="Description" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL"></asp:ObjectDataSource>
図 7 は、ブラウザーで表示される場合を示しています Default.aspx
。 カテゴリの [製品の表示] リンクをクリックすると、 ProductsByCategory.aspx?CategoryID=categoryID
手順 3 で作成する に移動します。
図 7: 各カテゴリが製品の表示リンクと共に一覧表示されている (フルサイズの画像を表示する をクリックします)
手順 3: 選択したカテゴリの製品を一覧表示する
ページを ProductsByCategory.aspx
開き、GridView を追加して という名前を付ける ProductsByCategory
。 スマート タグから、GridView を という名前 ProductsByCategoryDataSource
の新しい ObjectDataSource にバインドします。 クラス s GetProductsByCategoryID(categoryID)
メソッドを使用ProductsBLL
するように ObjectDataSource を構成し、UPDATE、INSERT、DELETE タブでドロップダウン リストを (None) に設定します。
図 8: クラス s GetProductsByCategoryID(categoryID)
メソッドをProductsBLL
使用する (フルサイズの画像を表示する場合にクリックします)
データ ソースの構成ウィザードの最後の手順では、 categoryID のパラメーター ソースの入力を求められます。 この情報は querystring フィールド CategoryID
を介して渡されるため、ドロップダウン リストから [QueryString] を選択し、図 9 に示すように [QueryStringField] テキスト ボックスに「CategoryID」と入力します。 [完了] をクリックしてウィザードを終了します。
図 9: categoryID パラメーターに CategoryID
Querystring フィールドを使用する (フルサイズの画像を表示するには、ここをクリックします)
ウィザードが完了すると、Visual Studio は対応する BoundFields と CheckBoxField を製品データ フィールドの GridView に追加します。 、UnitPrice
、ProductName
および SupplierName
BoundFields 以外のすべてを削除します。 これら 3 つの BoundFields プロパティを HeaderText
カスタマイズして、Product、Price、および Supplier をそれぞれ読み取ります。 BoundField を UnitPrice
通貨として書式設定します。
次に、HyperLinkField を追加し、左端の位置に移動します。 プロパティを Text
[詳細の表示] に設定し、その DataNavigateUrlFields
プロパティを に ProductID
設定し、その DataNavigateUrlFormatString
プロパティを に ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}
設定します。
図 10: ポイントするビューの詳細 HyperLinkField を追加する ProductDetails.aspx
これらのカスタマイズを行った後、GridView と ObjectDataSource の宣言型マークアップは次のようになります。
<asp:GridView ID="ProductsByCategory" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsByCategoryDataSource"
EnableViewState="False">
<Columns>
<asp:HyperLinkField DataNavigateUrlFields="ProductID"
DataNavigateUrlFormatString=
"~/SiteMapProvider/ProductDetails.aspx?ProductID={0}"
Text="View Details" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
<SelectParameters>
<asp:QueryStringParameter Name="categoryID"
QueryStringField="CategoryID" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
ブラウザーで表示 Default.aspx
に戻り、飲料の [製品の表示] リンクをクリックします。 これにより、 にアクセスし、飲料カテゴリに ProductsByCategory.aspx?CategoryID=1
属する Northwind データベース内の製品の名前、価格、サプライヤーが表示されます (図 11 を参照)。 このページをさらに拡張して、ユーザーをカテゴリ一覧ページ (Default.aspx
) に戻すリンクと、選択したカテゴリの名前と説明を表示する DetailsView または FormView コントロールを含めます。
図 11: [飲料の名前]、[価格]、[仕入先] が表示されます (クリックするとフルサイズの画像が表示されます)
手順 4: 製品の詳細を表示する
最後のページ () には、 ProductDetails.aspx
選択した製品の詳細が表示されます。 DetailsView を開ProductDetails.aspx
き、ツールボックスからDesignerにドラッグします。 DetailsView の ID
プロパティを にProductInfo
設定し、プロパティ値と Width
プロパティ値をHeight
クリアします。 スマート タグから DetailsView を という名前ProductDataSource
の新しい ObjectDataSource にバインドし、クラスの GetProductByProductID(productID)
メソッドからデータをプルするように ObjectDataSource をProductsBLL
構成します。 手順 2 および 3 で作成した前の Web ページと同様に、UPDATE、INSERT、DELETE タブのドロップダウン リストを (None) に設定します。
図 12: メソッドを使用 GetProductByProductID(productID)
するように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)
データ ソースの構成ウィザードの最後の手順では、 productID パラメーターのソースの入力を求められます。 このデータは querystring フィールド ProductID
を介して取得されるため、ドロップダウン リストを QueryString に、QueryStringField テキスト ボックスを ProductID に設定します。 最後に、[完了] ボタンをクリックしてウィザードを完了します。
図 13: Querystring フィールドから値をプルするように productID パラメーターを ProductID
構成する (フルサイズの画像を表示する をクリックします)
データ ソースの構成ウィザードを完了すると、Visual Studio によって、製品データ フィールドの DetailsView に対応する BoundFields と CheckBoxField が作成されます。 ProductID
、SupplierID
、および CategoryID
BoundFields を削除し、必要に応じて残りのフィールドを構成します。 いくつかの美的な構成の後、DetailsView と ObjectDataSource の宣言型マークアップは次のようになります。
<asp:DetailsView ID="ProductInfo" runat="server" AutoGenerateRows="False"
DataKeyNames="ProductID" DataSourceID="ProductDataSource"
EnableViewState="False">
<Fields>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice" />
<asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock"
SortExpression="UnitsInStock" />
<asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order"
SortExpression="UnitsOnOrder" />
<asp:BoundField DataField="ReorderLevel" HeaderText="Reorder Level"
SortExpression="ReorderLevel" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Fields>
</asp:DetailsView>
<asp:ObjectDataSource ID="ProductDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductByProductID" TypeName="ProductsBLL">
<SelectParameters>
<asp:QueryStringParameter Name="productID"
QueryStringField="ProductID" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
このページをテストするには、 に Default.aspx
戻り、[飲料] カテゴリの [製品の表示] をクリックします。 飲料製品の一覧から、チャイティーの詳細の表示リンクをクリックします。 これにより、チャイティーの詳細が表示されます ProductDetails.aspx?ProductID=1
(図 14 を参照)。
図 14: チャイ ティーのサプライヤー、カテゴリ、価格、およびその他の情報が表示されます (フルサイズの画像を表示する をクリックします)。
手順 5: サイト マップ プロバイダーの内部動作について
サイト マップは、階層を形成するインスタンスのコレクションとして Web サーバーの SiteMapNode
メモリ内で表されます。 ルートは 1 つだけ存在し、ルート以外のすべてのノードには 1 つの親ノードが必要であり、すべてのノードには任意の数の子が含まれる場合があります。 各 SiteMapNode
オブジェクトは、Web サイトの構造内のセクションを表します。これらのセクションには、一般的に対応する Web ページがあります。 そのため、 クラスには、 がSiteMapNode
表すセクションSiteMapNode
の情報を提供する、、Url
、 Description
などのTitle
プロパティがあります。 またKey
、階層内の各SiteMapNode
を一意に識別するプロパティと、この階層ChildNodes
の確立に使用されるプロパティ、、ParentNode
、NextSibling
PreviousSibling
、などもあります。
図 15 は、図 1 の一般的なサイト マップ構造を示していますが、実装の詳細は詳細にスケッチされています。
図 15: 各SiteMapNode
プロパティには、、Url
、Key
、 などのTitle
プロパティがあります (クリックするとフルサイズの画像が表示されます)
サイト マップには、 名前空間の System.Web
クラスをSiteMap
介してアクセスできます。 このクラス s プロパティはRootNode
、サイト マップのルート SiteMapNode
インスタンスを返しますCurrentNode
。プロパティがUrl
現在要求されているページの URL と一致する を返SiteMapNode
します。 このクラスは、ASP.NET 2.0 のナビゲーション Web コントロールによって内部的に使用されます。
クラスのプロパティに SiteMap
アクセスするときは、サイト マップ構造をいくつかの永続メディアからメモリにシリアル化する必要があります。 ただし、サイト マップのシリアル化ロジックは、 クラスに SiteMap
ハードコーディングされません。 代わりに、実行時にクラスによって SiteMap
、シリアル化に使用するサイト マップ プロバイダー が決定されます。 既定では、 クラスがXmlSiteMapProvider
使用され、適切な形式の XML ファイルからサイト マップの構造が読み取られます。 ただし、少しの作業で、独自のカスタム サイト マップ プロバイダーを作成できます。
すべてのサイト マップ プロバイダーは、 クラスからSiteMapProvider
派生する必要があります。これには、サイト マップ プロバイダーに必要な重要なメソッドとプロパティが含まれますが、実装の詳細の多くは省略されています。 2 番目のクラス である StaticSiteMapProvider
は、 クラスを SiteMapProvider
拡張し、必要な機能のより堅牢な実装を含みます。 内部的には、 StaticSiteMapProvider
は サイト マップのインスタンスを にHashtable
格納SiteMapNode
し、 などのAddNode(child, parent)
RemoveNode(siteMapNode),
メソッドを提供し、Clear()
内部 Hashtable
に s を追加および削除SiteMapNode
します。 XmlSiteMapProvider
は、StaticSiteMapProvider
から派生しています。
を拡張StaticSiteMapProvider
するカスタム サイト マップ プロバイダーを作成する場合は、 と GetRootNodeCore
の 2 つの抽象メソッドをオーバーライドBuildSiteMap
する必要があります。 BuildSiteMap
は、その名前が示すように、永続的ストレージからサイト マップ構造を読み込み、メモリ内に構築する役割を担います。 GetRootNodeCore
は、サイト マップ内のルート ノードを返します。
Web アプリケーションでサイト マップ プロバイダーを使用するには、その前にアプリケーションの構成に登録する必要があります。 既定では、 XmlSiteMapProvider
クラスは という名前 AspNetXmlSiteMapProvider
を使用して登録されます。 追加のサイト マップ プロバイダーを登録するには、次のマークアップを に Web.config
追加します。
<configuration>
<system.web>
...
<siteMap defaultProvider="defaultProviderName">
<providers>
<add name="name" type="type" />
</providers>
</siteMap>
</system.web>
</configuration>
name 値は人間が判読できる名前をプロバイダーに割り当てますが、type はサイト マップ プロバイダーの完全修飾型名を指定します。 カスタム サイト マップ プロバイダーを作成した後、手順 7 で 名前 と 型 の値の具体的な値について説明します。
サイト マップ プロバイダー クラスは、 クラスから SiteMap
初めてアクセスされるときにインスタンス化され、Web アプリケーションの有効期間にわたってメモリ内に残ります。 複数の同時 Web サイト訪問者から呼び出される可能性があるサイト マップ プロバイダーのインスタンスは 1 つだけであるため、プロバイダーのメソッドは スレッド セーフであることが不可欠です。
パフォーマンスとスケーラビリティの理由から、メモリ内サイト マップ構造をキャッシュし、メソッドが呼び出されるたびに再作成するのではなく、このキャッシュされた構造を BuildSiteMap
返す必要があります。 BuildSiteMap
ページで使用されているナビゲーション コントロールとサイト マップ構造の深さに応じて、ユーザーごとにページ要求ごとに複数回呼び出すことができます。 いずれの場合も、 で BuildSiteMap
サイト マップ構造をキャッシュしない場合は、呼び出されるたびに、アーキテクチャから製品とカテゴリの情報を再取得する必要があります (その結果、データベースに対するクエリが発生します)。 前のキャッシュチュートリアルで説明したように、キャッシュされたデータは古くなる可能性があります。 これに対処するために、時間または SQL キャッシュの依存関係ベースの有効期限を使用できます。
注意
サイト マップ プロバイダーは、必要に応じて メソッドをInitialize
オーバーライドできます。 Initialize
は、サイト マップ プロバイダーが最初にインスタンス化されたときに呼び出され、 要素内<add>
の Web.config
プロバイダーに割り当てられたカスタム属性が 渡されます。 <add name="name" type="type" customAttribute="value" />
これは、ページ開発者がプロバイダーのコードを変更することなく、さまざまなサイト マップ プロバイダー関連の設定を指定できるようにする場合に便利です。 たとえば、アーキテクチャではなく、カテゴリと製品のデータをデータベースから直接読み取る場合、ページ開発者がプロバイダーのコードでハードコーディングされた値を使用するのではなく、 を使用してWeb.config
データベース接続文字列を指定できるようにする必要があります。 手順 6 でビルドするカスタム サイト マップ プロバイダーは、この Initialize
メソッドをオーバーライドしません。 メソッドの使用Initialize
例については、「Jeff Prosise s Storing Site Maps in SQL Server article」を参照してください。
手順 6: カスタム サイト マップ プロバイダーの作成
Northwind データベースのカテゴリと製品からサイト マップを構築するカスタム サイト マップ プロバイダーを作成するには、 を拡張 StaticSiteMapProvider
するクラスを作成する必要があります。 手順 1 で、 フォルダーに フォルダーをCustomProviders
追加するように求めました。 という名前NorthwindSiteMapProvider
のApp_Code
このフォルダーに新しいクラスを追加します。 以下のコードを NorthwindSiteMapProvider
クラスに追加します。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Caching;
public class NorthwindSiteMapProvider : StaticSiteMapProvider
{
private readonly object siteMapLock = new object();
private SiteMapNode root = null;
public const string CacheDependencyKey =
"NorthwindSiteMapProviderCacheDependency";
public override SiteMapNode BuildSiteMap()
{
// Use a lock to make this method thread-safe
lock (siteMapLock)
{
// First, see if we already have constructed the
// rootNode. If so, return it...
if (root != null)
return root;
// We need to build the site map!
// Clear out the current site map structure
base.Clear();
// Get the categories and products information from the database
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Create the root SiteMapNode
root = new SiteMapNode(
this, "root", "~/SiteMapProvider/Default.aspx", "All Categories");
AddNode(root);
// Create SiteMapNodes for the categories and products
foreach (Northwind.ProductsRow product in products)
{
// Add a new category SiteMapNode, if needed
string categoryKey, categoryName;
bool createUrlForCategoryNode = true;
if (product.IsCategoryIDNull())
{
categoryKey = "Category:None";
categoryName = "None";
createUrlForCategoryNode = false;
}
else
{
categoryKey = string.Concat("Category:", product.CategoryID);
categoryName = product.CategoryName;
}
SiteMapNode categoryNode = FindSiteMapNodeFromKey(categoryKey);
// Add the category SiteMapNode if it does not exist
if (categoryNode == null)
{
string productsByCategoryUrl = string.Empty;
if (createUrlForCategoryNode)
productsByCategoryUrl =
"~/SiteMapProvider/ProductsByCategory.aspx?CategoryID="
+ product.CategoryID;
categoryNode = new SiteMapNode(
this, categoryKey, productsByCategoryUrl, categoryName);
AddNode(categoryNode, root);
}
// Add the product SiteMapNode
string productUrl =
"~/SiteMapProvider/ProductDetails.aspx?ProductID="
+ product.ProductID;
SiteMapNode productNode = new SiteMapNode(
this, string.Concat("Product:", product.ProductID),
productUrl, product.ProductName);
AddNode(productNode, categoryNode);
}
// Add a "dummy" item to the cache using a SqlCacheDependency
// on the Products and Categories tables
System.Web.Caching.SqlCacheDependency productsTableDependency =
new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products");
System.Web.Caching.SqlCacheDependency categoriesTableDependency =
new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories");
// Create an AggregateCacheDependency
System.Web.Caching.AggregateCacheDependency aggregateDependencies =
new System.Web.Caching.AggregateCacheDependency();
aggregateDependencies.Add(productsTableDependency, categoriesTableDependency);
// Add the item to the cache specifying a callback function
HttpRuntime.Cache.Insert(
CacheDependencyKey, DateTime.Now, aggregateDependencies,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal,
new CacheItemRemovedCallback(OnSiteMapChanged));
// Finally, return the root node
return root;
}
}
protected override SiteMapNode GetRootNodeCore()
{
return BuildSiteMap();
}
protected void OnSiteMapChanged(string key, object value, CacheItemRemovedReason reason)
{
lock (siteMapLock)
{
if (string.Compare(key, CacheDependencyKey) == 0)
{
// Refresh the site map
root = null;
}
}
}
public DateTime? CachedDate
{
get
{
return HttpRuntime.Cache[CacheDependencyKey] as DateTime?;
}
}
}
まず、 ステートメントで始まるこのクラスの メソッド BuildSiteMap
を lock
調べてみましょう。 ステートメントでは lock
、一度に 1 つのスレッドのみを入力できるため、コードへのアクセスをシリアル化し、2 つの同時実行スレッドが互いに足を踏み入れないようにします。
クラス レベル SiteMapNode
の変数 root
は、サイト マップ構造をキャッシュするために使用されます。 サイト マップが初めて構築されたとき、または基になるデータが変更された後に初めて作成される場合は、 が になりnull
、root
サイト マップ構造が構築されます。 サイト マップのルート ノードが構築プロセス中に に root
割り当てられるため、次にこのメソッドが呼び出されるときに、 root
は になります null
。 したがって、 が でないnull
限りroot
、サイト マップ構造は再作成しなくても呼び出し元に返されます。
root が の場合、 null
サイト マップ構造は製品とカテゴリの情報から作成されます。 サイト マップは、インスタンスをSiteMapNode
作成し、クラスの AddNode
メソッドの呼び出しを通じて階層をStaticSiteMapProvider
形成することによって構築されます。 AddNode
は内部簿記を実行し、アソートされた SiteMapNode
インスタンスを に Hashtable
格納します。 階層の構築を開始する前に、 メソッドを Clear
呼び出して開始します。これにより、内部 Hashtable
から要素が消去されます。 次に ProductsBLL
、クラスの GetProducts
メソッドと結果の ProductsDataTable
がローカル変数に格納されます。
サイト マップの構築は、ルート ノードを作成して に root
割り当てることから始まります。 この全体でBuildSiteMap
使用される SiteMapNode
s コンストラクターのオーバーロードには、次の情報が渡されます。
- サイト マップ プロバイダー (
this
) への参照。 SiteMapNode
sKey
。 この必須値は、 ごとにSiteMapNode
一意である必要があります。SiteMapNode
sUrl
。Url
は省略可能ですが、指定した場合は、それぞれのSiteMapNode
値がUrl
一意である必要があります。- が
SiteMapNode
Title
必要です。
メソッド呼び出しはAddNode(root)
SiteMapNode
root
、 をルートとしてサイト マップに追加します。 次に、 のProductsDataTable
各 ProductRow
が列挙されます。 現在の製品カテゴリの SiteMapNode
が既に存在する場合は、それが参照されます。 それ以外の場合は、カテゴリの新しい SiteMapNode
が作成され、 メソッド呼び出しを介して のSiteMapNode``root
AddNode(categoryNode, root)
子として追加されます。 適切なカテゴリ SiteMapNode
ノードが見つかったか作成されると、SiteMapNode
現在の製品の が作成され、 を介してAddNode(productNode, categoryNode)
カテゴリSiteMapNode
の子として追加されます。 製品のUrl
プロパティが 割り当てられている~/SiteMapNode/ProductDetails.aspx?ProductID=productID
間、SiteMapNode
カテゴリSiteMapNode
の プロパティ値Url
は ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
であることに注意してください。
注意
データベースNULL
値CategoryID
を持つ製品は、プロパティが None Url
に設定され、そのプロパティが空の文字列に設定されているカテゴリSiteMapNode
Title
の下にグループ化されます。 クラスのGetProductsByCategory(categoryID)
メソッドには現在、値を持つCategoryID
NULL
製品だけを返す機能がないため、空の文字列ProductBLL
に設定Url
することにしました。 また、ナビゲーション コントロールが プロパティの値Url
を欠く をSiteMapNode
レンダリングする方法を示したいと思いました。 このチュートリアルを拡張して、None SiteMapNode
プロパティが をProductsByCategory.aspx
指し示し、値を持つCategoryID
NULL
製品のみが表示されるようにUrl
することをお勧めします。
サイト マップを構築した後、オブジェクトを介して テーブルと Products
テーブルに対する SQL キャッシュ依存関係を使用して、任意のオブジェクトがデータ キャッシュにCategories
AggregateCacheDependency
追加されます。 前のチュートリアル「SQL キャッシュ依存関係の使用」で SQL キャッシュの依存関係の使用について説明しました。 ただし、カスタム サイト マップ プロバイダーでは、まだ調べていないデータ キャッシュの メソッドの Insert
オーバーロードが使用されます。 このオーバーロードは、オブジェクトがキャッシュから削除されたときに呼び出されるデリゲートを最終的な入力パラメーターとして受け取ります。 具体的には、 クラスでさらに下に定義されたメソッドをOnSiteMapChanged
指す新しいCacheItemRemovedCallback
デリゲートをNorthwindSiteMapProvider
渡します。
注意
サイト マップのメモリ内表現は、クラス レベルの変数 root
を介してキャッシュされます。 カスタム サイト マップ プロバイダー クラスのインスタンスは 1 つだけであり、そのインスタンスは Web アプリケーション内のすべてのスレッド間で共有されるため、このクラス変数はキャッシュとして機能します。 メソッドはBuildSiteMap
データ キャッシュも使用しますが、 または Products
テーブルの基になるデータベース データが変更されたときに通知をCategories
受け取る手段としてのみ使用されます。 データ キャッシュに格納される値は、現在の日付と時刻に過ぎないことに注意してください。 実際のサイト マップ データはデータ キャッシュに格納 されません 。
メソッドは BuildSiteMap
、サイト マップのルート ノードを返すことによって完了します。
残りの方法はかなり簡単です。 GetRootNodeCore
は、ルート ノードを返す役割を担います。 はルートを返すの BuildSiteMap
で、 GetRootNodeCore
単に s の戻り値を BuildSiteMap
返します。 キャッシュ項目がOnSiteMapChanged
削除されると、 メソッドは にnull
戻root
ります。 root を に null
戻すと、次回 BuildSiteMap
呼び出されると、サイト マップ構造が再構築されます。 最後に、 プロパティは CachedDate
、データ キャッシュに格納されている日付と時刻の値 (そのような値が存在する場合) を返します。 このプロパティは、ページ開発者がサイト マップ データが最後にキャッシュされた日時を判断するために使用できます。
手順 7: を登録するNorthwindSiteMapProvider
Web アプリケーションで手順 6 で作成したサイト マップ プロバイダーをNorthwindSiteMapProvider
使用するには、 のWeb.config
セクションに登録する<siteMap>
必要があります。 具体的には、 の 要素Web.config
内に次のマークアップを<system.web>
追加します。
<siteMap defaultProvider="AspNetXmlSiteMapProvider">
<providers>
<add name="Northwind" type="NorthwindSiteMapProvider" />
</providers>
</siteMap>
このマークアップは 2 つのことを行います。最初に、組み込みが既定の AspNetXmlSiteMapProvider
サイト マップ プロバイダーであることを示します。2 つ目は、手順 6 で作成したカスタム サイト マップ プロバイダーを、人間にわかりやすい名前 Northwind に登録します。
注意
アプリケーションのフォルダーにあるサイト マップ プロバイダーの App_Code
場合、 属性の type
値は単にクラス名です。 または、カスタム サイト マップ プロバイダーを別のクラス ライブラリ プロジェクトに作成し、コンパイルされたアセンブリを Web アプリケーションの /Bin
ディレクトリに配置することもできます。 その場合、 type
属性値は Namespace になります。ClassName、 AssemblyName 。
を更新した Web.config
後、少し時間を取って、ブラウザーでチュートリアルから任意のページを表示します。 左側のナビゲーション インターフェイスには、 で Web.sitemap
定義されているセクションとチュートリアルが引き続き表示されることに注意してください。 これは、既定のプロバイダーとして残 AspNetXmlSiteMapProvider
したためです。 を使用 NorthwindSiteMapProvider
するナビゲーション ユーザー インターフェイス要素を作成するには、Northwind サイト マップ プロバイダーを使用するように明示的に指定する必要があります。 これを行う方法については、手順 8 で説明します。
手順 8: カスタム サイト マップ プロバイダーを使用してサイト マップ情報を表示する
で作成および登録されたWeb.config
カスタム サイト マップ プロバイダーを使用して、フォルダー内の 、ProductsByCategory.aspx
、および ProductDetails.aspx
ページにDefault.aspx
ナビゲーション コントロールを追加する準備ができましたSiteMapProvider
。 まずページをDefault.aspx
開き、[ツールボックス] から Designerに をドラッグSiteMapPath
します。 SiteMapPath コントロールは、[ツールボックス] の [ナビゲーション] セクションにあります。
図 16: SiteMapPath を に追加する Default.aspx
(クリックするとフルサイズの画像が表示されます)
SiteMapPath コントロールは階層リンクを表示し、サイト マップ内の現在のページの位置を示します。 マスター ページとサイト ナビゲーションのチュートリアルで、マスター ページの上部に SiteMapPath を 追加しました。
ブラウザーからこのページを表示するには、少し時間を取ります。 図 16 で追加された SiteMapPath は、既定のサイト マップ プロバイダーを使用して、 から Web.sitemap
データをプルします。 そのため、階層リンクには、右上隅の階層リンクと同様に、サイト マップのホーム > カスタマイズが表示されます。
図 17: 階層リンクは既定のサイト マップ プロバイダーを使用します (クリックするとフルサイズの画像が表示されます)
図 16 で SiteMapPath を追加するには、手順 6 で作成したカスタム サイト マップ プロバイダーを使用し、そのSiteMapProvider
プロパティを Northwind に設定します。これは、 で Web.config
にNorthwindSiteMapProvider
割り当てた名前です。 残念ながら、Designerは引き続き既定のサイト マップ プロバイダーを使用しますが、このプロパティの変更を行った後にブラウザーからページにアクセスすると、階層リンクでカスタム サイト マップ プロバイダーが使用されるようになりました。
図 18: 階層リンクでカスタム サイト マップ プロバイダー NorthwindSiteMapProvider
が使用されるようになりました (クリックするとフルサイズの画像が表示されます)
SiteMapPath コントロールには、 ページと ProductDetails.aspx
ページに、より機能的なユーザー インターフェイスがProductsByCategory.aspx
表示されます。 これらのページに SiteMapPath を追加し、両方の プロパティを SiteMapProvider
Northwind に設定します。 [ Default.aspx
飲み物] の [製品の表示] リンクをクリックし、[Chai Tea] の [詳細の表示] リンクをクリックします。 図 19 に示すように、階層リンクには、現在のサイト マップ セクション ( Chai Tea ) とその先祖が含まれています。
図 19: 階層リンクでカスタム サイト マップ プロバイダー NorthwindSiteMapProvider
が使用されるようになりました (クリックするとフルサイズの画像が表示されます)
その他のナビゲーション ユーザー インターフェイス要素は、Menu コントロールや TreeView コントロールなど、SiteMapPath に加えて使用できます。 Default.aspx
たとえば、このチュートリアルのダウンロードの 、ProductsByCategory.aspx
、および ProductDetails.aspx
ページには、すべて Menu コントロールが含まれます (図 20 を参照)。 ASP.NET 2.0 のナビゲーション コントロールとサイト マップ システムの詳細については、「ASP.NET 2.0 の高度なサイト ナビゲーション機能」および「ASP.NET 2.0 クイック スタート」の「Using Site Navigation Controls」セクションを参照してください。
図 20: メニュー コントロールLists各カテゴリと製品 (フルサイズの画像を表示する をクリックします)
このチュートリアルで前述したように、 クラスを使用して SiteMap
、サイト マップ構造にプログラムでアクセスできます。 次のコードは、既定のプロバイダーのルート SiteMapNode
を返します。
SiteMapNode root = SiteMap.RootNode;
AspNetXmlSiteMapProvider
はアプリケーションの既定のプロバイダーであるため、上記のコードは でWeb.sitemap
定義されているルート ノードを返します。 既定以外のサイト マップ プロバイダーを参照するには、 クラスの Providers
プロパティをSiteMap
次のように使用します。
SiteMapNode root = SiteMap.Providers["name"].RootNode;
ここで 、name はカスタム サイト マップ プロバイダーの名前です (Web アプリケーションの場合は Northwind)。
サイト マップ プロバイダーに固有のメンバーにアクセスするには、 を使用 SiteMap.Providers["name"]
してプロバイダー インスタンスを取得し、それを適切な型にキャストします。 たとえば、ASP.NET ページに s CachedDate
プロパティを表示NorthwindSiteMapProvider
するには、次のコードを使用します。
NorthwindSiteMapProvider customProvider =
SiteMap.Providers["Northwind"] as NorthwindSiteMapProvider;
if (customProvider != null)
{
DateTime? lastCachedDate = customProvider.CachedDate;
if (lastCachedDate != null)
LabelID.Text = "Site map cached on: " + lastCachedDate.Value.ToString();
else
LabelID.Text = "The site map is being reconstructed!";
}
注意
SQL キャッシュの依存関係機能を必ずテストしてください。 、ProductsByCategory.aspx
、および ProductDetails.aspx
の各ページにアクセスDefault.aspx
したら、「編集」、「挿入」、「削除」セクションのいずれかのチュートリアルに移動し、カテゴリまたは製品の名前を編集します。 次に、 フォルダー内のいずれかのページに SiteMapProvider
戻ります。 ポーリング メカニズムが基になるデータベースへの変更をメモするのに十分な時間が経過したと仮定すると、サイト マップを更新して新しい製品またはカテゴリ名を表示する必要があります。
まとめ
ASP.NET 2.0 のサイト マップ機能には、クラス、多数の組み込みナビゲーション Web コントロール、および XML ファイルに保持されるサイト マップ情報を必要とする既定のサイト マップ プロバイダーが含まれます SiteMap
。 データベース、アプリケーションのアーキテクチャ、リモート Web サービスなどの他のソースからのサイト マップ情報を使用するには、カスタム サイト マップ プロバイダーを作成する必要があります。 これには、 クラスから直接または間接的に派生するクラスを作成する必要 SiteMapProvider
があります。
このチュートリアルでは、アプリケーション アーキテクチャから取得した製品とカテゴリ情報に基づいてサイト マップを基にしたカスタム サイト マップ プロバイダーを作成する方法について説明しました。 プロバイダーは クラスを StaticSiteMapProvider
拡張し、データを取得し BuildSiteMap
、サイト マップ階層を構築し、結果の構造をクラス レベルの変数にキャッシュするメソッドを作成する必要がありました。 基になる Categories
または Products
データが変更されたときにキャッシュされた構造を無効にするために、コールバック関数と共に SQL キャッシュ依存関係を使用しました。
幸せなプログラミング!
もっと読む
このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。
特別な感謝
このチュートリアル シリーズは、多くの役立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dave Gardner、Zack Jones、Teresa Murphy、Bernadette Leigh でした。 今後の MSDN 記事の確認に関心がありますか? その場合は、 に行mitchell@4GuysFromRolla.comをドロップしてください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示