濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider

在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider

熱門標(biāo)簽:臨沂智能電話機(jī)器人加盟 網(wǎng)絡(luò)電話外呼系統(tǒng)上海 蘇州如何辦理400電話 外呼電話機(jī)器人成本 地圖標(biāo)注軟件免費(fèi)下載 聯(lián)通官網(wǎng)400電話辦理 400電話辦理怎么樣 西寧呼叫中心外呼系統(tǒng)線路商 百應(yīng)電話機(jī)器人外呼系統(tǒng)

導(dǎo)言:

  ASP.NET 2.0的網(wǎng)站地圖(site map)功能允許頁(yè)面開發(fā)者在一些持久介質(zhì)(persistent medium),比如一個(gè)XML文件里,自己定義一個(gè)web程序的site map.一旦定義了之后,我們可以通過System.Web命名空間的SiteMap class類或某個(gè)Web導(dǎo)航控件,比如SiteMapPath, Menu, 或TreeView來(lái)對(duì)其進(jìn)行訪問。site map系統(tǒng)使用的是provider model模式,所以可以創(chuàng)建不同的site map,并將其應(yīng)用到一個(gè)web應(yīng)用程序。ASP.NET 2.0默認(rèn)的site map provider,其結(jié)構(gòu)為一個(gè)XML文件。在教程《Master Pages and Site Navigation》里我們創(chuàng)建了一個(gè)Web.sitemap文件,它就包含了這種結(jié)構(gòu),并且在教程的每一個(gè)新部分里我們都要更新其XML.

  當(dāng)site map的結(jié)構(gòu)是靜態(tài)的時(shí)候,默認(rèn)的這種基于XML(XML-based)的site map provider工作正常,就像本系列教程一樣。但是在很多時(shí)候我們需要?jiǎng)討B(tài)的site map.如圖1的site map,每個(gè)種類以及屬于該種類的產(chǎn)品在網(wǎng)站的結(jié)構(gòu)里做層次狀體系分布。在該site map里,當(dāng)訪問根目錄的web頁(yè)面時(shí),將列出所有的種類;再訪問某個(gè)具體的種類的根目錄時(shí),將列出屬于該種類的所有產(chǎn)品;再訪問某個(gè)具體的產(chǎn)品時(shí)將列出該產(chǎn)品的詳細(xì)信息。


圖1:Categories 和 Products構(gòu)成了Site Map的層次結(jié)構(gòu)

  這種基于category 和product的結(jié)構(gòu)可以通過"硬編碼"的方式添加到Web.sitemap文件.每當(dāng)對(duì)category 或 product進(jìn)行添加、刪除、重命名等操作時(shí),都需要對(duì)該文件進(jìn)行更新。很自然的,如果其結(jié)構(gòu)是通過數(shù)據(jù)庫(kù),或更理想地,是從業(yè)務(wù)邏輯層來(lái)獲取的,那么對(duì)site map的維護(hù)是很簡(jiǎn)單的。那樣的話,只要對(duì)products 和 categories進(jìn)行添加、刪除、重命名時(shí),site map會(huì)自動(dòng)的更新以反應(yīng)這些變化。

  由于ASP.NET 2.0的site map是建立在provider模式的基礎(chǔ)上的,因此我們可以創(chuàng)建一個(gè)自定義的site map provider,從數(shù)據(jù)庫(kù)或某個(gè)層來(lái)獲取數(shù)據(jù).在本文,我們創(chuàng)建的provider將從業(yè)務(wù)邏輯層獲取數(shù)據(jù)。讓我們開始吧!

  注意:本文創(chuàng)建的用戶定制site map provider僅僅依賴于系統(tǒng)的層及其數(shù)據(jù)模式(data model)。Jeff Prosise的文章《Storing Site Maps in SQL Server》(http://msdn.microsoft.com/msdnmag/issues/05/06/WickedCode/)
和《The SQL Site Map Provider You've Been Waiting For》
(http://msdn.microsoft.com/msdnmag/issues/06/02/wickedcode/default.aspx)
考察了將site map數(shù)據(jù)存儲(chǔ)在SQL Server的方法。

第一步:創(chuàng)建用戶定制Site Map Provider頁(yè)面

在創(chuàng)建用戶定制Site Map Provider之前,先添加本章將用到的ASP.NET頁(yè)面。首先添加一個(gè)名為SiteMapProvider的文件夾;然后在文件夾里添加如下所示的頁(yè)面確保采用母版Site.master:

Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx

同樣,在App_Code文件夾里添加CustomProviders


圖2:添加相關(guān)的ASP.NET頁(yè)面.

由于這部分只有一篇文章,沒有必要使Default.aspx頁(yè)面列出本部分的文章;我們將在Default.aspx里用一個(gè)GridView控件來(lái)列出categories,在第二步里探討.

然后,更新Web.sitemap使其包含對(duì)Default.aspx頁(yè)面的引用。特別的,在“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的更新后,花點(diǎn)時(shí)間在瀏覽器里登錄頁(yè)面,在左面的菜單里包含了本教程的條目。


圖3:Site Map現(xiàn)在包含了本章的條目

  本教程主要考察如何創(chuàng)建一個(gè)用戶自定義的site map provider,及對(duì)設(shè)置web應(yīng)用程序進(jìn)行包含該site map provider.具體來(lái)講,它返回的網(wǎng)站地圖(site map)不僅包含了根節(jié)點(diǎn),而且包含每個(gè)種類節(jié)點(diǎn)和產(chǎn)品節(jié)點(diǎn),就像圖1顯示的那樣??偟膩?lái)說(shuō),網(wǎng)站地圖里的每一個(gè)節(jié)點(diǎn)都對(duì)應(yīng)一個(gè)具體的URL.就我們的網(wǎng)站地圖而言,根節(jié)點(diǎn)的URL為~/SiteMapProvider/Default.aspx,它列出了所有產(chǎn)品和種類;每個(gè)種類節(jié)點(diǎn)對(duì)應(yīng)的URL 為~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID,它根據(jù)指定的categoryID列出該種類的所有產(chǎn)品;最后,每個(gè)產(chǎn)品對(duì)應(yīng)的URL為~/SiteMapProvider/ProductDetails.aspx?ProductID=productID, 它根據(jù)指定的productID值,列出該產(chǎn)品的詳細(xì)信息。

  首先,讓我們創(chuàng)建Default.aspx, ProductsByCategory.aspx和ProductDetails.aspx頁(yè)面。我們將分別在第二、三、四步創(chuàng)建這些頁(yè)面.因?yàn)楸疚牡闹攸c(diǎn)是site map providers,并且這種主/從頁(yè)面在前面的教程里已經(jīng)討論過了,我們?cè)诘?到第4步將一筆帶過,如果你對(duì)這種主/從頁(yè)面頁(yè)面不是很了解的話,請(qǐng)參考前面的教程之9《Master/Detail Filtering Across Two Pages》.

