カスタム データベース駆動型サイト マップ プロバイダーを構築する (VB)

作成者: Scott Mitchell

PDF のダウンロード

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 追加します。

サイト マップ Provider-Related チュートリアルの ASP.NET ページを追加する

図 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.aspxProductsByCategory.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) に設定します。

GetCategories メソッドを使用してカテゴリを返すように ObjectDataSource を構成する

図 4: メソッドを使用して GetCategories カテゴリを返すように ObjectDataSource を構成する (フルサイズの画像を表示するをクリックします)

[更新]、[挿入]、[削除] タブの Drop-Down Lists を [(なし)] に設定します

図 5: UPDATE、INSERT、DELETE タブの Drop-Down Lists を (なし) に設定します (フルサイズの画像を表示する場合はクリックします)

データ ソースの構成ウィザードが完了すると、Visual Studio によって、、NumberOfProductsCategoryNameDescriptionおよび BrochurePathCategoryIDBoundField が追加されます。 GridView を編集して、 と Description BoundFields のみが含まれるCategoryNameようにし、BoundField の HeaderText プロパティを CategoryName Category に更新します。

次に、HyperLinkField を追加し、左端のフィールドになるように配置します。 DataNavigateUrlFields プロパティを CategoryID に、DataNavigateUrlFormatString プロパティを ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0} にそれぞれ設定します。 プロパティを [製品の Text 表示] に設定します。

カテゴリ GridView に HyperLinkField を追加する

図 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) に設定します。

ProductsBLL クラスの GetProductsByCategoryID(categoryID) メソッドを使用する

図 8: クラス s GetProductsByCategoryID(categoryID) メソッドをProductsBLL使用する (フルサイズの画像を表示する場合にクリックします)

データ ソースの構成ウィザードの最後の手順では、 categoryID のパラメーター ソースの入力を求められます。 この情報は querystring フィールド CategoryIDを介して渡されるため、ドロップダウン リストから [QueryString] を選択し、図 9 に示すように [QueryStringField] テキスト ボックスに「CategoryID」と入力します。 [完了] をクリックしてウィザードを終了します。

categoryID パラメーターに CategoryID Querystring フィールドを使用する

図 9: categoryID パラメーターに CategoryID Querystring フィールドを使用する (フルサイズの画像を表示するには、ここをクリックします)

ウィザードが完了すると、Visual Studio は対応する BoundFields と CheckBoxField を製品データ フィールドの GridView に追加します。 、UnitPriceProductNameおよび SupplierName BoundFields 以外のすべてを削除します。 これら 3 つの BoundFields プロパティを HeaderText カスタマイズして、Product、Price、および Supplier をそれぞれ読み取ります。 BoundField を UnitPrice 通貨として書式設定します。

次に、HyperLinkField を追加し、左端の位置に移動します。 プロパティを Text [詳細の表示] に設定し、その DataNavigateUrlFields プロパティを に ProductID設定し、その DataNavigateUrlFormatString プロパティを に ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}設定します。

ProductDetails.aspxを指すビューの詳細 HyperLinkField を追加する

図 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) に設定します。

GetProductByProductID(productID) メソッドを使用するように ObjectDataSource を構成する

図 12: メソッドを使用 GetProductByProductID(productID) するように ObjectDataSource を構成する (フルサイズの画像を表示するをクリックします)

データ ソースの構成ウィザードの最後の手順では、 productID パラメーターのソースの入力を求められます。 このデータは querystring フィールド ProductIDを介して取得されるため、ドロップダウン リストを QueryString に、QueryStringField テキスト ボックスを ProductID に設定します。 最後に、[完了] ボタンをクリックしてウィザードを完了します。

ProductID クエリ文字列フィールドから値をプルするように productID パラメーターを構成する

図 13: Querystring フィールドから値をプルするように productID パラメーターを ProductID 構成する (フルサイズの画像を表示する をクリックします)

データ ソースの構成ウィザードが完了すると、Visual Studio によって、製品データ フィールドの DetailsView に対応する BoundFields と CheckBoxField が作成されます。 、SupplierIDProductIDおよび 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: Chai Tea のサプライヤー、カテゴリ、価格、およびその他の情報が表示されている (フルサイズの画像を表示する をクリックします)

手順 5: サイト マップ プロバイダーの内部動作について

