建置自訂的資料庫驅動網站導覽提供者 (VB)
ASP.NET 2.0 中的預設網站地圖提供者會從靜態 XML 檔案擷取其數據。 雖然 XML 型提供者適用於許多小型和中型網站,但較大的 Web 應用程式需要更動態的網站對應。 在本教學課程中,我們將建置自定義網站地圖提供者,以從商業規則層擷取其數據,進而從資料庫擷取數據。
簡介
ASP.NET 2.0 s 網站地圖功能可讓頁面開發人員定義某些持續性媒體中的 Web 應用程式網站地圖,例如在 XML 檔案中。 定義之後,即可透過SiteMap
命名空間中的 System.Web
類別,或透過各種導覽 Web 控件,例如 SiteMapPath、Menu 和 TreeView 控件,以程式設計方式存取網站地圖數據。 網站地圖系統會使用提供者模型,以便建立不同的網站地圖串行化實作,並插入 Web 應用程式。 隨附於 ASP.NET 2.0 的默認網站地圖提供者會將網站地圖結構保存在 XML 檔案中。 回到主版 頁面和網站導覽 教學課程中,我們建立了名為的 Web.sitemap
檔案,其中包含此結構,並已使用每個新的教學課程區段更新其 XML。
如果網站地圖結構相當靜態,則預設 XML 型網站地圖提供者可正常運作,例如針對這些教學課程。 不過,在許多情況下,需要更動態的網站地圖。 請考慮圖 1 所示的網站地圖,其中每個類別和產品會顯示為網站結構中的區段。 透過此網站地圖,流覽對應至根節點的網頁可能會列出所有類別,而流覽特定類別的網頁會列出該類別的產品,並檢視特定產品的網頁會顯示該產品的詳細數據。
圖 1:類別和產品使網站地圖結構 (按兩下即可檢視大小完整的影像)
雖然這個類別和產品型結構可以硬式編碼到 Web.sitemap
檔案中,但每次新增、移除或重新命名類別或產品時,都必須更新檔案。 因此,如果從資料庫擷取網站地圖維護,或者最好是從應用程式架構的商業規則層擷取結構,則網站地圖維護會大幅簡化。 如此一來,隨著新增、重新命名或刪除產品與類別,網站地圖會自動更新以反映這些變更。
由於 ASP.NET 2.0 s 網站地圖串行化建置在提供者模型之上,我們可以建立自己的自定義網站地圖提供者,以從替代數據存放區擷取其數據,例如資料庫或架構。 在本教學課程中,我們將建置自定義提供者,以從 BLL 擷取其數據。 讓我們開始吧!
注意
本教學課程中建立的自定義網站地圖提供者與應用程式的架構和數據模型緊密結合。 Jeff Prosise s Storing Site Maps in SQL Server and The SQL Site Map Provider You ve waiting For articles, examine a generalized approach to storing site map data in SQL Server.
步驟 1:建立自定義網站地圖提供者網頁
開始建立自定義網站地圖提供者之前,讓我們先新增本教學課程所需的 ASP.NET 頁面。 首先,新增名為 SiteMapProvider
的新資料夾。 接下來,將下列 ASP.NET 頁面新增至該資料夾,請務必將每個頁面與 Site.master
主版頁面產生關聯:
Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx
此外, CustomProviders
將子資料夾新增至 App_Code
資料夾。
圖 2:新增網站地圖 Provider-Related 教學課程的 ASP.NET 頁面
由於本節只有一個教學課程,因此我們不需要 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
之後,請花點時間透過瀏覽器檢視教學課程網站。 左側功能表現在包含唯一網站地圖提供者教學課程的專案。
圖 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:顯示類別清單
Default.aspx
開啟資料夾中的頁面SiteMapProvider
,並將 GridView 從 [工具箱] 拖曳至 Designer,並將其ID
設定為 Categories
。 從 GridView 的智慧標記中,將其系結至名為 CategoriesDataSource
的新 ObjectDataSource,並加以設定,使其使用 CategoriesBLL
類別 s GetCategories
方法擷取其數據。 由於此 GridView 只會顯示類別,且不提供資料修改功能,因此請將 UPDATE、INSERT 和 DELETE 索引卷標的下拉式清單設定為 ([無) ]。
圖 4:使用 方法設定 ObjectDataSource 以傳 GetCategories
回類別 (按兩下即可檢視大小完整的影像)
圖 5:將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無]) (按兩下即可檢視完整大小的映像)
完成 [設定數據源精靈] 之後,Visual Studio 會新增 、CategoryName
、、 Description
NumberOfProducts
和 BrochurePath
的 CategoryID
BoundField。 編輯 GridView,使其只包含 CategoryName
和 Description
BoundFields,並將 BoundField s HeaderText
屬性更新CategoryName
為 Category 。
接下來,新增 HyperLinkField 並將它定位為最左邊的字段。 屬性設定為 CategoryID
,DataNavigateUrlFields
並將 DataNavigateUrlFormatString
屬性設定為 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}
。 將 Text
屬性設定為 [檢視產品]。
圖 6:將 HyperLinkField 新增至 Categories
GridView
建立 ObjectDataSource 並自定義 GridView 的欄位之後,這兩個控件宣告式標記看起來會如下所示:
<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。 將 ObjectDataSource 設定為使用 ProductsBLL
類別 s GetProductsByCategoryID(categoryID)
方法,並將下拉式清單設定為 ([UPDATE]、[INSERT] 和 [DELETE] 索引標籤中的 [無) ]。
圖 8:使用 ProductsBLL
類別 s GetProductsByCategoryID(categoryID)
方法 (按下即可檢視大小完整的影像)
[設定數據源精靈] 中的最後一個步驟會提示 輸入 categoryID 的參數來源。 由於這項資訊是透過 querystring 字段 CategoryID
傳遞,請從下拉式清單中選取 [QueryString],然後在 [QueryStringField] 文本框中輸入 CategoryID,如圖 9 所示。 按一下 [完成] 以完成精靈。
圖 9:使用 CategoryID
categoryID 參數的 Querystring 欄位 (按兩下即可檢視完整大小的影像)
完成精靈之後,Visual Studio 會將對應的 BoundFields 和 CheckBoxField 新增至 GridView 中的產品數據欄位。 拿掉 、UnitPrice
、 和 SupplierName
BoundFields。ProductName
自定義這三個 BoundFields HeaderText
屬性,分別讀取 Product、Price 和 Supplier。 將 UnitPrice
BoundField 格式化為貨幣。
接下來,新增 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
會顯示選取的產品詳細數據。 開啟 ProductDetails.aspx
[詳細數據檢視],然後將 [工具箱] 拖曳至 Designer。 將 DetailsView s ID
屬性設定為 ProductInfo
,並清除其 Height
和 Width
屬性值。 從智慧標記中,將 DetailsView 系結至名為 ProductDataSource
的新 ObjectDataSource,將 ObjectDataSource 設定為從 ProductsBLL
類別 s GetProductByProductID(productID)
方法提取其數據。 如同在步驟 2 和 3 中建立的先前網頁,請將 UPDATE、INSERT 和 DELETE 索引標籤中的下拉式清單設定為 (None) 。
圖 12:將 ObjectDataSource 設定為使用 GetProductByProductID(productID)
方法 (按兩下即可檢視大小完整的映像)
[設定數據源精靈] 的最後一個步驟會提示 productID 參數的來源。 由於此數據會通過 querystring 欄位 ProductID
,請將下拉式清單設定為 QueryString,並將 QueryStringField 文字框設定為 ProductID。 最後,按兩下 [完成] 按鈕以完成精靈。
圖 13:將 productID 參數設定為從 ProductID
Querystring 欄位提取其值 (按兩下即可檢視大小完整的影像)
完成 [設定數據源精靈] 之後,Visual Studio 會在 [詳細數據] 欄位中建立對應的 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
,然後按兩下 [檢視產品] 以取得 [供應專案] 類別。 從飲料產品清單中,按兩下 Chai Tea 的 [檢視詳細資料] 連結。 這會帶您前往 ProductDetails.aspx?ProductID=1
,其中顯示 Chai Tea 的詳細數據 (請參閱圖 14) 。
圖 14:Chai Tea s 供應商、類別、價格和其他資訊隨即顯示 (按兩下即可檢視大小完整的影像)
步驟 5:了解網站地圖提供者的內部工作
網站地圖會以構成階層的 SiteMapNode
實例集合的形式呈現在網頁伺服器的記憶體中。 必須只有一個根目錄,所有非根節點都必須只有一個父節點,而且所有節點可能都有任意數目的子節點。 每個 SiteMapNode
物件都代表網站結構中的區段;這些區段通常有對應的網頁。 因此, SiteMapNode
類別 具有的屬性,例如 Title
、 Url
和 Description
,可提供 所表示區段 SiteMapNode
的資訊。 另外還有一個屬性可唯一 Key
識別 SiteMapNode
階層中的每個屬性,以及用來建立此階層 ChildNodes
、 ParentNode
、 NextSibling
、 PreviousSibling
等等的屬性。
圖 15 顯示圖 1 中的一般網站地圖結構,但實作詳細數據會更精細地繪製出來。
圖 15:每個 SiteMapNode
屬性都有類似 Title
、 Url
、、 Key
等等 (按鍵即可檢視完整大小的影像)
網站地圖可透過 SiteMap
命名空間中的 System.Web
類別來存取。 這個類別 s RootNode
屬性會傳回網站地圖的根 SiteMapNode
實例; CurrentNode
傳回 SiteMapNode
其 Url
屬性符合目前要求頁面 URL 的 。 ASP.NET 2.0 s 導覽 Web 控件會在內部使用此類別。
SiteMap
存取類別的屬性時,它必須將網站地圖結構從某些持續性媒體串行化為記憶體。 不過,網站地圖串行化邏輯不會硬式編碼到 SiteMap
類別中。 相反地,類別會在 SiteMap
運行時間決定要用於串行化的月臺地圖 提供者 。 根據預設,會 XmlSiteMapProvider
使用 類別 ,它會從正確格式化的 XML 檔案讀取網站地圖結構。 不過,我們只需要一些工作,就可以建立自己的自定義網站地圖提供者。
所有網站地圖提供者都必須衍生自 SiteMapProvider
類別,其中包含網站地圖提供者所需的基本方法和屬性,但省略許多實作詳細數據。 第二個類別 StaticSiteMapProvider
會 SiteMapProvider
擴充 類別,並包含更強固的所需功能實作。 在內部,會將 StaticSiteMapProvider
網站地圖的實例儲存 SiteMapNode
在 中 Hashtable
,並提供 之類的 AddNode(child, parent)
方法, RemoveNode(siteMapNode),
並將 Clear()
新增和移除 SiteMapNode
至內部 Hashtable
。 XmlSiteMapProvider
衍生自 StaticSiteMapProvider
。
建立擴充 StaticSiteMapProvider
的自訂網站地圖提供者時,必須覆寫兩個抽象方法: BuildSiteMap
和 GetRootNodeCore
。 BuildSiteMap
如其名稱所示,負責從永續性記憶體載入網站地圖結構,並在記憶體中建構。 GetRootNodeCore
會傳回網站地圖中的根節點。
Web 應用程式必須先在應用程式的組態中註冊,才能使用網站地圖提供者。 根據預設,類別 XmlSiteMapProvider
會使用 名稱 AspNetXmlSiteMapProvider
註冊。 若要註冊其他網站地圖提供者,請將下列標記新增至 Web.config
:
<configuration>
<system.web>
...
<siteMap defaultProvider="defaultProviderName">
<providers>
<add name="name" type="type" />
</providers>
</siteMap>
</system.web>
</configuration>
名稱值會在類型指定網站地圖提供者的完整類型名稱時,將人類可讀取的名稱指派給提供者。 我們將在建立自定義網站地圖提供者之後,探索步驟 7 中名稱和類型值的具體值。
網站地圖提供者類別會在第一次從 SiteMap
類別存取時具現化,並在 Web 應用程式的存留期內保留在記憶體中。 由於網站地圖提供者只有一個實例可從多個並行網站訪客叫用,因此提供者的方法必須 安全線程。
基於效能和延展性考慮,請務必快取記憶體內部網站地圖結構,並傳回此快取結構,而不是每次叫用 方法時 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 中,我要求您在資料夾中新增資料夾 App_Code
- 將新類別新增CustomProviders
至名為 的NorthwindSiteMapProvider
這個資料夾。 將下列程式碼新增至 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
讓我們從探索這個類別的 BuildSiteMap
方法開始,其開頭為 lock
語句。 lock
語句一次只允許一個線程輸入,藉此串行化其程式代碼的存取權,並防止兩個並行線程彼此逐步執行。
類別層級 SiteMapNode
變數 root
可用來快取網站地圖結構。 第一次建構網站地圖時,或第一次修改基礎數據之後,將會是 Nothing
,root
並建構網站地圖結構。 網站地圖的根節點會在建構程式期間指派給 root
,因此下次呼叫 root
此方法時,將不會是 Nothing
。 因此,只要 root
不是 Nothing
網站地圖結構,就會傳回給呼叫端,而不需要重新建立它。
如果 root 為 Nothing
,則會從產品和類別資訊建立網站地圖結構。 網站地圖是藉由建立 SiteMapNode
實例,然後透過對類別 s AddNode
方法的呼叫StaticSiteMapProvider
來形成階層來建置。 AddNode
會執行內部記事,將各種 SiteMapNode
實例儲存在 中 Hashtable
。 開始建構階層之前,我們會先呼叫 Clear
方法,從內部 Hashtable
清除元素。 接下來,類別 ProductsBLL
s GetProducts
方法和結果 ProductsDataTable
會儲存在局部變數中。
網站地圖的建構從建立根節點開始,並將它指派給 root
。 這裡所使用的 s 建構函式多SiteMapNode
載,並在整個過程中BuildSiteMap
傳遞下列資訊:
- 網站地圖提供者的參考 (
Me
) 。 SiteMapNode
sKey
。 每個的必要值都必須是唯一SiteMapNode
的。SiteMapNode
sUrl
。Url
是選擇性的,但如果提供,每個SiteMapNode
值都必須是唯一的Url
。- 需要
SiteMapNode
的 。Title
方法呼叫會將 AddNode(root)
SiteMapNode
root
新增至網站地圖作為根目錄。 接下來,會列舉 中的ProductsDataTable
每個 ProductRow
。 如果目前產品類別已有, SiteMapNode
則會加以參考。 否則,會建立類別的新 SiteMapNode
,並透過 AddNode(categoryNode, root)
方法呼叫新增為 的SiteMapNode``root
子系。 找到或建立適當的類別SiteMapNode
節點之後,會為目前的產品建立 ,SiteMapNode
並透過 AddNode(productNode, categoryNode)
新增為類別SiteMapNode
的子系。 請注意,類別 SiteMapNode
s Url
屬性值是在~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
指派 ~/SiteMapNode/ProductDetails.aspx?ProductID=productID
product SiteMapNode
s Url
屬性時。
注意
具有其CategoryID
資料庫NULL
值的這些產品會分組在屬性設定為 None 且其屬性設定為空字串的Url
類別SiteMapNode
Title
之下。 我決定設定 Url
為空字串, ProductBLL
因為類別 s GetProductsByCategory(categoryID)
方法目前缺少只傳回具有 NULL
CategoryID
值的這些產品的功能。 此外,我想要示範導覽控件如何呈現 SiteMapNode
缺少其 Url
屬性值的 。 我們鼓勵您擴充本教學課程,讓 None SiteMapNode
Url
屬性指向 ProductsByCategory.aspx
,但只顯示具有 NULL
CategoryID
值的產品。
建構網站地圖之後,會使用透過 物件對 Categories
和 Products
數據表 AggregateCacheDependency
的 SQL 快取相依性,將任意物件新增至數據快取。 我們在上一個教學課程中探索了 使用 SQL 快取相依性:使用 SQL 快取相依性。 不過,自定義網站地圖提供者會使用我們尚未探索的數據快取 Insert
方法多載。 此多載接受做為其最終輸入參數,這是從快取中移除物件時所呼叫的委派。 具體而言,我們會傳入指向 類別中NorthwindSiteMapProvider
進一步定義的 方法的新CacheItemRemovedCallback
委派OnSiteMapChanged
。
注意
網站地圖的記憶體內部表示會透過類別層級變數 root
快取。 因為自定義網站地圖提供者類別只有一個實例,而且因為該實例會在 Web 應用程式中的所有線程之間共用,所以這個類別變數會做為快取。 方法BuildSiteMap
也會使用數據快取,但只有在 或 Products
數據表中的Categories
基礎資料庫數據變更時,才會收到通知。 請注意,放入數據快取中的值只是目前的日期和時間。 實際的網站地圖數據 不會 放入數據快取中。
方法 BuildSiteMap
會傳回網站地圖的根節點來完成。
其餘的方法相當簡單。 GetRootNodeCore
負責傳回根節點。 由於 BuildSiteMap
會傳回根目錄, GetRootNodeCore
因此只會傳 BuildSiteMap
回 傳回值。 方法會在 OnSiteMapChanged
移除快取項目時設定 root
回 Nothing
。 將根設定回 Nothing
時,下次 BuildSiteMap
叫用時,將會重建網站地圖結構。 最後,如果這類值存在, CachedDate
屬性會傳回儲存在數據快取中的日期和時間值。 此頁面開發人員可以使用此屬性來判斷網站地圖數據上次快取的時間。
步驟 7:註冊NorthwindSiteMapProvider
為了讓 Web 應用程式使用 NorthwindSiteMapProvider
在步驟 6 中建立的網站地圖提供者,我們必須在 <siteMap>
的 Web.config
區段中註冊它。 具體而言,請在 中的 <system.web>
Web.config
元素內新增下列標記:
<siteMap defaultProvider="AspNetXmlSiteMapProvider">
<providers>
<add name="Northwind" type="NorthwindSiteMapProvider" />
</providers>
</siteMap>
此標記會執行兩件事:首先,它會指出內 AspNetXmlSiteMapProvider
建是預設的網站地圖提供者;其次,它會使用人類易記的名稱 Northwind 註冊在步驟 6 中建立的自定義網站地圖提供者。
注意
對於位於應用程式 App_Code
資料夾的網站地圖提供者,屬性的值 type
只是類別名稱。 或者,自定義網站地圖提供者可能已在個別的類別庫專案中建立,並將編譯的元件放在 Web 應用程式目錄中 /Bin
。 在此情況下,type
屬性值會是 Namespace。ClassName,AssemblyName 。
更新 Web.config
之後,請花點時間從瀏覽器中的教學課程檢視任何頁面。 請注意,左側導覽介面仍會顯示 中 Web.sitemap
定義的區段和教學課程。 這是因為我們保留 AspNetXmlSiteMapProvider
為預設提供者。 若要建立使用 的 NorthwindSiteMapProvider
導覽使用者介面元素,我們必須明確指定應該使用 Northwind 網站地圖提供者。 我們將瞭解如何在步驟 8 中完成此作業。
步驟 8:使用自訂網站地圖提供者顯示網站地圖資訊
在 中 Web.config
建立並註冊自定義網站地圖提供者之後,我們即可將導覽控件新增至資料夾中的 Default.aspx
、 ProductsByCategory.aspx
和 ProductDetails.aspx
頁面 SiteMapProvider
。 從開啟頁面開始,Default.aspx
並將 從 [工具箱] 拖曳SiteMapPath
到 Designer。 SiteMapPath 控制件位於 [工具箱] 的 [瀏覽] 區段中。
圖 16:新增 SiteMapPath 以 Default.aspx
(按兩下即可檢視完整大小的影像)
SiteMapPath 控件會顯示階層連結,指出網站地圖中的目前頁面位置。 我們已在主 版頁面和網站導覽 教學課程中,將 SiteMapPath 新增回主版頁面頂端。
請花點時間透過瀏覽器檢視此頁面。 圖 16 中新增的 SiteMapPath 會使用預設的網站地圖提供者,從 Web.sitemap
提取其數據。 因此,階層鏈接會顯示首頁 > 自定義網站地圖,就像右上角的階層連結一樣。
圖 17:階層連結使用默認網站地圖提供者, (按兩下即可檢視完整大小的影像)
若要在圖 16 中新增 SiteMapPath,請使用我們在步驟 6 中建立的自定義網站地圖提供者,將其 SiteMapProvider
屬性設定為 Northwind,也就是我們在 中Web.config
指派給 NorthwindSiteMapProvider
的名稱。 不幸的是,Designer 會繼續使用預設的網站地圖提供者,但如果您在進行此屬性變更之後瀏覽頁面,您會看到階層連結現在使用自定義網站地圖提供者。
圖 18:階層鏈接現在使用自訂網站地圖提供者 NorthwindSiteMapProvider
, (按兩下即可檢視完整大小的影像)
SiteMapPath 控制件會在 ProductsByCategory.aspx
和 ProductDetails.aspx
頁面中顯示功能更實用的使用者介面。 將 SiteMapPath 新增至這些頁面,將兩者中的 屬性設定 SiteMapProvider
為 Northwind。 從 Default.aspx
按兩下 [檢視產品] 連結的 [建立者],然後在Chai Tea的 [檢視詳細資料] 連結上。 如圖 19 所示,階層連結包含目前的網站地圖區段, ( Chai Tea ) 及其祖系:飲料和所有類別 。
圖 19:階層連結現在使用自定義網站地圖提供者 NorthwindSiteMapProvider
, (按兩下即可檢視完整大小的影像)
除了 SiteMapPath 之外,也可以使用其他導覽使用者介面元素,例如 Menu 和 TreeView 控制件。 Default.aspx
本教學課程下載中的、 ProductsByCategory.aspx
和 ProductDetails.aspx
頁面,例如,所有包含功能表控件 (請參閱圖 20) 。 如需 ASP.NET 2.0 中流覽控件和網站導覽系統的深入探討,請參閱 ASP.NET 2.0 快速入門中的 ASP.NET 2.0複雜網站導覽功能和網站導覽控件一節。
圖 20:功能表控件 清單 每個類別和產品 (按單擊即可檢視完整大小的影像)
如本教學課程稍早所述,網站地圖結構可以透過 SiteMap
類別以程式設計方式存取。 下列程式代碼會傳回預設提供者的根 SiteMapNode
目錄:
Dim root As SiteMapNode = SiteMap.RootNode
AspNetXmlSiteMapProvider
由於是應用程式的預設提供者,因此上述程式代碼會傳回 中Web.sitemap
定義的根節點。 若要參考預設以外的網站地圖提供者,請使用 SiteMap
類別的 Providers
屬性 ,如下所示:
Dim root As SiteMapNode = SiteMap.Providers("name").RootNode
其中 name 是 Northwind ( 自定義網站地圖提供者的名稱,在我們的 Web 應用程式) 。
若要存取網站地圖提供者特定的成員,請使用 SiteMap.Providers["name"]
來擷取提供者實例,然後將它轉換成適當的類型。 例如,若要在 NorthwindSiteMapProvider
ASP.NET 頁面中顯示 s CachedDate
屬性,請使用下列程式代碼:
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 快取相依性功能。 流覽 Default.aspx
、 ProductsByCategory.aspx
和 ProductDetails.aspx
頁面之後,請移至 [編輯]、[插入] 和 [刪除] 區段中的其中一個教學課程,然後編輯類別或產品名稱。 然後返回資料夾中的其中一個頁面 SiteMapProvider
。 假設輪詢機制已經過足夠的時間,以記下基礎資料庫的變更,則網站地圖應該更新以顯示新的產品或類別名稱。
摘要
ASP.NET 2.0 s 網站地圖功能包括類別 SiteMap
、一些內建導覽 Web 控制件,以及預期網站地圖資訊保存到 XML 檔案的預設網站地圖提供者。 若要使用來自某些其他來源的網站地圖資訊,例如從資料庫、應用程式架構或遠端 Web 服務,我們需要建立自定義網站地圖提供者。 這牽涉到建立直接或間接衍生自 類別的 SiteMapProvider
類別。
在本教學課程中,我們瞭解如何建立自定義網站地圖提供者,以從應用程式架構中擷取的產品和類別資訊為基礎的網站地圖。 我們的提供者擴充 了 StaticSiteMapProvider
類別,並需要建立 BuildSiteMap
方法來擷取數據、建構網站地圖階層,並在類別層級變數中快取產生的結構。 我們使用 SQL 快取相依性搭配回呼函式,在修改基礎 Categories
或 Products
數據時使快取結構失效。
快樂的程序設計!
深入閱讀
如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源:
關於作者
Scott Mitchell 是 1998 年以來,1998 年與 Microsoft Web 技術合作的 七篇 ASP/ASP.NET 書籍和 4GuysFromRolla.com 作者。 Scott 是獨立的顧問、訓練者和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格連到,也可以透過其部落格來存取,網址為 http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者所檢閱。 本教學課程的首席檢閱者是 Dave Gardner、Zack Jones、Teresa Murphy 和 Bernadette 一節。 想要檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行放在 mitchell@4GuysFromRolla.com。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應