第二步:將Categories顯示出來(lái)

  打開文件夾SiteMapProvider里的Default.aspx頁(yè)面,在設(shè)計(jì)模式里從工具箱拖一個(gè) GridView控件到頁(yè)面,設(shè)置其ID為Categories.從其智能標(biāo)簽里,將其綁定到一個(gè)名為CategoriesDataSource的ObjectDataSource,設(shè)置其使用CategoriesBLL類的 GetCategories方法。因?yàn)樵揋ridView控件只是顯示categories而不修改數(shù)據(jù),因此在UPDATE, INSERT和 DELETE標(biāo)簽里選“(None)”.


圖4:設(shè)置ObjectDataSource使用GetCategories方法返回Categories


圖5:在UPDATE, INSERT和DELETE標(biāo)簽里選“(None)”

  設(shè)置完成后,Visual Studio會(huì)自動(dòng)的添加CategoryID, CategoryName, Description, NumberOfProducts 和 BrochurePath這些綁定列(BoundField),修改GridView,使其只包含CategoryName 和 Description兩列,且將CategoryName綁定列的HeaderText屬性改為“Category”.

  然后,添加一個(gè)HyperLinkField,將其放在最左邊,設(shè)其DataNavigateUrlFields屬性為 CategoryID;DataNavigateUrlFormatString 屬性為 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0};再將Text屬性設(shè)置為“View Products”.


圖6:為GridView添加一個(gè)HyperLinkField

創(chuàng)建完ObjectDataSource并定制GridView控件的列后,這2個(gè)控件的聲明代碼看起來(lái)應(yīng)該和下面的差不多:

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頁(yè)面,點(diǎn)某個(gè)類的“View Products”鏈接,將會(huì)轉(zhuǎn)到ProductsByCategory.aspx?CategoryID=categoryID頁(yè)面,該頁(yè)面我們將在第三步新建。


圖7:每個(gè)類都有一個(gè)“View Products”鏈接

第三步:顯示指定類的所有產(chǎn)品

  打開ProductsByCategory.aspx頁(yè)面并添加一個(gè)GridView控件,設(shè)其ID為ProductsByCategory.從其智能標(biāo)簽,將其綁定到一個(gè)名為ProductsByCategoryDataSource的ObjectDataSource;設(shè)置它使用ProductsBLL類的 GetProductsByCategoryID(categoryID)方法;在UPDATE, INSERT,和 DELETE標(biāo)簽里選擇“(None)”.


圖8:使用ProductsBLL類的GetProductsByCategoryID(categoryID)方法

  設(shè)置向?qū)У淖詈笠徊绞侵付╟ategoryID的參數(shù)來(lái)源,因?yàn)榇诵畔⑹峭ㄟ^查詢字符串(querystring field)CategoryID來(lái)傳遞的,因此在參數(shù)來(lái)源里選QueryString,在QueryStringField里輸入“CategoryID”;如圖9所示,點(diǎn)Finish完成設(shè)置.


圖9:為參數(shù)categoryID指定CategoryID Querystring Field

  完成設(shè)置后,Visual Studio將為GridView添加相應(yīng)的綁定列以及CheckBo列;將除ProductName, UnitPrice, SupplierName外的列刪除掉。將這3個(gè)列的HeaderText屬性分別設(shè)置為“Product”, “Price”, and “Supplier”, 將UnitPrice列格式化為貨幣形式.

  然后,添加一個(gè)HyperLinkField列,并將其放在最左邊;設(shè)其Text屬性為“View Details”,設(shè)其DataNavigateUrlFields屬性為ProductID;其DataNavigateUrlFormatString屬性為 ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.