サイト マップは、階層を形成するインスタンスのコレクションとして Web サーバーの SiteMapNode メモリで表されます。 ルートは 1 つだけ存在する必要があり、ルート以外のすべてのノードには 1 つの親ノードが必要であり、すべてのノードには任意の数の子が含まれる場合があります。 各 SiteMapNode オブジェクトは、Web サイトの構造内のセクションを表します。これらのセクションには、一般的に対応する Web ページがあります。 そのため、 クラスには、 SiteMapNode が表すセクションSiteMapNodeの情報を提供する、、UrlDescriptionなどのTitleプロパティがあります。 またKey、階層内の各SiteMapNodeを一意に識別するプロパティと、この階層ChildNodesの確立に使用されるプロパティ 、、ParentNodeNextSiblingPreviousSibling、などもあります。

図 15 は図 1 の一般的なサイト マップ構造を示していますが、実装の詳細は詳細にスケッチされています。

各 SiteMapNode には、タイトル、URL、キーなどのプロパティがあります

図 15: 各SiteMapNodeプロパティには、、UrlKey、 などのTitleプロパティがあります (フルサイズの画像を表示する場合はクリックします)

サイト マップには、 名前空間の System.WebクラスをSiteMap介してアクセスできます。 このクラス s RootNode プロパティは、サイト マップのルート SiteMapNode インスタンスを返しますCurrentNode。プロパティがUrl現在要求されているページの URL と一致する を返SiteMapNodeします。 このクラスは、ASP.NET 2.0 s ナビゲーション 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 は、サイト マップ プロバイダーが最初にインスタンス化されたときに呼び出され、 要素内の Web.config<add> プロバイダーに割り当てられたカスタム属性が 次 <add name="name" type="type" customAttribute="value" />のように渡されます。 これは、ページ開発者がプロバイダーのコードを変更することなく、さまざまなサイト マップ プロバイダー関連の設定を指定できるようにする場合に便利です。 たとえば、アーキテクチャではなく、カテゴリと製品のデータをデータベースから直接読み取る場合は、プロバイダーのコードでハードコーディングされた値を使用するのではなく、ページ開発者がデータベースの接続文字列Web.configを指定できるようにする必要があります。 手順 6 でビルドするカスタム サイト マップ プロバイダーでは、この Initialize メソッドはオーバーライドされません。 メソッドの使用Initialize例については、Jeff Prosiseサイト マップの格納に関する記事SQL Server参照してください。

手順 6: カスタム サイト マップ プロバイダーの作成

Northwind データベースのカテゴリと製品からサイト マップを構築するカスタム サイト マップ プロバイダーを作成するには、 を拡張 StaticSiteMapProviderするクラスを作成する必要があります。 手順 1 で、 フォルダーにフォルダーをCustomProviders追加するように求めました。 という名前NorthwindSiteMapProviderApp_Code新しいクラスをこのフォルダーに追加します。 以下のコードを NorthwindSiteMapProvider クラスに追加します。

