go4it

just do it

.NETPetShop4缓存设计(一)

image

1.CacheDependencyFactory包:
a.DependencyAccess.cs
b.DependencyFacade.cs



2.ICacheDependency包
主要是IPetShopCacheDependency.cs









3.TableCacheDependency包:
每个model的缓存类以及基类:TableDependency.cs

思路如下:

1. 最底层是采用SqlCacheDependency,与数据库表对应,实施SQL Cache Invalidation技术,当对应的数据表数据发生更改后,能从缓存中移除。

总的缓存接口:

using System.Web.Caching;

namespace PetShop.ICacheDependency {
    /// <summary>
    /// This is the interface that the DependencyFactory (Factory Pattern) returns.
    /// Developers could implement this interface to add different types of Cache Dependency to Pet Shop.
    /// </summary>
    public interface IPetShopCacheDependency {

        /// <summary>
        /// Method to create the appropriate implementation of Cache Dependency
        /// </summary>
        /// <returns>CacheDependency object(s) embedded in AggregateCacheDependency</returns>
        AggregateCacheDependency GetDependency();
    }
}

缓存实现的基类:

using System.Web.Caching;
using System.Configuration;

namespace PetShop.TableCacheDependency {

    /// <summary>
    /// This is the base class for SQL2KCacheDependency implementation that encapsulates common
    /// algorithm to retrieve database and table names from configuration file and create
    /// the necessary AggregateCacheDependency object
    /// </summary>
    public abstract class TableDependency : PetShop.ICacheDependency.IPetShopCacheDependency {

        // This is the separator that's used in web.config
        protected char[] configurationSeparator = new char[] { ',' };

        protected AggregateCacheDependency dependency = new AggregateCacheDependency();

        /// <summary>
        /// The constructor retrieves all related configuration and add CacheDependency object accordingly
        /// </summary>
        /// <param name="configKey">Configuration key for specific derived class implementation</param>
        protected TableDependency(string configKey) {

            string dbName = ConfigurationManager.AppSettings["CacheDatabaseName"];
            string tableConfig = ConfigurationManager.AppSettings[configKey];
            string[] tables = tableConfig.Split(configurationSeparator);

            foreach (string tableName in tables)
                dependency.Add(new SqlCacheDependency(dbName, tableName));
        }

        /*这个类也是继承了接口PetShop.ICacheDependency.IPetShopCacheDependency,
         * 那么,这个TableDependency(string configKey)这个方法有什么用呢。
         * 看看源代码:定义字符串dbName,由配置文件里得到这个值为“MSPetShop4”,
         * 这里有个疑问就是定义configurationSeparator怎么是这char类型的数组。这里定义数组有什么作用?怎么就不直接使用char?
         * 这里是将传进来的字符串变成转成AggregateCacheDependency类型的一个集合.使用GetDependency()返回这一类型。那么我们看Product.cs页 public Product() : base("ProductTableDependency") { }
         * 这样我们实例化product之后.使用GetDependency()方法就能获取AggregateCacheDependency类型的值了,这个值就是使用foreach循环用dependency将一个一个值组合起来。而使用base("ProductTableDependency")我们就可以知道放在TableDependency()这里的string 传入为”ProductTableDependency”,那我们直接套进去那值,就可以得到tableConfig的值为"Product,Category"。那么dependencyY就是添加了2个SqlCacheDependency,分别为(“Product”,”MSPetShop4”)及(“Category”,”MSPetShop4”),最终结果只是返回这两个东西合成的一个结果集。
         * 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/s281012/archive/2009/02/25/3934177.aspx     
         */



        public AggregateCacheDependency GetDependency() {
            return dependency;
        }
    }
}

各个model的缓存实现类,如:

using System.Web.Caching;

namespace PetShop.TableCacheDependency {
    /// <summary>
    /// Implementation of Product Cache Dependency for SQL Server 2000
    /// </summary>
    public class Product : TableDependency {

        /// <summary>
        /// Call its base constructor by passing its specific configuration key
        /// </summary>
        public Product() : base("ProductTableDependency") { }
    }
}

2. 然后通过在TableDpendency(实现ICacheDependency命名空间里的接口IPetShopDependency)类

里面的AggregateCacheDependency组合SqlCacheDependency。(在TableCacheDependency命名空间里面)。

3. CacheDependencyFactory命名空间里的DependencyAccess(工厂方法):