圖10:添加一個(gè)“View Details” 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>

  返回來(lái)登錄Default.aspx頁(yè)面,點(diǎn)Beverages(飲料)的“View Products”鏈接,這將轉(zhuǎn)到ProductsByCategory.aspx?CategoryID=1頁(yè)面,顯示飲料類的所有產(chǎn)品的names, prices, 和 suppliers信息(見圖11)。盡管改進(jìn)該頁(yè)面吧,添加一個(gè)鏈接以方便用戶返回上一頁(yè)(Default.aspx) .還可以添加一個(gè)DetailsView 或 FormView控件來(lái)顯示該種類的名稱和描述。


圖11:顯示Beverages類的Names, Prices, Suppliers信息

第四步:顯示產(chǎn)品的詳細(xì)信息

  最后要?jiǎng)?chuàng)建的頁(yè)面—ProductDetails.aspx,是用來(lái)顯示指定產(chǎn)品的詳細(xì)信息的。打開ProductDetails.aspx頁(yè)面,從工具箱拖一個(gè)DetailsView控件到頁(yè)面,設(shè)置其ID為ProductInfo,并清除其Height 和 Width屬性值。在其智能標(biāo)簽里,綁定到一個(gè)名為ProductDataSource的ObjectDataSource,設(shè)置該ObjectDataSource使用ProductsBLL類的GetProductByProductID(productID)方法。在UPDATE, INSERT,和DELETE標(biāo)簽里選“(None)”.


圖12:設(shè)置該ObjectDataSource控件調(diào)用GetProductByProductID(productID)方法

  最后,需要設(shè)置參數(shù)productID的來(lái)源,由于數(shù)據(jù)通過查詢字符串ProductID來(lái)傳遞,在參數(shù)源下拉列表里選QueryString,在QueryStringField對(duì)話框里輸入“ProductID”. 最后,點(diǎn)Finish按鈕完成設(shè)置。


圖13:設(shè)置參數(shù)productID來(lái)源于查詢字符串

  完成設(shè)置后,Visual Studio會(huì)為DetailsView控件添加相應(yīng)的綁定列和CheckBox列,移除ProductID, SupplierID, 和CategoryID列,剩下的列想怎樣設(shè)就怎樣設(shè)置吧。我對(duì)界面做了些優(yōu)化,這樣的話,聲明代碼看起來(lái)像下面這樣:

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>

  來(lái)對(duì)該頁(yè)面進(jìn)行測(cè)試,返回Default.aspx頁(yè)面,點(diǎn)種類Beverages的“View Products”鏈接;再點(diǎn)產(chǎn)品Chai Tea的“View Details”鏈接。這將轉(zhuǎn)到ProductDetails.aspx?ProductID=1頁(yè)面,其顯示的是Chai Tea的詳細(xì)信息(如圖14所示).


圖14:Chai Tea的Supplier, Category, Price等信息顯示出來(lái)了

第五步:理解Site Map Provider的內(nèi)部處理機(jī)制

  site map呈現(xiàn)的是源于某種層次結(jié)構(gòu)的SiteMapNode實(shí)例集(a  collection of SiteMapNode instances)。其必須有一個(gè)根節(jié)點(diǎn),所有的非根節(jié)點(diǎn)都有一個(gè)父節(jié)點(diǎn),且每個(gè)節(jié)點(diǎn)都可以有任意數(shù)量的子節(jié)點(diǎn).每個(gè)SiteMapNode對(duì)象對(duì)應(yīng)的是website體系結(jié)構(gòu)的某個(gè)部分。這些部分通常都有對(duì)應(yīng)的web頁(yè)面,因此,SiteMapNode class類有像Title, Url, 和 Description這樣的屬性,它們用來(lái)描述SiteMapNode所對(duì)應(yīng)部分的相關(guān)信息。
還有一個(gè)Key屬性用來(lái)專門唯一的標(biāo)識(shí)這些SiteMapNode;除此以外,還有ChildNodes, ParentNode, NextSibling, PreviousSibling等等.