Imports System.Web
Imports System.Web.Caching
Public Class NorthwindSiteMapProvider
    Inherits StaticSiteMapProvider
    Private ReadOnly siteMapLock As New Object()
    Private root As SiteMapNode = Nothing
    Public Const CacheDependencyKey As String = "NorthwindSiteMapProviderCacheDependency"
    Public Overrides Function BuildSiteMap() As System.Web.SiteMapNode
        ' Use a lock to make this method thread-safe
        SyncLock siteMapLock
            ' First, see if we already have constructed the
            ' rootNode. If so, return it...
            If root IsNot Nothing Then
                Return root
            End If
            ' We need to build the site map!
            ' Clear out the current site map structure
            MyBase.Clear()
            ' Get the categories and products information from the database
            Dim productsAPI As New ProductsBLL()
            Dim products As Northwind.ProductsDataTable = productsAPI.GetProducts()
            ' Create the root SiteMapNode
            root = New SiteMapNode( _
                Me, "root", "~/SiteMapProvider/Default.aspx", "All Categories")
            AddNode(root)
            ' Create SiteMapNodes for the categories and products
            For Each product As Northwind.ProductsRow In products
                ' Add a new category SiteMapNode, if needed
                Dim categoryKey, categoryName As String
                Dim createUrlForCategoryNode As Boolean = True
                If product.IsCategoryIDNull() Then
                    categoryKey = "Category:None"
                    categoryName = "None"
                    createUrlForCategoryNode = False
                Else
                    categoryKey = String.Concat("Category:", product.CategoryID)
                    categoryName = product.CategoryName
                End If
                Dim categoryNode As SiteMapNode = FindSiteMapNodeFromKey(categoryKey)
                ' Add the category SiteMapNode if it does not exist
                If categoryNode Is Nothing Then
                    Dim productsByCategoryUrl As String = String.Empty
                    If createUrlForCategoryNode Then
                        productsByCategoryUrl = _
                            "~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=" & _
                            product.CategoryID
                    End If
                    categoryNode = New SiteMapNode _
                        (Me, categoryKey, productsByCategoryUrl, categoryName)
                    AddNode(categoryNode, root)
                End If
                ' Add the product SiteMapNode
                Dim productUrl As String = _
                    "~/SiteMapProvider/ProductDetails.aspx?ProductID=" & _
                    product.ProductID
                Dim productNode As New SiteMapNode _
                    (Me, String.Concat("Product:", product.ProductID), _
                    productUrl, product.ProductName)
                AddNode(productNode, categoryNode)
            Next
            ' Add a "dummy" item to the cache using a SqlCacheDependency
            ' on the Products and Categories tables
            Dim productsTableDependency As New _
                System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products")
            Dim categoriesTableDependency As New _
                System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories")
            ' Create an AggregateCacheDependency
            Dim aggregateDependencies As 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, AddressOf OnSiteMapChanged)
            ' Finally, return the root node
            Return root
        End SyncLock
    End Function
    Protected Overrides Function GetRootNodeCore() As System.Web.SiteMapNode
        Return BuildSiteMap()
    End Function
    Protected Sub OnSiteMapChanged _
    (key As String, value As Object, reason As CacheItemRemovedReason)
        SyncLock siteMapLock
            If String.Compare(key, CacheDependencyKey) = 0 Then
                ' Refresh the site map
                root = Nothing
            End If
        End SyncLock
    End Sub
    Public ReadOnly Property CachedDate() As Nullable(Of DateTime)
        Get
            Dim value As Object = HttpRuntime.Cache(CacheDependencyKey)
            If value Is Nothing OrElse Not TypeOf value Is Nullable(Of DateTime) Then
                Return Nothing
            Else
                Return CType(value, Nullable(Of DateTime))
            End If
        End Get
    End Property
End Class

まず、 ステートメントで始まるこのクラスの メソッド BuildSiteMaplock 調べてみましょう。 ステートメントでは lock 、一度に 1 つのスレッドのみを入力できるため、そのコードへのアクセスをシリアル化し、2 つの同時実行スレッドが互いに足を踏み入れないようにします。

クラス レベル SiteMapNode の変数 root は、サイト マップ構造をキャッシュするために使用されます。 サイト マップが初めて構築されたとき、または基になるデータが変更された後で初めて構築される場合は、 が になりNothingrootサイト マップ構造が構築されます。 サイト マップのルート ノードが構築プロセス中に に root 割り当てられるため、 root 次にこのメソッドを呼び出す際に、 は になります Nothing。 したがって、 がでないNothing限りroot、サイト マップ構造は再作成しなくても呼び出し元に返されます。

root が の場合、 Nothingサイト マップ構造は製品とカテゴリの情報から作成されます。 サイト マップは、インスタンスをSiteMapNode作成し、クラスの AddNode メソッドの呼び出しを通じて階層をStaticSiteMapProvider形成することによって構築されます。 AddNode は内部簿記を実行し、並べ替えた SiteMapNode インスタンスを に Hashtable格納します。 階層の構築を開始する前に、 メソッドを Clear 呼び出して、内部 Hashtableから要素をクリアします。 次に ProductsBLL 、 クラスの GetProducts メソッドと結果の ProductsDataTable がローカル変数に格納されます。

サイト マップの構築は、ルート ノードを作成して に root割り当てることから始まります。 ここでおよびこれをBuildSiteMap通して使用される s コンストラクターのSiteMapNodeオーバーロードには、次の情報が渡されます。

  • サイト マップ プロバイダーへの参照 (Me)。
  • s SiteMapNodeKey。 この必須値は、 ごとに SiteMapNode一意である必要があります。
  • s SiteMapNodeUrlUrl は省略可能ですが、指定された場合は、各 SiteMapNode s Url 値が一意である必要があります。
  • SiteMapNodeTitle必要です。

