建置自訂的資料庫驅動網站導覽提供者 (C#)Building a Custom Database-Driven Site Map Provider (C#)

Scott Mitchellby Scott Mitchell

下載程式代碼下載 PDFDownload Code or Download PDF

ASP.NET 2.0 中的預設網站地圖提供者會從靜態 XML 檔案抓取其資料。The default site map provider in ASP.NET 2.0 retrieves its data from a static XML file. 雖然以 XML 為基礎的提供者適用于許多中小型網站,但較大型的 Web 應用程式需要更動態的網站地圖。While the XML-based provider is suitable to many small and medium-sized Web sites, larger Web applications require a more dynamic site map. 在本教學課程中,我們將建立自訂網站地圖提供者,它會從商務邏輯層抓取其資料,然後從資料庫中抓取資料。In this tutorial we'll build a custom site map provider that retrieves its data from the Business Logic Layer, which in turn retrieves data from the database.

簡介Introduction

ASP.NET 2.0 s 網站地圖功能可讓網頁開發人員在某些持續性的媒體(例如 XML 檔案)中定義 web 應用程式的網站地圖。ASP.NET 2.0 s site map feature enables a page developer to define a web application s site map in some persistent medium, such as in an XML file. 定義好之後,可以透過System.Web 命名空間中的SiteMap 類別,或透過各種導覽 Web 控制項(例如 SiteMapPath、Menu 和 TreeView 控制項),以程式設計方式存取網站地圖資料。Once defined, the site map data can be accessed programmatically through the SiteMap class in the System.Web namespace or through a variety of navigation Web controls, such as the SiteMapPath, Menu, and TreeView controls. 網站地圖系統會使用提供者模型,因此可以建立不同的網站地圖序列化,並將其插入 web 應用程式中。The site map system uses the provider model so that different site map serialization implementations can be created and plugged into a web application. ASP.NET 2.0 隨附的預設網站地圖提供者會將網站地圖結構保存在 XML 檔案中。The default site map provider that ships with ASP.NET 2.0 persists site map structure in an XML file. 回到主版頁面和網站導覽教學課程中,我們建立了一個名為 Web.sitemap 的檔案,其中包含此結構,並已使用每個新的教學課程區段更新其 XML。Back in the Master Pages and Site Navigation tutorial we created a file named Web.sitemap that contained this structure and have been updating its XML with each new tutorial section.

如果網站地圖 s 結構相當靜態,例如這些教學課程,則預設的 XML 網站地圖提供者運作良好。The default XML-based site map provider works well if the site map s structure is fairly static, such as for these tutorials. 不過,在許多情況下,都需要更動態的網站地圖。In many scenarios, however, a more dynamic site map is needed. 請考慮 [圖 1] 所示的網站地圖,其中每個類別和產品都會顯示為網站 s 結構中的區段。Consider the site map shown in Figure 1, where each category and product appear as sections in the website s structure. 使用此網站地圖時,流覽對應到根節點的網頁可能會列出所有類別,而流覽特定的類別目錄則會列出該類別目錄的產品,而查看特定的產品網頁會顯示該產品的詳細資料。With this site map, visiting the web page corresponding to the root node might list all of the categories, whereas visiting a particular category s web page would list that category s products and viewing a particular product s web page would show that product s details.

分類和產品構成網站地圖 s 結構The Categories and Products Makeup the Site Map s Structure

圖 1:分類和產品構成網站地圖 s 結構(按一下以觀看完整大小的影像Figure 1: The Categories and Products Makeup the Site Map s Structure (Click to view full-size image)

雖然這個類別目錄和產品架構的結構可以硬式編碼到 Web.sitemap 檔案中,但每次加入、移除或重新命名類別或產品時,都必須更新檔案。While this category- and product-based structure could be hard-coded into the Web.sitemap file, the file would need to be updated each time a category or product was added, removed, or renamed. 因此,如果從資料庫取出結構,或在理想情況下,從應用程式架構的商務邏輯層來看,網站地圖維護會大幅簡化。Consequently, the site map maintenance would be greatly simplified if its structure was retrieved from the database or, ideally, from the Business Logic Layer of the application s architecture. 如此一來,當加入、重新命名或刪除產品和類別時,網站地圖會自動更新以反映這些變更。That way, as products and categories were added, renamed, or deleted, the site map would automatically update to reflect these changes.

由於 ASP.NET 2.0 s 網站地圖序列化是以提供者模型為基礎所建立,因此我們可以建立自己的自訂網站地圖提供者,從其他資料存放區(例如資料庫或架構)抓取其資料。Since ASP.NET 2.0 s site map serialization is built atop the provider model, we can create our own custom site map provider that grabs its data from an alternate data store, such as the database or architecture. 在本教學課程中,我們將建立自訂提供者,以從 BLL 抓取其資料。In this tutorial we'll build a custom provider that retrieves its data from the BLL. 讓我們開始吧!Let s get started!

Note

本教學課程中建立的自訂網站地圖提供者,與應用程式的架構和資料模型緊密結合。The custom site map provider created in this tutorial is tightly coupled to the application s architecture and data model. Jeff Prosise 會將網站地圖儲存在 SQL Server 中,而您之前正在等待文章的 SQL 網站地圖提供者,會檢查在 SQL Server 中儲存網站地圖資料的一般化方法。Jeff Prosise s Storing Site Maps in SQL Server and The SQL Site Map Provider You ve Been Waiting For articles examine a generalized approach to storing site map data in SQL Server.

步驟1:建立自訂網站地圖提供者網頁Step 1: Creating the Custom Site Map Provider Web Pages

開始建立自訂網站地圖提供者之前,請先新增本教學課程所需的 ASP.NET 網頁。Before we start creating a custom site map provider, let s first add the ASP.NET pages we'll need for this tutorial. 從新增名為 SiteMapProvider的資料夾開始。Start by adding a new folder named SiteMapProvider. 接下來,將下列 ASP.NET 網頁新增至該資料夾,並確定每個頁面都與 Site.master 主版頁面相關聯:Next, add the following ASP.NET pages to that folder, making sure to associate each page with the Site.master master page:

  • Default.aspx
  • ProductsByCategory.aspx
  • ProductDetails.aspx

此外,也請將 CustomProviders 子資料夾新增至 App_Code 資料夾。Also add a CustomProviders subfolder to the App_Code folder.

新增網站地圖提供者相關教學課程的 ASP.NET 網頁

圖 2:新增網站地圖提供者相關教學課程的 ASP.NET 網頁Figure 2: Add the ASP.NET Pages for the Site Map Provider-Related Tutorials

因為這一節只有一個教學課程,所以我們不需要 Default.aspx 來列出章節的教學課程。Since there is only one tutorial for this section, we don t need Default.aspx to list the section s tutorials. 相反地,Default.aspx 會在 GridView 控制項中顯示類別目錄。Instead, Default.aspx will display the categories in a GridView control. 我們會在步驟2中解決此情況。We'll tackle this in Step 2.

接下來,更新 Web.sitemap 以包含 Default.aspx 頁面的參考。Next, update Web.sitemap to include a reference to the Default.aspx page. 具體而言,請在快取 <siteMapNode>之後加入下列標記:Specifically, add the following markup after the Caching <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之後,請花一點時間透過瀏覽器來觀看教學課程網站。After updating Web.sitemap, take a moment to view the tutorials website through a browser. 左側功能表現在包含唯一網站地圖提供者教學課程的專案。The menu on the left now includes an item for the sole site map provider tutorial.

網站地圖現在包含網站地圖提供者教學課程的專案

圖 3:網站地圖現在包含網站地圖提供者教學課程的專案Figure 3: The Site Map Now Includes an Entry for the Site Map Provider Tutorial

本教學課程的主要重點在於說明如何建立自訂網站地圖提供者,以及如何將 web 應用程式設定為使用該提供者。This tutorial s main focus is to illustrate creating a custom site map provider and configuring a web application to use that provider. 特別是,我們將建立一個提供者,它會傳回網站地圖,其中包含根節點以及每個類別目錄和產品的節點,如 [圖 1] 所示。In particular, we'll build a provider that returns a site map that includes a root node along with a node for each category and product, as depicted in Figure 1. 一般來說,網站地圖中的每個節點都可以指定 URL。In general, each node in the site map may specify a URL. 針對我們的網站地圖,根節點的 URL 將會是 ~/SiteMapProvider/Default.aspx,這會列出資料庫中的所有類別。For our site map, the root node s URL will be ~/SiteMapProvider/Default.aspx, which will list all of the categories in the database. 網站地圖中的每個類別目錄節點都會有指向 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID的 URL,這會列出指定之類別目錄中的所有產品。Each category node in the site map will have a URL that points to ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID, which will list all of the products in the specified categoryID. 最後,每個 [product 網站地圖] 節點都會指向 [~/SiteMapProvider/ProductDetails.aspx?ProductID=productID],這會顯示特定的產品詳細資料。Finally, each product site map node will point to ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID, which will display the specific product s details.

若要開始,我們必須建立 Default.aspxProductsByCategory.aspxProductDetails.aspx 頁面。To start we need to create the Default.aspx, ProductsByCategory.aspx, and ProductDetails.aspx pages. 這些頁面會分別在步驟2、3和4中完成。These pages are completed in Steps 2, 3, and 4, respectively. 由於本教學課程的天生是在網站地圖提供者上,而且自從過去的教學課程已涵蓋如何建立這類多頁主要/詳細資料包表,因此我們將會在步驟2到4中進行。Since the thrust of this tutorial is on site map providers, and since past tutorials have covered creating these sorts of multi-page master/detail reports, we will hurry through Steps 2 through 4. 如果您需要重新整理程式來建立跨越多個頁面的主要/詳細資料包表,請參閱跨兩個頁面的主要/詳細資料篩選教學課程。If you need a refresher on creating master/detail reports that span multiple pages, refer back to the Master/Detail Filtering Across Two Pages tutorial.

步驟2:顯示類別清單Step 2: Displaying a List of Categories

開啟 [SiteMapProvider] 資料夾中的 [Default.aspx] 頁面,並將 GridView 從 [工具箱] 拖曳至設計工具,將其 ID 設定為 [Categories]。Open the Default.aspx page in the SiteMapProvider folder and drag a GridView from the Toolbox onto the Designer, setting its ID to Categories. 從 GridView 的智慧標籤,將它系結至名為 CategoriesDataSource 的新 ObjectDataSource 並加以設定,讓它使用 CategoriesBLL 類別 s GetCategories 方法來抓取其資料。From the GridView s smart tag, bind it to a new ObjectDataSource named CategoriesDataSource and configure it so that it retrieves its data using the CategoriesBLL class s GetCategories method. 由於此 GridView 只會顯示類別,而不提供資料修改功能,因此,請將 [更新]、[插入] 和 [刪除] 索引標籤中的下拉式清單設定為 [(無)]。Since this GridView just displays the categories and does not provide data modification capabilities, set the drop-down lists in the UPDATE, INSERT, and DELETE tabs to (None) .

使用 GetCategories 方法設定 ObjectDataSource 以傳回分類Configure the ObjectDataSource to Return Categories Using the GetCategories Method

圖 4:設定 ObjectDataSource 以使用 GetCategories 方法傳回分類(按一下以查看完整大小的影像Figure 4: Configure the ObjectDataSource to Return Categories Using the GetCategories Method (Click to view full-size image)

將 [更新]、[插入] 和 [刪除] 索引標籤中的下拉式清單設定為 [(無)]Set the Drop-Down Lists in the UPDATE, INSERT, and DELETE Tabs to (None)

圖 5:將 [更新]、[插入] 和 [刪除] 索引標籤中的下拉式清單設定為 [(無)] (按一下以查看完整大小的影像Figure 5: Set the Drop-Down Lists in the UPDATE, INSERT, and DELETE Tabs to (None) (Click to view full-size image)

完成 [設定資料來源] wizard 之後,Visual Studio 將會加入 CategoryIDCategoryNameDescriptionNumberOfProductsBrochurePath的 BoundField。After completing the Configure Data Source wizard, Visual Studio will add a BoundField for CategoryID, CategoryName, Description, NumberOfProducts, and BrochurePath. 編輯 GridView,使其只包含 CategoryNameDescription BoundFields,並將 CategoryName BoundField s HeaderText 屬性更新為 [類別]。Edit the GridView so that it only contains the CategoryName and Description BoundFields and update the CategoryName BoundField s HeaderText property to Category .

接下來,新增 HyperLinkField,並將它放在最左邊的欄位。Next, add a HyperLinkField and position it so that it s the left-most field. 將 [DataNavigateUrlFields] 屬性設定為 [CategoryID],並將 DataNavigateUrlFormatString 屬性設為 [~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}]。Set the DataNavigateUrlFields property to CategoryID and the DataNavigateUrlFormatString property to ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}. 將 [Text] 屬性設定為 [查看產品]。Set the Text property to View Products .

將 HyperLinkField 新增至類別 GridView

圖 6:將 HyperLinkField 新增至 Categories GridViewFigure 6: Add a HyperLinkField to the Categories GridView

建立 ObjectDataSource 並自訂 GridView 欄位之後,這兩個控制項的宣告式標記如下所示:After creating the ObjectDataSource and customizing the GridView s fields, the two controls declarative markup will look like the following:

<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.aspxFigure 7 shows Default.aspx when viewed through a browser. 按一下 [類別目錄] [產品] 連結會帶您前往 ProductsByCategory.aspx?CategoryID=categoryID,我們將在步驟3中建立。Clicking a category s View Products link takes you to ProductsByCategory.aspx?CategoryID=categoryID, which we will build in Step 3.

每個類別都會與 [View Products] 連結一起列出Each Category is Listed Along with a View Products Link

圖 7:每個類別都會與 [view Products] 連結一併列出(按一下以觀看完整大小的影像Figure 7: Each Category is Listed Along with a View Products Link (Click to view full-size image)

步驟3:列出選取的類別目錄 s 產品Step 3: Listing the Selected Category s Products

開啟 [ProductsByCategory.aspx] 頁面,並加入 GridView,將其命名為 ProductsByCategoryOpen the ProductsByCategory.aspx page and add a GridView, naming it ProductsByCategory. 從其智慧標籤,將 GridView 系結至名為 ProductsByCategoryDataSource的新 ObjectDataSource。From its smart tag, bind the GridView to a new ObjectDataSource named ProductsByCategoryDataSource. 將 ObjectDataSource 設定為使用 ProductsBLL 類別的 GetProductsByCategoryID(categoryID) 方法,並將 [更新]、[插入] 和 [刪除] 索引標籤中的下拉式清單設為(無)。Configure the ObjectDataSource to use the ProductsBLL class s GetProductsByCategoryID(categoryID) method and set the drop-down lists to (None) in the UPDATE, INSERT, and DELETE tabs.

使用 ProductsBLL 類別的 GetProductsByCategoryID (類別 Id)方法Use the ProductsBLL Class s GetProductsByCategoryID(categoryID) Method

圖 8:使用 ProductsBLL 類別的 GetProductsByCategoryID(categoryID) 方法(按一下以查看完整大小的影像Figure 8: Use the ProductsBLL Class s GetProductsByCategoryID(categoryID) Method (Click to view full-size image)

[設定資料來源] 嚮導中的最後一個步驟會提示您輸入類別的參數來源。The final step in the Configure Data Source wizard prompts for a parameter source for categoryID. 由於這項資訊是透過 querystring 欄位 CategoryID傳遞,因此請從下拉式清單中選取 [QueryString],然後在 [QueryStringField] 文字方塊中輸入 [類別],如 [圖 9] 所示。Since this information is passed through the querystring field CategoryID, select QueryString from the drop-down list and enter CategoryID in the QueryStringField textbox as shown in Figure 9. 按一下 [完成] 完成精靈。Click Finish to complete the wizard.

使用 [類別 Id] 參數的 [類別 Id Querystring] 欄位Use the CategoryID Querystring Field for the categoryID Parameter

圖 9:使用 [類別] 參數的 [CategoryID Querystring] 欄位(按一下以查看完整大小的影像Figure 9: Use the CategoryID Querystring Field for the categoryID Parameter (Click to view full-size image)

完成 wizard 之後,Visual Studio 會將對應的 BoundFields 和 CheckBoxField 加入至 GridView 中的產品資料欄位。After completing the wizard, Visual Studio will add corresponding BoundFields and a CheckBoxField to the GridView for the product data fields. 移除 ProductNameUnitPriceSupplierName BoundFields 以外的所有。Remove all but the ProductName, UnitPrice, and SupplierName BoundFields. 將這三個 BoundFields HeaderText 屬性自訂為分別讀取產品、價格和供應商。Customize these three BoundFields HeaderText properties to read Product, Price, and Supplier, respectively. UnitPrice BoundField 格式化為貨幣。Format the UnitPrice BoundField as a currency.

接下來,新增 HyperLinkField,並將它移至最左邊的位置。Next, add a HyperLinkField and move it to the left-most position. 將其 [Text] 屬性設為 [View Details],將其 DataNavigateUrlFields 屬性設定為 [ProductID],將其 [DataNavigateUrlFormatString] ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}屬性設定為Set its Text property to View Details, its DataNavigateUrlFields property to ProductID, and its DataNavigateUrlFormatString property to ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.

新增指向 ProductDetails 的 View Details HyperLinkField

圖 10:加入指向 ProductDetails.aspx 的 [View Details] HyperLinkFieldFigure 10: Add a View Details HyperLinkField that Points to ProductDetails.aspx

進行這些自訂之後,GridView 和 ObjectDataSource 的宣告式標記應該如下所示:After making these customizations, the GridView and ObjectDataSource s declarative markup should resemble the following:

<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],然後按一下飲料的 [查看產品] 連結。Return to viewing Default.aspx through a browser and click on the View Products link for Beverages. 這會讓您 ProductsByCategory.aspx?CategoryID=1,在 Northwind 資料庫中顯示屬於飲料類別的產品名稱、價格和供應商(請參閱 [圖 11])。This will take you to ProductsByCategory.aspx?CategoryID=1, displaying the names, prices, and suppliers of the products in the Northwind database that belong to the Beverages category (see Figure 11). 歡迎您進一步增強此頁面,以包含可將使用者傳回分類清單頁面(Default.aspx)的連結,以及顯示所選類別目錄名稱和描述的 DetailsView 或 FormView 控制項。Feel free to further enhance this page to include a link to return users to the category listing page (Default.aspx) and a DetailsView or FormView control that displays the selected category s name and description.

會顯示飲料名稱、價格和供應商The Beverages Names, Prices, and Suppliers are Displayed

圖 11:顯示飲料名稱、價格和供應商(按一下以觀看完整大小的影像Figure 11: The Beverages Names, Prices, and Suppliers are Displayed (Click to view full-size image)

步驟4:顯示產品的詳細資料Step 4: Showing a Product s Details

最後一頁 [ProductDetails.aspx] 會顯示所選產品的詳細資料。The final page, ProductDetails.aspx, displays the selected products details. 開啟 ProductDetails.aspx,然後從 [工具箱] 將 [DetailsView] 拖曳至設計工具。Open ProductDetails.aspx and drag a DetailsView from the Toolbox onto the Designer. 將 [DetailsView s ID] 屬性設定為 ProductInfo 並清除其 HeightWidth 屬性值。Set the DetailsView s ID property to ProductInfo and clear out its Height and Width property values. 從其智慧標籤,將 DetailsView 系結至名為 ProductDataSource的新 ObjectDataSource,並設定 ObjectDataSource 從 ProductsBLL 類別 s GetProductByProductID(productID) 方法提取其資料。From its smart tag, bind the DetailsView to a new ObjectDataSource named ProductDataSource, configuring the ObjectDataSource to pull its data from the ProductsBLL class s GetProductByProductID(productID) method. 如同先前在步驟2和3中建立的網頁,將 [更新]、[插入] 和 [刪除] 索引標籤中的下拉式清單設定為 [(無)]。As with the previous web pages created in Steps 2 and 3, set the drop-down lists in the UPDATE, INSERT, and DELETE tabs to (None) .

將 ObjectDataSource 設定為使用 GetProductByProductID (productID)方法Configure the ObjectDataSource to Use the GetProductByProductID(productID) Method

圖 12:設定 ObjectDataSource 以使用 GetProductByProductID(productID) 方法(按一下以查看完整大小的影像Figure 12: Configure the ObjectDataSource to Use the GetProductByProductID(productID) Method (Click to view full-size image)

[設定資料來源] wizard 的最後一個步驟會提示您輸入productID參數的來源。The last step of the Configure Data Source wizard prompts for the source of the productID parameter. 由於此資料是透過 querystring 欄位 ProductID,請將下拉式清單設定為 QueryString,並將 QueryStringField 文字方塊設為 ProductID。Since this data comes through the querystring field ProductID, set the drop-down list to QueryString and the QueryStringField textbox to ProductID. 最後,按一下 [完成] 按鈕以完成嚮導。Finally, click the Finish button to complete the wizard.

設定 productID 參數,從 ProductID Querystring 欄位提取其值Configure the productID Parameter to Pull its Value from the ProductID Querystring Field

圖 13:設定productID參數以從 ProductID Querystring 欄位提取其值(按一下以查看完整大小的影像Figure 13: Configure the productID Parameter to Pull its Value from the ProductID Querystring Field (Click to view full-size image)

完成 [設定資料來源] wizard 之後,Visual Studio 將會在 [產品資料] 欄位的 DetailsView 中建立對應的 BoundFields 和 CheckBoxField。After completing the Configure Data Source wizard, Visual Studio will create corresponding BoundFields and a CheckBoxField in the DetailsView for the product data fields. 移除 [ProductID]、[SupplierID] 和 [CategoryID BoundFields],並視需要設定其餘欄位。Remove the ProductID, SupplierID, and CategoryID BoundFields and configure the remaining fields as you see fit. 在少數美觀設定之後,我的 DetailsView 和 ObjectDataSource 宣告式標記看起來如下所示:After a handful of aesthetic configurations, my DetailsView and ObjectDataSource s declarative markup looked like the following:

<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,然後按一下 [飲料] 類別的 [查看產品]。To test this page, return to Default.aspx and click on View Products for the Beverages category. 從飲料產品的清單中,按一下 [Chai 茶] 的 [View Details] 連結。From the listing of beverage products, click on the View Details link for Chai Tea. 這會帶您前往 ProductDetails.aspx?ProductID=1,其中顯示 Chai 的茶詳細資料(請參閱 [圖 14])。This will take you to ProductDetails.aspx?ProductID=1, which shows a Chai Tea s details (see Figure 14).

會顯示 Chai 茶 s 供應商、類別、價格及其他資訊Chai Tea s Supplier, Category, Price, and Other Information is Displayed

圖 14:顯示 Chai 茶 s 供應商、類別、價格和其他資訊(按一下以觀看完整大小的影像Figure 14: Chai Tea s Supplier, Category, Price, and Other Information is Displayed (Click to view full-size image)

步驟5:瞭解網站地圖提供者的內部運作Step 5: Understanding the Inner Workings of a Site Map Provider

網站地圖會在 web 伺服器的記憶體中表示為構成階層之 SiteMapNode 實例的集合。The site map is represented in the web server s memory as a collection of SiteMapNode instances that form a hierarchy. 只能有一個根,所有非根節點都只能有一個父節點,而所有節點可能會有任意數目的子系。There must be exactly one root, all non-root nodes must have exactly one parent node, and all nodes may have an arbitrary number of children. 每個 SiteMapNode 物件都代表網站 s 結構中的一個區段;這些區段通常會有對應的網頁。Each SiteMapNode object represents a section in the website s structure; these sections commonly have a corresponding web page. 因此, SiteMapNode 類別的屬性如 TitleUrlDescription,可提供 SiteMapNode 所代表之區段的資訊。Consequently, the SiteMapNode class has properties like Title, Url, and Description, which provide information for the section the SiteMapNode represents. 另外還有一個 Key 屬性,可唯一識別階層中的每個 SiteMapNode,以及用來建立此階層 ChildNodesParentNodeNextSiblingPreviousSibling等等的屬性。There is also a Key property that uniquely identifies each SiteMapNode in the hierarchy, as well as properties used to establish this hierarchy ChildNodes, ParentNode, NextSibling, PreviousSibling, and so forth.

[圖 15] 顯示 [圖 1] 中的一般網站地圖結構,但有更詳細的執行詳細資料。Figure 15 shows the general site map structure from Figure 1, but with the implementation details sketched out in finer detail.

每個 SiteMapNode 都有標題、Url、索引鍵等等的屬性Each SiteMapNode has Properties Like Title, Url, Key, and So On

圖 15:每個 SiteMapNode 的屬性如 TitleUrlKey等等(按一下以觀看完整大小的影像Figure 15: Each SiteMapNode has Properties Like Title, Url, Key, and So On (Click to view full-size image)

網站對應可透過System.Web 命名空間中的SiteMap 類別來存取。The site map is accessible through the SiteMap class in the System.Web namespace. 這個類別的 RootNode 屬性會傳回網站地圖 s 根 SiteMapNode 實例;CurrentNode 會傳回 SiteMapNode,其 Url 屬性符合目前要求之網頁的 URL。This class s RootNode property returns the site map s root SiteMapNode instance; CurrentNode returns the SiteMapNode whose Url property matches the URL of the currently requested page. 這個類別是由 ASP.NET 2.0 s 導覽 Web 控制項在內部使用。This class is used internally by ASP.NET 2.0 s navigation Web controls.

存取 SiteMap 類別的屬性時,必須將來自某個持續性媒介的網站地圖結構序列化為記憶體。When the SiteMap class s properties are accessed, it must serialize the site map structure from some persistent medium into memory. 不過,網站地圖序列化邏輯並不會硬式編碼到 SiteMap 類別中。However, the site map serialization logic is not hard coded into the SiteMap class. 相反地,在執行時間,SiteMap 類別會決定要用於序列化的網站地圖提供者Instead, at runtime the SiteMap class determines which site map provider to use for serialization. 預設會使用XmlSiteMapProvider 類別,這會從格式正確的 XML 檔案讀取網站地圖 s 結構。By default, the XmlSiteMapProvider class is used, which reads the site map s structure from a properly-formatted XML file. 不過,只要稍加一些工作,我們就可以建立自己的自訂網站地圖提供者。However, with a little bit of work we can create our own custom site map provider.

所有網站地圖提供者都必須衍生自SiteMapProvider 類別,其中包括網站地圖提供者所需的基本方法和屬性,但省略了許多的執行細節。All site map providers must be derived from the SiteMapProvider class, which includes the essential methods and properties needed for site map providers, but omits many of the implementation details. 第二個類別( StaticSiteMapProvider)會擴充 SiteMapProvider 類別,並包含更健全的必要功能執行。A second class, StaticSiteMapProvider, extends the SiteMapProvider class and contains a more robust implementation of the needed functionality. 就內部而言,StaticSiteMapProvider 會在 Hashtable 中儲存網站地圖的 SiteMapNode 實例,並提供 AddNode(child, parent)RemoveNode(siteMapNode),Clear() 之類的方法,將 SiteMapNode 新增和移除至內部 HashtableInternally, the StaticSiteMapProvider stores the SiteMapNode instances of the site map in a Hashtable and provides methods like AddNode(child, parent), RemoveNode(siteMapNode), and Clear() that add and remove SiteMapNode s to the internal Hashtable. XmlSiteMapProvider 衍生自 StaticSiteMapProviderXmlSiteMapProvider is derived from StaticSiteMapProvider.

建立擴充 StaticSiteMapProvider的自訂網站地圖提供者時,有兩個必須覆寫的抽象方法: BuildSiteMapGetRootNodeCoreWhen creating a custom site map provider that extends StaticSiteMapProvider, there are two abstract methods that must be overridden: BuildSiteMap and GetRootNodeCore. BuildSiteMap,正如其名,會負責從持續性儲存體載入網站地圖結構,並在記憶體中加以建立。BuildSiteMap, as its name implies, is responsible for loading the site map structure from persistent storage and constructing it in memory. GetRootNodeCore 會傳回網站地圖中的根節點。GetRootNodeCore returns the root node in the site map.

在 web 應用程式可以使用網站地圖提供者之前,您必須先在應用程式的設定中註冊。Before a web application can use a site map provider it must be registered in the application s configuration. 根據預設,會使用名稱 AspNetXmlSiteMapProvider註冊 XmlSiteMapProvider 類別。By default, the XmlSiteMapProvider class is registered using the name AspNetXmlSiteMapProvider. 若要註冊額外的網站地圖提供者,請將下列標記新增至 Web.configTo register additional site map providers, add the following markup to Web.config:

<configuration>
    <system.web>
        ...
        <siteMap defaultProvider="defaultProviderName">
          <providers>
            <add name="name" type="type" />
          </providers>
        </siteMap>
    </system.web>
</configuration>

Name值會將人類可讀的名稱指派給提供者,而type會指定網站地圖提供者的完整型別名稱。The name value assigns a human-readable name to the provider while type specifies the fully-qualified type name of the site map provider. 我們會在建立自訂網站地圖提供者之後,探索步驟7中的名稱類型值的具體值。We'll explore concrete values for the name and type values in Step 7, after we ve created our custom site map provider.

網站地圖提供者類別會在第一次從 SiteMap 類別存取時具現化,並在 web 應用程式的存留期間保留在記憶體中。The site map provider class is instantiated the first time it is accessed from the SiteMap class and remains in memory for the lifetime of the web application. 因為只有一個網站地圖提供者的實例可以從多個並行網站訪客叫用,所以提供者的方法必須是安全線程Since there is only one instance of the site map provider that may be invoked from multiple, concurrent web site visitors, it is imperative that the provider s methods be thread-safe.

基於效能和擴充性的原因,我們一定要快取記憶體中的網站地圖結構並傳回此快取的結構,而不是在每次叫用 BuildSiteMap 方法時重新建立。For performance and scalability reasons, it s important that we cache the in-memory site map structure and return this cached structure rather than recreating it every time the BuildSiteMap method is invoked. 視頁面上使用的導覽控制項和網站地圖結構的深度而定,每位使用者可能會針對每個頁面要求多次呼叫 BuildSiteMapBuildSiteMap may be called several times per page request per user, depending on the navigation controls in use on the page and the depth of the site map structure. 在任何情況下,如果我們未在 BuildSiteMap 中快取網站地圖結構,則每次叫用時,我們都必須從架構重新取出產品和類別資訊(這會導致資料庫的查詢)。In any case, if we do not cache the site map structure in BuildSiteMap then each time it is invoked we would need to re-retrieve the product and category information from the architecture (which would result in a query to the database). 如先前的快取教學課程中所討論,快取的資料可能會過時。As we discussed in the previous caching tutorials, cached data can become stale. 為了對抗此,我們可以使用時間或 SQL 快取相依性的 expiries。To combat this, we can use either time- or SQL cache dependency-based expiries.

Note

網站地圖提供者可以選擇性地覆寫Initialize 方法A site map provider may optionally override the Initialize method. 當第一次具現化網站地圖提供者,並在 <add> 專案的 Web.config 中傳遞任何指派給提供者的自訂屬性時,會叫用 Initialize,例如: <add name="name" type="type" customAttribute="value" />Initialize is invoked when the site map provider is first instantiated and is passed any custom attributes assigned to the provider in Web.config in the <add> element like: <add name="name" type="type" customAttribute="value" />. 如果您想要讓網頁開發人員指定各種網站地圖提供者相關的設定,而不需要修改提供者的程式碼,這會很有用。It is useful if you want to allow a page developer to specify various site map provider-related settings without having to modify the provider s code. 例如,如果我們直接從資料庫讀取類別目錄和產品資料,而不是透過架構,我們可能會想要讓網頁開發人員透過 Web.config 來指定資料庫連接字串,而不是在提供者的程式碼中使用硬式編碼值。For example, if we were reading the category and products data directly from the database as opposed to through the architecture, we d likely want to let the page developer specify the database connection string through Web.config rather than using a hard coded value in the provider s code. 我們將在步驟6中建立的自訂網站地圖提供者,並不會覆寫此 Initialize 方法。The custom site map provider we'll build in Step 6 does not override this Initialize method. 如需使用 Initialize 方法的範例,請參閱在 SQL Server 文章中儲存網站對應Jeff ProsiseFor an example of using the Initialize method, refer to Jeff Prosise s Storing Site Maps in SQL Server article.

步驟6:建立自訂網站地圖提供者Step 6: Creating the Custom Site Map Provider

若要建立自訂網站地圖提供者,以根據 Northwind 資料庫中的類別和產品建立網站地圖,我們需要建立可延伸 StaticSiteMapProvider的類別。To create a custom site map provider that builds the site map from the categories and products in the Northwind database, we need to create a class that extends StaticSiteMapProvider. 在步驟1中,我要求您在 [App_Code] 資料夾中新增一個 CustomProviders 資料夾-將新類別新增至名為 NorthwindSiteMapProvider的這個資料夾。In Step 1 I asked you to add a CustomProviders folder in the App_Code folder - add a new class to this folder named NorthwindSiteMapProvider. 將下列程式碼加入 NorthwindSiteMapProvider 類別:Add the following code to the NorthwindSiteMapProvider class:

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語句開頭。Let s start with exploring this class s BuildSiteMap method, which starts with a lock statement. lock 語句一次只允許一個執行緒進入,因此會序列化其程式碼的存取,並防止兩個平行線程在另一個腳趾上逐步執行。The lock statement only allows one thread at a time to enter, thereby serializing access to its code and preventing two concurrent threads from stepping on one another s toes.

類別層級 SiteMapNode 變數 root 用來快取網站地圖結構。The class-level SiteMapNode variable root is used to cache the site map structure. 第一次建立網站對應時,或在基礎資料修改後第一次建立時,root 將會 null,並會構造網站地圖結構。When the site map is constructed for the first time, or for the first time after the underlying data has been modified, root will be null and the site map structure will be constructed. 網站地圖的根節點會在結構處理期間指派給 root,因此下次呼叫這個方法時,將不會 null``rootThe site map s root node is assigned to root during the construction process so that the next time this method is called, root will not be null. 因此,只要 rootnull,網站地圖結構就會傳回給呼叫者,而不需要重新建立。Consequently, so long as root is not null the site map structure will be returned to the caller without having to recreate it.

如果 nullroot,則會從產品和類別資訊建立網站地圖結構。If root is null, the site map structure is created from the product and category information. 網站地圖的建立方式,是藉由建立 SiteMapNode 實例,然後透過呼叫 StaticSiteMapProvider 類別 s AddNode 方法來形成階層。The site map is built by creating the SiteMapNode instances and then forming the hierarchy through calls to the StaticSiteMapProvider class s AddNode method. AddNode 會執行內部簿記,將各種 SiteMapNode 實例儲存在 Hashtable中。AddNode performs the internal bookkeeping, storing the assorted SiteMapNode instances in a Hashtable. 開始建立階層之前,我們先呼叫 Clear 方法,這會清除內部 Hashtable的元素。Before we start constructing the hierarchy, we start by calling the Clear method, which clears out the elements from the internal Hashtable. 接下來,ProductsBLL 類別 GetProducts 方法和產生的 ProductsDataTable 會儲存在本機變數中。Next, the ProductsBLL class s GetProducts method and the resulting ProductsDataTable are stored in local variables.

網站地圖的結構一開始先建立根節點,並將它指派給 rootThe site map s construction begins by creating the root node and assigning it to root. 在此 BuildSiteMap 中使用的SiteMapNode s函式的多載會傳遞下列資訊:The overload of the SiteMapNode s constructor used here and throughout this BuildSiteMap is passed the following information:

  • 網站地圖提供者的參考(this)。A reference to the site map provider (this).
  • SiteMapNode s KeyThe SiteMapNode s Key. 針對每個 SiteMapNode,此必要值必須是唯一的。This required value must be unique for each SiteMapNode.
  • SiteMapNode s UrlThe SiteMapNode s Url. Url 是選擇性的,但如果提供的話,每個 SiteMapNode s Url 值都必須是唯一的。Url is optional, but if provided, each SiteMapNode s Url value must be unique.
  • SiteMapNode s Title,這是必要的。The SiteMapNode s Title, which is required.

AddNode(root) 方法呼叫會將 SiteMapNode root 新增至網站地圖做為根。The AddNode(root) method call adds the SiteMapNode root to the site map as the root. 接下來,會列舉 ProductsDataTable 中的每個 ProductRowNext, each ProductRow in the ProductsDataTable is enumerated. 如果目前產品的類別已經有 SiteMapNode,則會參考它。If there already exists a SiteMapNode for the current product s category, it is referenced. 否則,會透過 AddNode(categoryNode, root) 方法呼叫來建立類別的新 SiteMapNode,並將其新增為 SiteMapNode``root 的子系。Otherwise, a new SiteMapNode for the category is created and added as a child of the SiteMapNode``root through the AddNode(categoryNode, root) method call. 找到或建立適當的類別 SiteMapNode 節點之後,就會為目前的產品建立 SiteMapNode,並透過 AddNode(productNode, categoryNode)將其新增為類別 SiteMapNode 目錄的子系。After the appropriate category SiteMapNode node has been found or created, a SiteMapNode is created for the current product and added as a child of the category SiteMapNode via AddNode(productNode, categoryNode). 請注意,類別 SiteMapNode s Url 屬性值是 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID,而產品 SiteMapNode s Url 屬性指派 ~/SiteMapNode/ProductDetails.aspx?ProductID=productIDNote that the category SiteMapNode s Url property value is ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID while the product SiteMapNode s Url property is assigned ~/SiteMapNode/ProductDetails.aspx?ProductID=productID.

Note

具有資料庫 NULL 值的產品 CategoryID 會分組在其 Title 屬性設為 [無] 且其 [Url] 屬性設定為空字串的類別 SiteMapNodeThose products that have a database NULL value for their CategoryID are grouped under a category SiteMapNode whose Title property is set to None and whose Url property is set to an empty string. 我決定將 Url 設為空字串,因為 ProductBLL 類別的 GetProductsByCategory(categoryID) 方法目前缺少以 NULL CategoryID 值傳回那些產品的功能。I decided to set Url to an empty string since the ProductBLL class s GetProductsByCategory(categoryID) method currently lacks the capability to return just those products with a NULL CategoryID value. 此外,我也想要示範導覽控制項如何呈現缺少其 Url 屬性值的 SiteMapNodeAlso, I wanted to demonstrate how the navigation controls render a SiteMapNode that lacks a value for its Url property. 我建議您擴充本教學課程,讓 None SiteMapNode s Url 屬性指向 ProductsByCategory.aspx,但只會顯示具有 NULL CategoryID 值的產品。I encourage you to extend this tutorial so that the None SiteMapNode s Url property points to ProductsByCategory.aspx, yet only displays the products with NULL CategoryID values.

在建立網站地圖之後,會使用 Categories 上的 SQL 快取相依性,將任意物件新增至資料快取,並透過 AggregateCacheDependency 物件 Products 資料表。After constructing the site map, an arbitrary object is added to the data cache using a SQL cache dependency on the Categories and Products tables through an AggregateCacheDependency object. 我們已在先前的教學課程中使用 sql 快取相依性來探索使用 sql 快取相依性。We explored using SQL cache dependencies in the preceding tutorial, Using SQL Cache Dependencies. 不過,自訂網站地圖提供者會使用我們尚未探索的資料快取 Insert 方法的多載。The custom site map provider, however, uses an overload of the data cache s Insert method that we ve yet to explore. 這個多載會接受在從快取中移除物件時所呼叫的委派作為其最終的輸入參數。This overload accepts as its final input parameter a delegate that is called when the object is removed from the cache. 具體而言,我們會傳入新的CacheItemRemovedCallback 委派,指向 NorthwindSiteMapProvider 類別中進一步向下定義的 OnSiteMapChanged 方法。Specifically, we pass in a new CacheItemRemovedCallback delegate that points to the OnSiteMapChanged method defined further down in the NorthwindSiteMapProvider class.

Note

網站地圖的記憶體中表示會透過類別層級的變數 root進行快取。The in-memory representation of the site map is cached through the class-level variable root. 因為自訂網站地圖提供者類別只有一個實例,而且該實例會在 web 應用程式中的所有線程之間共用,所以這個類別變數會當做快取。Since there is only one instance of the custom site map provider class and since that instance is shared among all threads in the web application, this class variable serves as a cache. BuildSiteMap 方法也會使用資料快取,但只有在 CategoriesProducts 資料表中的基礎資料庫資料變更時,才會收到通知。The BuildSiteMap method also uses the data cache, but only as a means to receive notification when the underlying database data in the Categories or Products tables changes. 請注意,放入資料快取中的值只是目前的日期和時間。Note that the value put into the data cache is just the current date and time. 實際網站地圖資料並會放在資料快取中。The actual site map data is not put in the data cache.

BuildSiteMap 方法會藉由傳回網站地圖的根節點來完成。The BuildSiteMap method completes by returning the root node of the site map.

其餘的方法相當簡單。The remaining methods are fairly straightforward. GetRootNodeCore 會負責傳回根節點。GetRootNodeCore is responsible for returning the root node. 因為 BuildSiteMap 會傳回根,GetRootNodeCore 只會傳回 BuildSiteMap s 傳回值。Since BuildSiteMap returns the root, GetRootNodeCore simply returns BuildSiteMap s return value. 當移除快取專案時,OnSiteMapChanged 方法會將 root 設定回 nullThe OnSiteMapChanged method sets root back to null when the cache item is removed. 當 root 設定為 null,下一次叫用 BuildSiteMap 時,將會重建網站地圖結構。With root set back to null, the next time BuildSiteMap is invoked, the site map structure will be rebuilt. 最後,如果有這樣的值,CachedDate 屬性會傳回儲存在資料快取中的日期和時間值。Lastly, the CachedDate property returns the date and time value stored in the data cache, if such a value exists. 頁面開發人員可以使用這個屬性來判斷上次快取網站對應資料的時間。This property can be used by a page developer to determine when the site map data was last cached.

步驟7:註冊NorthwindSiteMapProviderStep 7: Registering theNorthwindSiteMapProvider

為了讓 web 應用程式使用在步驟6中建立的 NorthwindSiteMapProvider 網站地圖提供者,我們需要在 Web.config<siteMap> 區段中註冊它。In order for our web application to use the NorthwindSiteMapProvider site map provider created in Step 6, we need to register it in the <siteMap> section of Web.config. 具體而言,請在 Web.config中的 <system.web> 元素內新增下列標記:Specifically, add the following markup within the <system.web> element in Web.config:

<siteMap defaultProvider="AspNetXmlSiteMapProvider">
  <providers>
    <add name="Northwind" type="NorthwindSiteMapProvider" />
  </providers>
</siteMap>

此標記會執行兩件事:首先,它會指出內建的 AspNetXmlSiteMapProvider 是預設網站地圖提供者;第二,它會註冊在步驟6中建立的自訂網站地圖提供者,並使用易記名稱 Northwind。This markup does two things: first, it indicates that the built-in AspNetXmlSiteMapProvider is the default site map provider; second, it registers the custom site map provider created in Step 6 with the human-friendly name Northwind .

Note

針對位於應用程式 s App_Code 資料夾中的網站地圖提供者,type 屬性的值只是類別名稱。For site map providers located in the application s App_Code folder, the value of the type attribute is simply the class name. 或者,也可以在個別的類別庫專案中建立自訂的網站地圖提供者,並將編譯的元件放在 web 應用程式的 /Bin 目錄中。Alternatively, the custom site map provider could have been created in a separate Class Library project with the compiled assembly placed in the web application s /Bin directory. 在此情況下,type 屬性值會是NamespaceClassNameAssemblyNameIn that case, the type attribute value would be Namespace.ClassName, AssemblyName .

更新 Web.config之後,請花一點時間從瀏覽器的教學課程中查看任何頁面。After updating Web.config, take a moment to view any page from the tutorials in a browser. 請注意,左邊的導覽介面仍然會顯示 Web.sitemap中定義的區段和教學課程。Note that the navigation interface on the left still shows the sections and tutorials defined in Web.sitemap. 這是因為我們將 AspNetXmlSiteMapProvider 保留為預設提供者。This is because we left AspNetXmlSiteMapProvider as the default provider. 為了建立使用 NorthwindSiteMapProvider的導覽使用者介面專案,我們必須明確指定應使用 Northwind 網站地圖提供者。In order to create a navigation user interface element that uses the NorthwindSiteMapProvider, we'll need to explicitly specify that the Northwind site map provider should be used. 我們會在步驟8中瞭解如何完成這項工作。We'll see how to accomplish this in Step 8.

步驟8:使用自訂網站地圖提供者顯示網站地圖資訊Step 8: Displaying Site Map Information Using the Custom Site Map Provider

Web.config中建立並註冊自訂網站地圖提供者之後,我們就可以將導覽控制項加入至 [SiteMapProvider] 資料夾中的 [Default.aspx]、[ProductsByCategory.aspx] 和 [ProductDetails.aspx] 頁面。With the custom site map provider created and registered in Web.config, we re ready to add navigation controls to the Default.aspx, ProductsByCategory.aspx, and ProductDetails.aspx pages in the SiteMapProvider folder. 一開始先開啟 [Default.aspx] 頁面,並將 SiteMapPath 從 [工具箱] 拖曳至設計工具。Start by opening the Default.aspx page and drag a SiteMapPath from the Toolbox onto the Designer. SiteMapPath 控制項位於 [工具箱] 的導覽區段中。The SiteMapPath control is located in the Navigation section of the Toolbox.

將 SiteMapPath 新增至 default.aspxAdd a SiteMapPath to Default.aspx

圖 16:將 SiteMapPath 新增至 Default.aspx按一下以觀看完整大小的影像Figure 16: Add a SiteMapPath to Default.aspx (Click to view full-size image)

SiteMapPath 控制項會顯示階層連結,指出網站地圖內目前的頁面位置。The SiteMapPath control displays a breadcrumb, indicating the current page s location within the site map. 我們已在主版頁面和網站導覽教學課程中,將 SiteMapPath 新增至主版頁面的頂端。We added a SiteMapPath to the top of the master page back in the Master Pages and Site Navigation tutorial.

請花點時間透過瀏覽器觀看此頁面。Take a moment to view this page through a browser. [圖 16] 中新增的 SiteMapPath 會使用預設網站地圖提供者,從 Web.sitemap提取其資料。The SiteMapPath added in Figure 16 uses the default site map provider, pulling its data from Web.sitemap. 因此,階層連結會顯示 [首頁] > 自訂網站地圖,就像右上角的階層連結一樣。Therefore, the breadcrumb shows Home > Customizing the Site Map, just like the breadcrumb in the upper-right corner.

階層連結會使用預設的網站地圖提供者The Breadcrumb Uses the Default Site Map Provider

圖 17:階層連結會使用預設網站地圖提供者(按一下以查看完整大小的影像Figure 17: The Breadcrumb Uses the Default Site Map Provider (Click to view full-size image)

若要在 [圖 16] 中新增 SiteMapPath,請使用我們在步驟6中建立的自訂網站地圖提供者,將其SiteMapProvider 屬性設定為 Northwind,這是我們指派給 Web.configNorthwindSiteMapProvider 的名稱。To have the SiteMapPath added in Figure 16 use the custom site map provider we created in Step 6, set its SiteMapProvider property to Northwind, the name we assigned to the NorthwindSiteMapProvider in Web.config. 可惜的是,設計工具會繼續使用預設的網站地圖提供者,但如果您在進行此屬性變更之後,透過瀏覽器造訪網頁,您會看到階層連結現在會使用自訂網站地圖提供者。Unfortunately, the Designer continues to use the default site map provider, but if you visit the page through a browser after making this property change you'll see that the breadcrumb now uses the custom site map provider.

階層連結現在會使用自訂網站地圖提供者 NorthwindSiteMapProviderThe Breadcrumb Now Uses the Custom Site Map Provider NorthwindSiteMapProvider

圖 18:階層連結現在會使用自訂網站地圖提供者 NorthwindSiteMapProvider按一下以觀看完整大小的影像Figure 18: The Breadcrumb Now Uses the Custom Site Map Provider NorthwindSiteMapProvider (Click to view full-size image)

SiteMapPath 控制項會在 [ProductsByCategory.aspx] 和 [ProductDetails.aspx] 頁面中顯示功能更多功能的使用者介面。The SiteMapPath control displays a more functional user interface in the ProductsByCategory.aspx and ProductDetails.aspx pages. 將 SiteMapPath 新增至這些頁面,並將兩者中的 SiteMapProvider 屬性設定為 Northwind。Add a SiteMapPath to these pages, setting the SiteMapProvider property in both to Northwind. Default.aspx 按一下飲料的 [查看產品] 連結,然後在 [Chai 茶] 的 [查看詳細資料] 連結上。From Default.aspx click on the View Products link for Beverages, and then on the View Details link for Chai Tea. 如 [圖 19] 所示,階層連結包括目前的網站地圖區段(Chai 茶)及其祖系:飲料和所有類別。As Figure 19 shows, the breadcrumb includes the current site map section ( Chai Tea ) and its ancestors: Beverages and All Categories .

階層連結現在會使用自訂網站地圖提供者 NorthwindSiteMapProviderThe Breadcrumb Now Uses the Custom Site Map Provider NorthwindSiteMapProvider

圖 19:階層連結現在會使用自訂網站地圖提供者 NorthwindSiteMapProvider按一下以觀看完整大小的影像Figure 19: The Breadcrumb Now Uses the Custom Site Map Provider NorthwindSiteMapProvider (Click to view full-size image)

除了 SiteMapPath 之外,還可以使用其他導覽使用者介面專案,例如功能表和 TreeView 控制項。Other navigation user interface elements can be used in addition to the SiteMapPath, such as the Menu and TreeView controls. 本教學課程的下載中的 Default.aspxProductsByCategory.aspxProductDetails.aspx 頁面,例如所有包含功能表控制項(請參閱 [圖 20])。The Default.aspx, ProductsByCategory.aspx, and ProductDetails.aspx pages in the download for this tutorial, for example, all include Menu controls (see Figure 20). 如需深入瞭解 ASP.NET 2.0 中的導覽控制項和網站地圖系統,請參閱 檢查 ASP.NET 2.0 s 網站流覽功能和[使用ASP.NET 2.0 快速入門的網站導覽控制項](https://quickstarts.asp.net/QuickStartv20/aspnet/doc/navigation/sitenavcontrols.aspx)一節。See Examining ASP.NET 2.0 s Site Navigation Features and the Using Site Navigation Controls section of the ASP.NET 2.0 QuickStarts for a more in-depth look at the navigation controls and site map system in ASP.NET 2.0.

功能表控制項列出每個類別和產品The Menu Control Lists Each of the Categories and Products

圖 20: Menu 控制項列出每個類別和產品(按一下以觀看完整大小的影像Figure 20: The Menu Control Lists Each of the Categories and Products (Click to view full-size image)

如本教學課程稍早所述,您可以透過 SiteMap 類別,以程式設計方式存取網站地圖結構。As mentioned earlier in this tutorial, the site map structure can be accessed programmatically through the SiteMap class. 下列程式碼會傳回預設提供者的根 SiteMapNodeThe following code returns the root SiteMapNode of the default provider:

SiteMapNode root = SiteMap.RootNode;

由於 AspNetXmlSiteMapProvider 是應用程式的預設提供者,因此上述程式碼會傳回 Web.sitemap中定義的根節點。Since the AspNetXmlSiteMapProvider is the default provider for our application, the above code would return the root node defined in Web.sitemap. 若要參考非預設的網站地圖提供者,請使用 SiteMap 類別的Providers 屬性,如下所示:To reference a site map provider other than the default, use the SiteMap class s Providers property like so:

SiteMapNode root = SiteMap.Providers["name"].RootNode;

其中name是自訂網站地圖提供者的名稱(Northwind,適用于我們的 web 應用程式)。Where name is the name of the custom site map provider ( Northwind, for our web application).

若要存取網站地圖提供者特定的成員,請使用 SiteMap.Providers["name"] 來抓取提供者實例,然後將它轉換成適當的類型。To access a member specific to a site map provider, use SiteMap.Providers["name"] to retrieve the provider instance and then cast it to the appropriate type. 例如,若要在 ASP.NET 網頁中顯示 NorthwindSiteMapProvider s CachedDate 屬性,請使用下列程式碼:For example, to display the NorthwindSiteMapProvider s CachedDate property in an ASP.NET page, use the following code:

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!";
}

Note

請務必測試 SQL 快取相依性功能。Be sure to test out the SQL cache dependency feature. 造訪 Default.aspxProductsByCategory.aspxProductDetails.aspx 頁面之後,請移至 [編輯、插入及刪除] 區段中的其中一個教學課程,然後編輯類別或產品的名稱。After visiting the Default.aspx, ProductsByCategory.aspx, and ProductDetails.aspx pages, go to one of the tutorials in the Editing, Inserting, and Deleting section and edit the name of a category or product. 然後返回 [SiteMapProvider] 資料夾中的其中一個頁面。Then return to one of the pages in the SiteMapProvider folder. 假設已有足夠的時間來輪詢機制,以注意基礎資料庫的變更,則應該更新網站地圖以顯示新的產品或類別名稱。Assuming enough time has passed for the polling mechanism to note the change to the underlying database, the site map should be updated to show the new product or category name.

總結Summary

ASP.NET 2.0 s 網站地圖功能包括 SiteMap 類別、數個內建的導覽 Web 控制項,以及預設的網站地圖提供者,其會預期網站對應資訊會保存到 XML 檔案中。ASP.NET 2.0 s site map features includes a SiteMap class, a number of built-in navigation Web controls, and a default site map provider that expects the site map information persisted to an XML file. 若要從其他來源(例如,從資料庫、應用程式架構或遠端 Web 服務)使用網站地圖資訊,我們需要建立自訂網站地圖提供者。In order to use site map information from some other source such as from a database, the application s architecture, or a remote Web service we need to create a custom site map provider. 這牽涉到建立直接或間接從 SiteMapProvider 類別衍生的類別。This involves creating a class that derives, directly or indirectly, from the SiteMapProvider class.

在本教學課程中,我們已瞭解如何根據從應用程式架構挑選的產品和類別資訊,建立以網站地圖為基礎的自訂網站地圖提供者。In this tutorial we saw how to create a custom site map provider that based the site map on the product and category information culled from the application architecture. 我們的提供者擴充了 StaticSiteMapProvider 類別,並詳述如下建立一個 BuildSiteMap 方法來抓取資料、結構化網站地圖階層,並在類別層級變數中快取產生的結構。Our provider extended the StaticSiteMapProvider class and entailed creating a BuildSiteMap method that retrieved the data, constructed the site map hierarchy, and cached the resulting structure in a class-level variable. 我們在修改基礎 CategoriesProducts 資料時,使用了 SQL 快取相依性搭配回呼函數使快取的結構失效。We used a SQL cache dependency with a callback function to invalidate the cached structure when the underlying Categories or Products data is modified.

快樂的程式設計!Happy Programming!

進一步閱讀Further Reading

如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源:For more information on the topics discussed in this tutorial, refer to the following resources:

關於作者About the Author

Scott Mitchell,自1998起,有七個 ASP/ASP. NET 書籍和創辦人的4GuysFromRolla.comScott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott 以獨立的顧問、訓練員和作者的身分運作。Scott works as an independent consultant, trainer, and writer. 他的最新著作是在24小時內讓自己的 ASP.NET 2.0His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. 他可以在mitchell@4GuysFromRolla.com觸達He can be reached at mitchell@4GuysFromRolla.com. 或者透過他的 blog,可以在http://ScottOnWriting.NET找到。or via his blog, which can be found at http://ScottOnWriting.NET.

特別感謝Special Thanks To

本教學課程系列已由許多有用的審核者所審查。This tutorial series was reviewed by many helpful reviewers. 本教學課程的領導審查者為 Dave Gardner、Zack,Teresa Murphy 和 Bernadette Leigh。Lead reviewers for this tutorial were Dave Gardner, Zack Jones, Teresa Murphy, and Bernadette Leigh. 有興趣複習我即將發行的 MSDN 文章嗎?Interested in reviewing my upcoming MSDN articles? 若是如此,請在mitchell@4GuysFromRolla.com的那一行下拉式If so, drop me a line at mitchell@4GuysFromRolla.com.