圖15顯示的是對(duì)應(yīng)于圖1的site map的總體結(jié)構(gòu),只是更細(xì)化了而已.

   可以通過命名空間System.Web的SiteMap class類來(lái)訪問site map;該類的RootNode屬性返回網(wǎng)站地圖的根目錄的SiteMapNode實(shí)例;CurrentNode屬性返回的是這種SiteMapNode,其Url屬性剛好與當(dāng)前請(qǐng)求頁(yè)面的URL匹配.ASP.NET 2.0的Web導(dǎo)航控件的內(nèi)部就會(huì)用到SiteMap class類.

  當(dāng)訪問SiteMap class類的屬性時(shí),必須將網(wǎng)站地圖的層次結(jié)構(gòu)從某個(gè)介質(zhì)傳入內(nèi)存(memory).SiteMap class類并不是通過“硬編碼”的方式來(lái)處理網(wǎng)站地圖的邏輯關(guān)系,而是通過某種site map provider來(lái)工作.在默認(rèn)情況下,使用的是XmlSiteMapProvider class類,它從一個(gè)標(biāo)準(zhǔn)的XML文件讀取網(wǎng)站地圖的結(jié)構(gòu).不過,我們稍微做點(diǎn)工作就可以創(chuàng)建自己的site map provider.

  所有的site map providers都繼承自SiteMapProvider class類,該類包含了site map providers要用到的最基本的方法和屬性,不過略去了很多執(zhí)行細(xì)節(jié).site map providers要用到的第二個(gè)類是StaticSiteMapProvider class類,它對(duì)SiteMapProvider class類進(jìn)行了擴(kuò)充,包含了更多的必要的函數(shù).在其內(nèi)部,StaticSiteMapProvider將網(wǎng)站地圖的SiteMapNode實(shí)例存儲(chǔ)在一個(gè)哈希表(Hashtable)里,并包含了AddNode(child, parent), RemoveNode(siteMapNode), Clear()等方法,以對(duì)哈希表里的SiteMapNodes執(zhí)行添加、刪除等操作.另外,XmlSiteMapProvider也繼承自StaticSiteMapProvider.

  當(dāng)創(chuàng)建自定義的site map provider時(shí),要對(duì)StaticSiteMapProvider進(jìn)行擴(kuò)充,重寫(overrid)2個(gè)抽象方法——BuildSiteMap 和 GetRootNodeCore. 對(duì)BuildSiteMap而言,就像它的名字暗示的那樣,將網(wǎng)站地圖的結(jié)構(gòu)從某種介質(zhì)里按層次結(jié)構(gòu)裝載進(jìn)內(nèi)存;而GetRootNodeCore返回的是網(wǎng)站地圖的根目錄.

  在使用某個(gè)site map provider時(shí),需要在應(yīng)用程序的配置文件里進(jìn)行注冊(cè)(registered)
.默認(rèn)情況下,XmlSiteMapProvider class類被注冊(cè)為AspNetXmlSiteMapProvider.為對(duì)額外添加的site map providers進(jìn)行注冊(cè),可以在Web.config文件里添加如下的代碼:

configuration>
 system.web>
 ...

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

  name屬性可以為site map provider指派一個(gè)易讀的名稱;type屬性決定了該site map provider的類型.當(dāng)創(chuàng)建完我們定制的site map provider后,我們將在第七步為name 和type屬性賦值.

  當(dāng)?shù)谝淮螐腟iteMap class類訪問site map provider時(shí),site map provider class類都應(yīng)該被實(shí)例化,并在web應(yīng)用程序的整個(gè)生命周期里都駐留在內(nèi)存.

  基于性能等方面的考慮,我們應(yīng)該將對(duì)駐留在內(nèi)存里的網(wǎng)站地圖結(jié)構(gòu)進(jìn)行數(shù)據(jù)緩存,每次調(diào)用BuildSiteMap的方法時(shí),直接返回緩存的數(shù)據(jù)而不用重新檢索數(shù)據(jù).在任何情況下,如果我們不對(duì)BuildSiteMap對(duì)應(yīng)的網(wǎng)站結(jié)構(gòu)進(jìn)行緩存的話,每次調(diào)用時(shí),我們都需要通過“層”來(lái)重新檢索產(chǎn)品和種類的信息(這將最終導(dǎo)致對(duì)數(shù)據(jù)庫(kù)的查詢).我們?cè)谇懊娴木彺嬲鹿?jié)探討過緩存數(shù)據(jù)“過時(shí)”的問題,為此,我們要么使用基于時(shí)間,要么使用基于SQL cache dependency的緩存技術(shù).

  注意:一個(gè)site map provider可以任意地重寫(override)Initialize method方法.Initialize 方法是當(dāng)site map provider第一次實(shí)例化的時(shí)候被調(diào)用的,并可以將我們?cè)赪eb.config 文件的add>元素里賦值的用戶自定義屬性值傳遞給它,比如:add name="name" type="type" customAttribute="value" />.當(dāng)一個(gè)頁(yè)面開發(fā)者希望指定各種與site map provider相關(guān)的設(shè)置,而又不希望修改site map provider的代碼的時(shí)候,這樣做很有用.比如,假如我們希望不通過“層”而直接從數(shù)據(jù)庫(kù)讀取category 和 products的數(shù)據(jù)時(shí),我們當(dāng)然希望頁(yè)面開發(fā)者調(diào)用Web.config文件里的數(shù)據(jù)庫(kù)連接字符串,而不使用site map provider代碼里的“硬編碼”值.我們不打算在第六步創(chuàng)建的自定義site map provider里重寫Initialize方法.見Jeff Prosise的文章《Storing Site Maps in SQL Server》(http://msdn.microsoft.com/msdnmag/issues/05/06/WickedCode/)

第六步:創(chuàng)建自定義的Site Map Provider

  要想創(chuàng)建一個(gè)自定義的site map provider來(lái)構(gòu)建源于Northwind數(shù)據(jù)庫(kù)里的categories 和 products信息的網(wǎng)站地圖(site map),我們需要?jiǎng)?chuàng)建一個(gè)類來(lái)擴(kuò)展StaticSiteMapProvider.在前面我們?cè)贏pp_Code文件夾里添加了一個(gè)CustomProviders文件夾,在該文件夾里添加名為NorthwindSiteMapProvider的新類,在類里添加如下的代碼:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Caching;