メソッド呼び出しはAddNode(root)SiteMapNoderoot、 をルートとしてサイト マップに追加します。 次に、 のProductsDataTableProductRow が列挙されます。 現在の製品カテゴリの SiteMapNode が既に存在する場合は、参照されます。 それ以外の場合は、カテゴリの新しい SiteMapNode が作成され、メソッド呼び出しを介して AddNode(categoryNode, root)SiteMapNode``root子として追加されます。 適切なカテゴリ SiteMapNode ノードが見つかったか作成されると、SiteMapNode現在の製品に対して が作成され、 を介してAddNode(productNode, categoryNode)カテゴリSiteMapNodeの子として追加されます。 製品の Url プロパティが割り当てられている~/SiteMapNode/ProductDetails.aspx?ProductID=productID間、SiteMapNodeカテゴリSiteMapNodeの プロパティ値Url~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID であることに注意してください。

注意

データベースNULLCategoryIDを持つ製品は、プロパティが None Url に設定され、プロパティが空の文字列に設定されているカテゴリSiteMapNodeTitleにグループ化されます。 クラスのGetProductsByCategory(categoryID)メソッドには現在、値を持つCategoryIDNULL製品だけを返す機能がないためProductBLL、空の文字列に設定Urlすることにしました。 また、ナビゲーションコントロールがプロパティの値Urlを欠く をSiteMapNodeレンダリングする方法を示したいと思いました。 このチュートリアルを拡張して、None SiteMapNode s Url プロパティが を ProductsByCategory.aspx指し示すのに、値を持つ NULLCategoryID 製品のみが表示されるようにすることをお勧めします。

サイト マップを構築した後、オブジェクトを介して テーブルと Products テーブルに対する SQL キャッシュ依存関係を使用して、任意のオブジェクトがAggregateCacheDependencyCategoriesデータ キャッシュに追加されます。 前のチュートリアル「SQL キャッシュ依存関係の使用」で、 SQL キャッシュの依存関係の使用について説明しました。 ただし、カスタム サイト マップ プロバイダーでは、まだ調べていないデータ キャッシュの メソッドの Insert オーバーロードが使用されます。 このオーバーロードは、オブジェクトがキャッシュから削除されたときに呼び出されるデリゲートを、最終的な入力パラメーターとして受け取ります。 具体的には、 クラスでさらに下に定義されたメソッドをOnSiteMapChanged指す新しいCacheItemRemovedCallbackデリゲートNorthwindSiteMapProvider渡します。

注意

サイト マップのメモリ内表現は、クラス レベルの変数 rootを介してキャッシュされます。 カスタム サイト マップ プロバイダー クラスのインスタンスは 1 つだけであり、そのインスタンスは Web アプリケーション内のすべてのスレッド間で共有されるため、このクラス変数はキャッシュとして機能します。 メソッドではBuildSiteMapデータ キャッシュも使用されますが、 または Products テーブルの基になるデータベース データが変更されたときに通知をCategories受け取る手段としてのみ使用されます。 データ キャッシュに格納される値は、現在の日付と時刻にすぎません。 実際のサイト マップ データはデータ キャッシュに格納 されません

メソッドは BuildSiteMap 、サイト マップのルート ノードを返すことによって完了します。

残りのメソッドは非常に簡単です。 GetRootNodeCore は、ルート ノードを返す役割を担います。 はルートを返す BuildSiteMap ので、 GetRootNodeCore 単に 戻り値を返します BuildSiteMap 。 メソッドはOnSiteMapChanged、キャッシュ項目が削除されたときに にNothingrootります。 ルートを に Nothing戻すと、次回 BuildSiteMap 呼び出されると、サイト マップ構造が再構築されます。 最後に、 プロパティは CachedDate 、データ キャッシュに格納されている日付と時刻の値 (そのような値が存在する場合) を返します。 このプロパティは、ページ開発者がサイト マップ データが最後にキャッシュされた日時を判断するために使用できます。

手順 7: を登録するNorthwindSiteMapProvider