using System.Reflection;
using System.Configuration;
using PetShop.ICacheDependency;

namespace PetShop.CacheDependencyFactory {
    public static class DependencyAccess {
        /// <summary>
        /// Method to create an instance of Category dependency implementation
        /// </summary>
        /// <returns>Category Dependency Implementation</returns>
        public static IPetShopCacheDependency CreateCategoryDependency() {
            return LoadInstance("Category");
        }

        /// <summary>
        /// Method to create an instance of Product dependency implementation
        /// </summary>
        /// <returns>Product Dependency Implementation</returns>
        public static IPetShopCacheDependency CreateProductDependency() {
            return LoadInstance("Product");
        }

        /// <summary>
        /// Method to create an instance of Item dependency implementation
        /// </summary>
        /// <returns>Item Dependency Implementation</returns>
        public static IPetShopCacheDependency CreateItemDependency() {
            return LoadInstance("Item");
        }

        /// <summary>
        /// Common method to load dependency class from information provided from configuration file 
        /// </summary>
        /// <param name="className">Type of dependency</param>
        /// <returns>Concrete Dependency Implementation instance</returns>
        private static IPetShopCacheDependency LoadInstance(string className) {

            string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
            string fullyQualifiedClass = path + "." + className;

            // Using the evidence given in the config file load the appropriate assembly and class
            return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
        }
    }
}

DependencyFacade(总的外观类):

using System.Configuration;
using System.Web.Caching;
using System.Collections.Generic;
using PetShop.ICacheDependency;

namespace PetShop.CacheDependencyFactory {
    /// <summary>
    /// This class is provided to ease the usage of DependencyFactory from the client.
    /// It's main usage is to determine whether to invoke the DependencyFactory.  
    /// When no assembly is specified under CacheDependencyAssembly section in configuraion file, 
    /// then this class will return null
    /// Notice that this assembly reference System.Web
    /// </summary>
    public static class DependencyFacade {
        private static readonly string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];

        public static AggregateCacheDependency GetCategoryDependency() {
            if (!string.IsNullOrEmpty(path))            //这个判断有什么用呢?还有dependency是什么意思?这么晦涩?
                return DependencyAccess.CreateCategoryDependency().GetDependency();
            else
                return null;
        }

        public static AggregateCacheDependency GetProductDependency() {
            if (!string.IsNullOrEmpty(path))
                return DependencyAccess.CreateProductDependency().GetDependency();
            else
                return null;
        }

        public static AggregateCacheDependency GetItemDependency() {
            if (!string.IsNullOrEmpty(path))
                return DependencyAccess.CreateItemDependency().GetDependency();
            else
                return null;
        }
    }
}

4. 使用缓存时在自定义控件(Controls文件夹)里面使用:

using System;
using System.Web;
using System.Web.UI.WebControls;
using PetShop.BLL;
using PetShop.CacheDependencyFactory;

namespace PetShop.Web {

    public partial class ProductsControl : System.Web.UI.UserControl {

        /// <summary>
        /// Rebind control 
        /// </summary>
        protected void PageChanged(object sender, DataGridPageChangedEventArgs e) {
            //reset index
            productsList.CurrentPageIndex = e.NewPageIndex;

            //get category id
            string categoryKey = Request.QueryString["categoryId"];

            //bind data
            Product product = new Product();
            productsList.DataSource = product.GetProductsByCategory(categoryKey);
            productsList.DataBind();

        }

        /// <summary>
        /// Add cache dependency
        /// </summary>
        protected void Page_Load(object sender, EventArgs e) {
            this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();
        }
    }
}
在页面load的时候设置UserControl的CachePolicy的Dependency属性。
http://blog.csdn.net/s281012/archive/2009/02/25/3934177.aspx

用户控件ProductsControl.ascx.cs,先看看代码

protected void Page_Load(object sender, EventArgs e) { 

            this.CachePolicy.Dependency = DependencyFacade.GetProductDependency(); 

        } 

解读一下 this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();

一、this.CachePolicy.Dependency部份

this.CachePolicy.Dependency用帮助文件里解析为:”获取或设置与缓存的用户控件输出关联的 CacheDependency 类的实例。”这句话什么意思呢?“与缓存的用户控件输出关联的…”,返回到ProductsControl.ascx,看页面最上面有三段代码

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ProductsControl.ascx.cs" Inherits="PetShop.Web.ProductsControl" EnableViewState="false" %> 

