基于性能等方面的考虑,我们应该将对驻留在内存里的网站地图结构进行数据缓存,每次调用BuildSiteMap的方法时,直接返回缓存的数据而不用重新检索数据.在任何情况下,如果我们不对BuildSiteMap对应的网站结构进行缓存的话,每次调用时,我们都需要通过“层”来重新检索产品和种类的信息(这将最终导致对数据库的查询).我们在前面的缓存章节探讨过缓存数据“过时”的问题,为此,我们要么使用基于时间,要么使用基于SQL cache dependency的缓存技术.
注意:一个site map provider可以任意地重写(override)Initialize method方法.Initialize 方法是当site map provider第一次实例化的时候被调用的,并可以将我们在Web.config 文件的<add>元素里赋值的用户自定义属性值传递给它,比如:<add type="type" customAttribute="value" />.当一个页面开发者希望指定各种与site map provider相关的设置,而又不希望修改site map provider的代码的时候,这样做很有用.比如,假如我们希望不通过“层”而直接从数据库读取category 和 products的数据时,我们当然希望页面开发者调用Web.config文件里的数据库连接字符串,而不使用site map provider代码里的“硬编码”值.我们不打算在第六步创建的自定义site map provider里重写Initialize方法.见Jeff Prosise的文章《Storing Site Maps in SQL Server》()
第六步:创建自定义的Site Map Provider
要想创建一个自定义的site map provider来构建源于Northwind数据库里的categories 和 products信息的网站地图(site map),我们需要创建一个类来扩展StaticSiteMapProvider.在前面我们在App_Code文件夹里添加了一个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?; } } }