手順 6 で作成したサイト マップ プロバイダーを NorthwindSiteMapProvider Web アプリケーションで使用するには、 の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 になります。ClassNameAssemblyName

を更新した Web.config後、少し時間を取って、ブラウザーでチュートリアルから任意のページを表示します。 左側のナビゲーション インターフェイスには、 で Web.sitemap定義されているセクションとチュートリアルが引き続き表示されることに注意してください。 これは、既定のプロバイダーとして残 AspNetXmlSiteMapProvider したためです。 を使用 NorthwindSiteMapProviderするナビゲーション ユーザー インターフェイス要素を作成するには、Northwind サイト マップ プロバイダーを使用するように明示的に指定する必要があります。 これを行う方法については、手順 8 で説明します。

手順 8: カスタム サイト マップ プロバイダーを使用してサイト マップ情報を表示する

カスタム サイト マップ プロバイダーを作成して に登録すると、フォルダー内Web.configの 、ProductsByCategory.aspx、および ProductDetails.aspx ページにDefault.aspxナビゲーション コントロールを追加する準備がSiteMapProvider整いました。 最初にページをDefault.aspx開き、ツールボックスから Designerに をドラッグSiteMapPathします。 SiteMapPath コントロールは、ツールボックスの [ナビゲーション] セクションにあります。

SiteMapPath をDefault.aspxに追加する

図 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 ) とその先祖が含まれています:飲料とすべてのカテゴリ。

階層リンクが現在のサイト マップ セクション (Chai Tea) とその先祖 (飲料とすべてのカテゴリ) を表示する方法を示すスクリーンショット。

図 19: 階層リンクでカスタム サイト マップ プロバイダー NorthwindSiteMapProvider が使用されるようになりました (クリックするとフルサイズの画像が表示されます)

他のナビゲーション ユーザー インターフェイス要素は、Menu コントロールや TreeView コントロールなど、SiteMapPath に加えて使用できます。 このチュートリアルのダウンロードの 、ProductsByCategory.aspx、および ProductDetails.aspx ページにはDefault.aspx、たとえば、すべてのメニュー コントロールが含まれます (図 20 を参照)。 ASP.NET 2.0 のナビゲーション コントロールとサイト マップ システムの詳細については、「ASP.NET 2.0 の高度なサイト ナビゲーション機能」および「ASP.NET 2.0 クイック スタート」の「サイト ナビゲーション コントロールの使用」セクションを参照してください。

各カテゴリと製品Listsメニュー コントロール

図 20: メニュー コントロール Lists各カテゴリと製品 (フルサイズの画像を表示する をクリックします)

このチュートリアルで既に説明したように、 クラスを介して SiteMap プログラムでサイト マップ構造にアクセスできます。 次のコードは、既定のプロバイダーのルート SiteMapNode を返します。

Dim root As SiteMapNode = SiteMap.RootNode

AspNetXmlSiteMapProviderはアプリケーションの既定のプロバイダーであるため、上記のコードでは、 でWeb.sitemap定義されているルート ノードが返されます。 既定以外のサイト マップ プロバイダーを参照するには、 クラスの Providers プロパティSiteMap次のように使用します。

Dim root As SiteMapNode = SiteMap.Providers("name").RootNode

name はカスタム サイト マップ プロバイダーの名前です (Web アプリケーションの場合は Northwind)。

サイト マップ プロバイダーに固有のメンバーにアクセスするには、 を使用 SiteMap.Providers["name"] してプロバイダー インスタンスを取得し、それを適切な型にキャストします。 たとえば、ASP.NET ページに s CachedDate プロパティを表示NorthwindSiteMapProviderするには、次のコードを使用します。

Dim customProvider As NorthwindSiteMapProvider = _
    TryCast(SiteMap.Providers("Northwind"), NorthwindSiteMapProvider)
If customProvider IsNot Nothing Then
    Dim lastCachedDate As Nullable(Of DateTime) = customProvider.CachedDate
    If lastCachedDate.HasValue Then
        SiteMapLastCachedDate.Text = _
            "Site map cached on: " & lastCachedDate.Value.ToString()
    Else
        SiteMapLastCachedDate.Text = "The site map is being reconstructed!"
    End If
End If

注意

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 は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dave Gardner、Zack Jones、薗薇薇マーフィー、ベルナデット リーでした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。