public class NorthwindSiteMapProvider : StaticSiteMapProvider
{
 private readonly object siteMapLock = new object();
 private SiteMapNode root = null;
 public const string CacheDependencyKey =
 "NorthwindSiteMapProviderCacheDependency";

 public override SiteMapNode BuildSiteMap()
 {
 // Use a lock to make this method thread-safe
 lock (siteMapLock)
 {
  // First, see if we already have constructed the
  // rootNode. If so, return it...
  if (root != null)
  return root;

  // We need to build the site map!
  
  // Clear out the current site map structure
  base.Clear();

  // Get the categories and products information from the database
  ProductsBLL productsAPI = new ProductsBLL();
  Northwind.ProductsDataTable products = productsAPI.GetProducts();

  // Create the root SiteMapNode
  root = new SiteMapNode(
  this, "root", "~/SiteMapProvider/Default.aspx", "All Categories");
  AddNode(root);

  // Create SiteMapNodes for the categories and products
  foreach (Northwind.ProductsRow product in products)
  {
  // Add a new category SiteMapNode, if needed
  string categoryKey, categoryName;
  bool createUrlForCategoryNode = true;
  if (product.IsCategoryIDNull())
  {
   categoryKey = "Category:None";
   categoryName = "None";
   createUrlForCategoryNode = false;
  }
  else
  {
   categoryKey = string.Concat("Category:", product.CategoryID);
   categoryName = product.CategoryName;
  }

  SiteMapNode categoryNode = FindSiteMapNodeFromKey(categoryKey);

  // Add the category SiteMapNode if it does not exist
  if (categoryNode == null)
  {
   string productsByCategoryUrl = string.Empty;
   if (createUrlForCategoryNode)
   productsByCategoryUrl =
    "~/SiteMapProvider/ProductsByCategory.aspx?CategoryID="
    + product.CategoryID;

   categoryNode = new SiteMapNode(
   this, categoryKey, productsByCategoryUrl, categoryName);
   AddNode(categoryNode, root);
  }

  // Add the product SiteMapNode
  string productUrl =
   "~/SiteMapProvider/ProductDetails.aspx?ProductID="
   + product.ProductID;
  SiteMapNode productNode = new SiteMapNode(
   this, string.Concat("Product:", product.ProductID),
   productUrl, product.ProductName);
  AddNode(productNode, categoryNode);
  }
  
  // Add a "dummy" item to the cache using a SqlCacheDependency
  // on the Products and Categories tables
  System.Web.Caching.SqlCacheDependency productsTableDependency =
  new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products");
  System.Web.Caching.SqlCacheDependency categoriesTableDependency =
  new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories");

  // Create an AggregateCacheDependency
  System.Web.Caching.AggregateCacheDependency aggregateDependencies =
  new System.Web.Caching.AggregateCacheDependency();
  aggregateDependencies.Add(productsTableDependency, categoriesTableDependency);

  // Add the item to the cache specifying a callback function
  HttpRuntime.Cache.Insert(
  CacheDependencyKey, DateTime.Now, aggregateDependencies,
  Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
  CacheItemPriority.Normal,
  new CacheItemRemovedCallback(OnSiteMapChanged));


  // Finally, return the root node
  return root;
 }
 }

 protected override SiteMapNode GetRootNodeCore()
 {
 return BuildSiteMap();
 }

 protected void OnSiteMapChanged(string key, object value, CacheItemRemovedReason reason)
 {
 lock (siteMapLock)
 {
  if (string.Compare(key, CacheDependencyKey) == 0)
  {
  // Refresh the site map
  root = null;
  }
 }
 }

 public DateTime? CachedDate
 {
 get
 {
  return HttpRuntime.Cache[CacheDependencyKey] as DateTime?;
 }
 }
}

  讓我們考察該類的BuildSiteMap方法,它有一個(gè)lock statement聲明。lock statement每次只允許“單線程操作”(one thread at a time to enter),以避免“多線程操作”之間的沖突.

  屬于“類級(jí)別”(class-level)的SiteMapNode變量—root,用來(lái)緩存網(wǎng)站地圖結(jié)構(gòu).當(dāng)網(wǎng)站地圖第一次被“結(jié)構(gòu)化”,或“源數(shù)據(jù)”發(fā)生變動(dòng)后的第一次“結(jié)構(gòu)化”時(shí),root為null值,在“結(jié)構(gòu)化”的過程中,root被賦值為網(wǎng)站地圖的根節(jié)點(diǎn);所以,當(dāng)?shù)诙握{(diào)用BuildSiteMap方法時(shí),root就不為null值了.自然,只要root不為null,直接將網(wǎng)站地圖結(jié)構(gòu)返回,而用不著重新創(chuàng)建.

  如果root為null,那么將根據(jù)product 和 category信息創(chuàng)建網(wǎng)站地圖結(jié)構(gòu).為此,先要?jiǎng)?chuàng)建一個(gè)SiteMapNode實(shí)例,再調(diào)用StaticSiteMapProvider class類的AddNode method方法來(lái)構(gòu)建網(wǎng)站地圖的層次體系,再將SiteMapNode實(shí)例存儲(chǔ)進(jìn)一個(gè)哈希表.在我們構(gòu)建層次體系之前,我們首先調(diào)用Clear method方法,將內(nèi)部的哈希表清空;然后,調(diào)用ProductsBLL class類的GetProducts()方法,把返回的ProductsDataTable存儲(chǔ)進(jìn)局部變量.

  創(chuàng)建網(wǎng)站地圖結(jié)構(gòu)從創(chuàng)建根節(jié)點(diǎn)并賦值給root開始,本章要用到的SiteMapNode's constructor重載,接受如下的信息:

對(duì)一個(gè)site map provider (this)的引用.

SiteMapNode的Key值:對(duì)每個(gè)SiteMapNode而言,這個(gè)待定值必須是唯一的.

SiteMapNode的Url值:Url為可選項(xiàng),但一旦指定的話,每個(gè)SiteMapNode的Url值必須是唯一的.

SiteMapNode的Title值:此為必選項(xiàng).

  AddNode(root) method方法將SiteMapNode root添加給網(wǎng)站地圖作為根節(jié)點(diǎn)。然后,遍歷ProductsDataTable里的所有ProductRow,如果當(dāng)前product的category所對(duì)應(yīng)的SiteMapNode已經(jīng)存在的話,那么引用該SiteMapNode;如果不存在的話,則為該category創(chuàng)建一個(gè)新的SiteMapNode,并且調(diào)用AddNode(categoryNode, root) method方法,將其作為SiteMapNode root的子節(jié)點(diǎn)進(jìn)行添加.當(dāng)找到或創(chuàng)建category對(duì)應(yīng)的SiteMapNode后,創(chuàng)建一個(gè)當(dāng)前product對(duì)應(yīng)的SiteMapNode,并通過AddNode(productNode, categoryNode)方法將其作為category SiteMapNode的子節(jié)點(diǎn)進(jìn)行添加.注意,category SiteMapNode的Url屬性為~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID;而product SiteMapNode的Url屬性為~/SiteMapNode/ProductDetails.aspx?ProductID=productID.

  注意:對(duì)那種CategoryID為NULL值的產(chǎn)品,統(tǒng)統(tǒng)將其歸為一個(gè)category,其對(duì)應(yīng)的category SiteMapNode的Title屬性可設(shè)置為“None”;Url屬性設(shè)置為空字符串。我將其Url設(shè)置為空字符串是因?yàn)镻roductBLL class類的GetProductsByCategory(categoryID)方法無(wú)法返回那些CategoryID值為NULL的產(chǎn)品.不過我鼓勵(lì)你對(duì)本教程進(jìn)行擴(kuò)展,使該category SiteMapNode的Url屬性對(duì)應(yīng)一個(gè)ProductsByCategory.aspx頁(yè)面,該頁(yè)面專門用來(lái)展示那些CategoryID為NULL的產(chǎn)品.

  當(dāng)完成site map的構(gòu)建后,將一個(gè)AggregateCacheDependency object對(duì)象添加到data cache,該對(duì)象使用基于Categories 和 Products表的SQL cache dependency技術(shù)。我們?cè)谇懊娴慕坛汤锾接戇^SQL cache dependencies,不過我們自定義的site map provider使用的是重載(overload)的data cache的Insert方法,該重載方法接受一個(gè)delegate作為輸入?yún)?shù).具體而言,我們將傳入一個(gè)CacheItemRemovedCallback delegate,其指向OnSiteMapChanged method方法,該方法定義在NorthwindSiteMapProvider class類里
注意:內(nèi)存里的site map表述是緩存在一個(gè)“類級(jí)”(class-level)變量root里的.由于只有一個(gè)site map provider的實(shí)例(instance),并且對(duì)web應(yīng)用程序的線程來(lái)說(shuō)都是共享的,這個(gè)類級(jí)變量當(dāng)作緩存服務(wù)。BuildSiteMap method方法也會(huì)用到data cache,但僅僅做作為一種探測(cè)Categories 或 Products表里的數(shù)據(jù)發(fā)生改變的方法。注意添加到data cache里的僅僅是當(dāng)前的date和time,實(shí)際的site map數(shù)據(jù)并沒有添加到data cache.

BuildSiteMap method方法最后返回網(wǎng)站地圖的根節(jié)點(diǎn).

  剩下的方法就比較簡(jiǎn)單易懂了.GetRootNodeCore方法用來(lái)返回根節(jié)點(diǎn),由于BuildSiteMap返回根節(jié)點(diǎn)root, GetRootNodeCore方法僅僅返回BuildSiteMap方法的返回值.當(dāng)緩存條碼被清除掉時(shí),OnSiteMapChanged方法將root設(shè)置為null;當(dāng)root為null的時(shí)候,當(dāng)下一次調(diào)用BuildSiteMap時(shí),將重新創(chuàng)建地圖網(wǎng)站結(jié)構(gòu).最后,如果data cache里存儲(chǔ)有date 和 time值的話,CachedDate屬性將返回這些值.頁(yè)面開發(fā)員可以用該屬性來(lái)探測(cè)site map數(shù)據(jù)最近被緩存的時(shí)間.