<%@ Register TagPrefix="PetShopControl" Namespace="PetShop.Web" %> 

<%@ OutputCache Duration="100000" VaryByParam="page;categoryId" %> 

原来这个用户控件ProductsControl.ascx这个控件设置了缓存
<%@ OutputCache Duration="100000" VaryByParam="page;categoryId" %>ASP.NET 页的缓存的方法是在页中使用 @ OutputCache 指令,
Duration:设置缓存到期时间,单位:秒。
VaryByParam:可用来使缓存输出因查询字符串而异,多个查询字符用分号隔开。
VaryByControl:可用来使缓存输出因控制值而异。
VaryByHeader:可用来使缓存输出因请求的 HTTP 标头而异。
VaryByCustom:可用来使缓存输出因浏览器类型或您定义的自定义字符串而异。
CacheProfile:结合配置文件使用。
Location:设置页的可缓存性,值有Any,Client,Downstream,None,Server,ServerAndClient。
这里的设置了缓存输出的参数为page及categoryID。(这里有些不明白,为什么他还要用个page?他只需要输出缓存给categoryID这个控件上就行了)

二、DependencyFacade.GetProductDependency()部份
DependencyFacade.GetProductDependency部份我们将开始分析PetShop是如何使用缓存技术的。先追踪一下DependencyFacade类,这个类放在CacheDependencyFactory项目里,根据这个项目名称我们得知,这个是专门放置缓存依赖工厂类。看来又是一个工厂模式的设计了,PetShop在看之前就听说了很了大量了工厂设计模式,看来可以好好学习了。先看这个DependencyFacade到底是在写什么东西?
DependencyFacade.cs代码:

 public static class DependencyFacade { 

        private static readonly string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"]; 

        public static AggregateCacheDependency GetCategoryDependency() { 

            if (!string.IsNullOrEmpty(path)) 

                return DependencyAccess.CreateCategoryDependency().GetDependency(); 

            else 

                return null; 

        } 

        public static AggregateCacheDependency GetProductDependency() { 

            if (!string.IsNullOrEmpty(path)) 

                return DependencyAccess.CreateProductDependency().GetDependency(); 

            else 

                return null; 

        } 

        public static AggregateCacheDependency GetItemDependency() { 

            if (!string.IsNullOrEmpty(path)) 

                return DependencyAccess.CreateItemDependency().GetDependency(); 

            else 

                return null; 

        } 

} 

这个类里有三个方法都有同一个返回类型为AggregateCacheDependency的东西。在这里我并不太清楚AggregateCacheDependency到底是什么东西?因为1.1原来是没有这个东西的,去看了帮助文档才知道原来这个是2.0新增的。解析为:“组合 ASP.NET 应用程序的 Cache 对象中存储的项和 CacheDependency 对象的数组之间的多个依赖项。无法继承此类。”
接下来分析一下这个类的作用,定义一个表态只读的字符串path,他的值来自配置文件,那么先看看这个配置文件里写什么东西 ?打开web.config.找到了那个配置文件为<add key="CacheDependencyAssembly" value="PetShop.TableCacheDependency"/> 他的值为PetShop.TableCacheDependency,类写了一个GetProductDependency()方法,返回值为AggregateCacheDependency的类型,如果这个path不为空,即返回一个DependencyAccess.CreateProductDependency().GetDependency().那么我们也就追踪这个类看看是什么东西?打开DependencyAccess.cs

public static class DependencyAccess { 

        /// <summary> 

        /// Method to create an instance of Category dependency implementation 

        /// </summary> 

        /// <returns>Category Dependency Implementation</returns> 

        public static IPetShopCacheDependency CreateCategoryDependency() { 

            return LoadInstance("Category"); 

        } 

        /// <summary> 

        /// Method to create an instance of Product dependency implementation 

        /// </summary> 

        /// <returns>Product Dependency Implementation</returns> 

        public static IPetShopCacheDependency CreateProductDependency() { 

            return LoadInstance("Product"); 

        } 

        /// <summary> 

        /// Method to create an instance of Item dependency implementation 

        /// </summary> 

        /// <returns>Item Dependency Implementation</returns> 

        public static IPetShopCacheDependency CreateItemDependency() { 

            return LoadInstance("Item"); 

        } 