第七步:對(duì)NorthwindSiteMapProvider進(jìn)行登記

  為了使用我們?cè)诘诹絼?chuàng)建的NorthwindSiteMapProvider site map provider,我們需要在Web.config文件的siteMap>部分進(jìn)行注冊(cè).具體來(lái)說(shuō),將下面的代碼添加到Web.config文件的system.web>部分:

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

  上述代碼闡明了如下2個(gè)事實(shí):第一,它指明了“內(nèi)置”的AspNetXmlSiteMapProvider為默認(rèn)的site map provider;第二,它將我們?cè)诘诹絼?chuàng)建的用戶自定義site map provider進(jìn)行了注冊(cè),取名為“Northwind”.
注意:對(duì)那些位于在App_Code文件夾的site map providers而言,type屬性的值就是類的名稱.還一種方法,我們可以用一個(gè)單獨(dú)的類庫(kù)工程來(lái)創(chuàng)建自定義的site map provider,將其編譯文件放置在/Bin目錄;如果是那樣的話,type屬性就變成了“Namespace.ClassName, AssemblyName”.

  更新Web.config文件后,花點(diǎn)時(shí)間在瀏覽器里登錄本教程的任何一個(gè)頁(yè)面,我們注意到左邊的導(dǎo)航界面跟以前一樣,那是因?yàn)槲覀儼袮spNetXmlSiteMapProvider作為默認(rèn)的provider,為了使導(dǎo)航用戶界面使用我們定制的NorthwindSiteMapProvider,我們應(yīng)明確的指定使用“Northwind” site map provider,我們將在第八步完成.

第八步:使用定制的Site Map Provider來(lái)顯示網(wǎng)站地圖信息

  把我們定制的site map provider注冊(cè)到Web.config文件后,我們可以將導(dǎo)航控件添加到SiteMapProvider文件夾里的Default.aspx, ProductsByCategory.aspx, 和ProductDetails.aspx頁(yè)面.首先,打開Default.aspx頁(yè)面進(jìn)入設(shè)計(jì)模式,從工具箱拖一個(gè)SiteMapPath控件到頁(yè)面。該控件位于工具箱的導(dǎo)航區(qū)域.


圖16:為Default.aspx頁(yè)面添加一個(gè)SiteMapPath控件

  SiteMapPath控件包含一個(gè)breadcrumb,用來(lái)顯示當(dāng)前頁(yè)面在網(wǎng)站地圖里的位置。我們?cè)诘谌隆赌0屙?yè)和站點(diǎn)導(dǎo)航》里在模板頁(yè)的頂部添加了一個(gè)SiteMapPath控件.

  花點(diǎn)時(shí)間在瀏覽器里登錄頁(yè)面,我們?cè)趫D16里添加的SiteMapPath控件使用的是默認(rèn)的site map provider,它從Web.sitemap文件獲取數(shù)據(jù),因此breadcrumb顯示為“Home > Customizing the Site Map”.如下圖:


圖17:Breadcrumb使用的是默認(rèn)的Site Map Provider

  要使在圖16里添加的SiteMapPath使用我們定制的site map provider的話,設(shè)其SiteMapProvider property屬性為“Northwind”, 這個(gè)名字是我們?cè)赪eb.config文件里分配給NorthwindSiteMapProvider的.不過,在設(shè)計(jì)器里依然使用的是默認(rèn)的site map provider,但是如你在瀏覽器里登錄該頁(yè)面的話,你將看到breadcrumb使用的是我們定制的site map provider了.


圖18:Breadcrumb現(xiàn)在使用的是我們定制的NorthwindSiteMapProvider

  SiteMapPath控件將在ProductsByCategory.aspx 和 ProductDetails.aspx頁(yè)面展示更具功能性的用戶界面.在這2個(gè)頁(yè)面里添加SiteMapPath控件,設(shè)置其SiteMapProvider屬性為“Northwind”. 在Default.aspx頁(yè)面里點(diǎn)擊Beverages類的“View Products”鏈接,然后再點(diǎn)Chai Tea的“View Details”鏈接,如圖19所示,breadcrumb顯示的是當(dāng)前的網(wǎng)站地圖節(jié)點(diǎn)(“Chai Tea”),及其上級(jí)節(jié)點(diǎn):“Beverages” 和“All Categories”.


圖19:Breadcrumb現(xiàn)在使用的是我們定制的NorthwindSiteMapProvider

  除了SiteMapPath外,還可以使用其它的導(dǎo)航控件,比如Menu 和 TreeView控件.本章的下載代碼里,Default.aspx, ProductsByCategory.aspx,和ProductDetails.aspx頁(yè)面都包含Menu控件(見圖20).要想更深入的了解ASP.NET 2.0里的導(dǎo)航控件和site map體系的話,可參閱《ASP.NET 2.0 QuickStarts》系列(http://quickstarts.asp.net/QuickStartv20/aspnet/)的《Examining ASP.NET 2.0's Site Navigation Features》和《Using Site Navigation Controls》部分.


圖20:Menu控件列出了所有的Categories 和 Products

就像在本教程前面提到的那樣,網(wǎng)站地圖結(jié)構(gòu)可以通過SiteMap class類來(lái)進(jìn)行訪問,下面的代碼返回默認(rèn)的provider的root SiteMapNode:

SiteMapNode root = SiteMap.RootNode;

由于AspNetXmlSiteMapProvider是默認(rèn)的provider,上述代碼返回的是定義在Web.sitemap文件里的根節(jié)點(diǎn),要引用其它的site map provider的話,使用SiteMap class類的Providers property屬性,如:

SiteMapNode root = SiteMap.Providers["name"].RootNode;
這里的name是用戶定制的site map provider的名稱(就本文而言,為“Northwind”)

要訪問某個(gè)具體的site map provider,使用SiteMap.Providers["name"]來(lái)獲取該provider的實(shí)例,再將其轉(zhuǎn)換成恰當(dāng)?shù)念愋汀1热?,要展示NorthwindSiteMapProvider的CachedDate property屬性,使用如下的代碼:

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

  注意:務(wù)必測(cè)試SQL cache dependency屬性,訪問完Default.aspx, ProductsByCategory.aspx, 和 ProductDetails.aspx頁(yè)面后,轉(zhuǎn)到本系列教程的《編輯插入和刪除數(shù)據(jù)》部分的任一個(gè)頁(yè)面,編輯某個(gè)category 或 product的名稱;然后再轉(zhuǎn)到SiteMapProvider文件夾里的某個(gè)頁(yè)面,假設(shè)時(shí)間足夠長(zhǎng),長(zhǎng)到檢測(cè)機(jī)制(polling mechanism)發(fā)現(xiàn)“源數(shù)據(jù)庫(kù)”已經(jīng)發(fā)生了改動(dòng),那么site map應(yīng)該被更新以顯示新的product 或 category名字.

結(jié)語(yǔ):

  ASP.NET 2.0的site map屬性包含一個(gè)SiteMap class類,一系列內(nèi)置的的導(dǎo)航Web控件,以及一個(gè)默認(rèn)的site map provider.為了使用來(lái)自某些數(shù)據(jù)源的site map信息——比如數(shù)據(jù)庫(kù)、系統(tǒng)的“層”、或者某些Web服務(wù),我們需要?jiǎng)?chuàng)建一個(gè)用戶定制的 site map provider.這就要?jiǎng)?chuàng)建一個(gè)類,該類直接或間接的源自SiteMapProvider class類.

  本章我們探討了如何創(chuàng)建一個(gè)用戶定制的site map provider,它以一個(gè)由product 和 category信息構(gòu)成的site map為基礎(chǔ).我們的provider對(duì)StaticSiteMapProvider class類進(jìn)行了擴(kuò)充,并創(chuàng)建了一個(gè)BuildSiteMap method方法來(lái)獲取數(shù)據(jù)、構(gòu)建site map的層次體系,并且將最終的網(wǎng)站地圖結(jié)構(gòu)緩存在一個(gè)“類級(jí)”的變量里.我們使用一個(gè)SQL cache dependency來(lái)確保當(dāng)Categories 或 Products的“源數(shù)據(jù)”發(fā)生改動(dòng)時(shí)使緩存的數(shù)據(jù)失效.

  祝編程快樂!

作者簡(jiǎn)介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。

您可能感興趣的文章:
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十九:使用SQL緩存依賴項(xiàng)SqlCacheDependency
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十一:在事務(wù)里對(duì)數(shù)據(jù)庫(kù)修改進(jìn)行封裝
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十二:GridView批量更新數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十三:GridView實(shí)現(xiàn)批量刪除數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十四:GridView批量添加數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十六:在TableAdapters中使用現(xiàn)有的存儲(chǔ)過程
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十七:在TableAdapters中使用JOINs
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十八:為DataTable添加額外的列
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十九:處理Computed Columns列

標(biāo)簽:中衛(wèi) 清遠(yuǎn) 臨夏 甘肅 海西 慶陽(yáng) 聊城 巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider》,本文關(guān)鍵詞  在,ASP.NET,2.0,中,操作,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。

  • 相關(guān)文章
  • 下面列出與本文章《在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    普定县| 延津县| 桐梓县| 旬阳县| 玉林市| 阜城县| 屯门区| 保定市| 化州市| 永靖县| 平南县| 潜山县| 呼和浩特市| 滦南县| 昌平区| 新安县| 北安市| 汉沽区| 淄博市| 漳州市| 庆云县| 新安县| 永昌县| 兰西县| 灵山县| 汉源县| 三亚市| 尼木县| 东源县| 剑阁县| 莱州市| 乳山市| 彰化市| 京山县| 太仆寺旗| 深泽县| 张家界市| 三江| 古田县| 德江县| 霍城县|