        /// <summary> 

        /// Common method to load dependency class from information provided from configuration file 

        /// </summary> 

        /// <param name="className">Type of dependency</param> 

        /// <returns>Concrete Dependency Implementation instance</returns> 

        private static IPetShopCacheDependency LoadInstance(string className) { 

            string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"]; 

            string fullyQualifiedClass = path + "." + className; 

            // Using the evidence given in the config file load the appropriate assembly and class 

            return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass); 

        } 

    } 

    怎么没有CreateProductDependency()方法里没有GetDependency()这个方法呢?怎么办。继续分析这个DependencyAccess.cs类。CreateProductDependency()代码如下:

public static IPetShopCacheDependency CreateProductDependency() { 

            return LoadInstance("Product"); 

        } 

返回一个类型为IPetShopCacheDependency类型的东西,这个东西就是LoadInstance("Product")这个方法,代码如下:

private static IPetShopCacheDependency LoadInstance(string className) { 

            string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"]; 

            string fullyQualifiedClass = path + "." + className; 

            return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass); 

        } 

这里用到反射,由上至下我们就可以知道这个是返回一个” PetShop.TableCacheDependency.Product”。
那么我们再将思路跳转到TableCacheDependency项目里的Product.cs

public class Product : TableDependency { 

        /// <summary> 

        /// Call its base constructor by passing its specific configuration key 

        /// </summary> 

        public Product() : base("ProductTableDependency") { } 

} 

这个类继承了TahleDependency,调用基类构造函数。那也就要转到基类去看看罗。TableDependency所以的项目文件为TableCacheDependency/ TableDependency.cs,代码如下:

public abstract class TableDependency : PetShop.ICacheDependency.IPetShopCacheDependency { 

        // This is the separator that's used in web.config 

        protected char[] configurationSeparator = new char[] { ',' }; 

        protected AggregateCacheDependency dependency = new AggregateCacheDependency(); 

        /// <summary> 

        /// The constructor retrieves all related configuration and add CacheDependency object accordingly 

        /// </summary> 

        /// <param name="configKey">Configuration key for specific derived class implementation</param> 

        protected TableDependency(string configKey) { 

            string dbName = ConfigurationManager.AppSettings["CacheDatabaseName"]; 

            string tableConfig = ConfigurationManager.AppSettings[configKey]; 

            string[] tables = tableConfig.Split(configurationSeparator); 

            foreach (string tableName in tables) 

                dependency.Add(new SqlCacheDependency(dbName, tableName)); 

        } 

        public AggregateCacheDependency GetDependency() { 

            return dependency; 

        } 

    } 

这个类也是继承了接口PetShop.ICacheDependency.IPetShopCacheDependency,那么,这个TableDependency(string configKey)这个方法有什么用呢。看看源代码:定义字符串dbName,由配置文件里得到这个值为“MSPetShop4”,
这里有个疑问就是定义configurationSeparator怎么是这char类型的数组。这里定义数组有什么作用?怎么就不直接使用char?
这里是将传进来的字符串变成转成AggregateCacheDependency类型的一个集合.使用GetDependency()返回这一类型。那么我们看Product.cs页 public Product() : base("ProductTableDependency") { }
这样我们实例化product之后.使用GetDependency()方法就能获取AggregateCacheDependency类型的值了,这个值就是使用foreach循环用dependency将一个一个值组合起来。而使用base("ProductTableDependency")我们就可以知道放在TableDependency()这里的string 传入为”ProductTableDependency”,那我们直接套进去那值,就可以得到tableConfig的值为"Product,Category"。那么dependencyY就是添加了2个SqlCacheDependency,分别为(“Product”,”MSPetShop4”)及(“Category”,”MSPetShop4”),最终结果只是返回这两个东西合成的一个结果集。

   返回后那么缓存依赖this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();我并不知道这里加了个缓存依赖有什么作用?好像这整个页面里没有缓存的设置

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/s281012/archive/2009/02/25/3934177.aspx

************************************************************************************************

另外在web\appcode里面有个WebUtility.CS文件

using System;
using System.Text.RegularExpressions;
using System.Web;
using PetShop.BLL;
using System.Web.Caching;
using PetShop.CacheDependencyFactory;
using System.Configuration;

namespace PetShop.Web {
    /// <summary>
    /// Collection of utility methods for web tier
    /// </summary>
    public static class WebUtility {

        private const string REDIRECT_URL = "~/Search.aspx?keywords={0}";
        private const string CATEGORY_NAME_KEY = "category_name_{0}";
        private const string PRODUCT_NAME_KEY = "product_name_{0}";
        private static readonly bool enableCaching = bool.Parse(ConfigurationManager.AppSettings["EnableCaching"]);


        /// <summary>
        /// Method to make sure that user's inputs are not malicious
        /// </summary>
        /// <param name="text">User's Input</param>
        /// <param name="maxLength">Maximum length of input</param>
        /// <returns>The cleaned up version of the input</returns>
        public static string InputText(string text, int maxLength) {
            text = text.Trim();
            if (string.IsNullOrEmpty(text))
                return string.Empty;
            if (text.Length > maxLength)
                text = text.Substring(0, maxLength);
            text = Regex.Replace(text, "[\\s]{2,}", " ");	//two or more spaces
            text = Regex.Replace(text, "(<[b|B][r|R]/*>)+|(<[p|P](.|\\n)*?>)", "\n");	//<br>
            text = Regex.Replace(text, "(\\s*&[n|N][b|B][s|S][p|P];\\s*)+", " ");	//&nbsp;
            text = Regex.Replace(text, "<(.|\\n)*?>", string.Empty);	//any other tags
            text = text.Replace("'", "''");
            return text;
        }

        /// <summary>
        /// Method to check whether input has other characters than numbers
        /// </summary>
        public static string CleanNonWord(string text) {
            return Regex.Replace(text, "\\W", "");
        }

        /// <summary>
        /// Method to redirect user to search page
        /// </summary>
        /// <param name="key">Search keyword</param> 
        public static void SearchRedirect(string key) {
            HttpContext.Current.Response.Redirect(string.Format(REDIRECT_URL, InputText(key, 255)));
        }

        /// <summary>
        /// Method to retrieve and cache category name by its ID
        /// </summary>
        /// <param name="categoryId">Category id</param>
        /// <returns>Category name</returns>
        public static string GetCategoryName(string categoryId) {

            Category category = new Category();
            if (!enableCaching)
                return category.GetCategory(categoryId).Name;

            string cacheKey = string.Format(CATEGORY_NAME_KEY, categoryId);

            // Check if the data exists in the data cache
            string data = (string)HttpRuntime.Cache[cacheKey];
            if (data == null) {
                // Caching duration from Web.config
                int cacheDuration = int.Parse(ConfigurationManager.AppSettings["CategoryCacheDuration"]);

                // If the data is not in the cache then fetch the data from the business logic tier
                data = category.GetCategory(categoryId).Name;

                // Create a AggregateCacheDependency object from the factory
                AggregateCacheDependency cd = DependencyFacade.GetCategoryDependency();

                // Store the output in the data cache, and Add the necessary AggregateCacheDependency object
                HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
            }

            return data;
        }

        /// <summary>
        /// Method to retrieve and cache product name by its ID
        /// </summary>
        /// <param name="productId">Product id</param>
        /// <returns>Product name</returns>
        public static string GetProductName(string productId) {

            Product product = new Product();
            if (!enableCaching)
                return product.GetProduct(productId).Name;

            string cacheKey = string.Format(PRODUCT_NAME_KEY, productId);

            // Check if the data exists in the data cache
            string data = (string)HttpRuntime.Cache[cacheKey];

            if (data == null) {
                // Caching duration from Web.config
                int cacheDuration = int.Parse(ConfigurationManager.AppSettings["ProductCacheDuration"]);

                // If the data is not in the cache then fetch the data from the business logic tier
                data = product.GetProduct(productId).Name;

                // Create a AggregateCacheDependency object from the factory
                AggregateCacheDependency cd = DependencyFacade.GetProductDependency();

                // Store the output in the data cache, and Add the necessary AggregateCacheDependency object
                HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High, null);

            }

            return data;
        }
    }
}

在里面对个别的属性添加缓存。

在页面里使用:

using System;


namespace PetShop.Web {
    
    public partial class Products : System.Web.UI.Page {

        protected void Page_Load(object sender, EventArgs e) {
            //get page header and title
            Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]);
        }
    }
}

posted on 2009-07-10 20:30  cxccbv  阅读(516)  评论(0)    收藏  举报

导航