PetShop 4.0的缓存处理

    在访问量非常大,但更新较少的网站中使用缓存,可以大大提高程序运行的效率,给网络用户一个良好的体验效果。在Microsoft提供的经典示例项 目.Net PetShop 4.0中,也提供了对缓存的支持,本文是作者在学习此项目时的一些心得体会,有一些地方还不十分清楚,希望能够抛砖引玉。

    在.Net PetShop 4.0中,非常成功地使用了工厂模式以及接口(interface)、静态类(Static class)、抽象类(abstract class)等成员。在使用缓存时,也是通过web.config配置进行设置,在使用时非常灵活。下面从底向上具体分析.Net PetShop 4.0缓存方面的技术。

    首先看一下该项目中与缓存直接相关的命名空间:

PetShop.ICacheDependency
PetShop.TableCacheDependency
PetShop.CacheDependencyFactory
PetShop.Web

一、PetShop.ICacheDependency命名空间

   最低层应该是接口的定义了,在PetShop.ICacheDependency命名空间中只定义了一个接口 IPetShopCacheDependency,该接口只有一个方法 GetDependency,没有任何参数,返回AggregateCacheDependency类型。 AggregateCacheDependency是在.NET Framework 2.0 版中是新增的类,组合 ASP.NET 应用程序的 Cache 对象中存储的项和 CacheDependency 对象的数组之间的多个依赖项(MSDN中原话)。

二、PetShop.TableCacheDependency命名空间

   PetShop.TableCacheDependency 命名空间中,提供两种类:抽象类 TableDependency 和它的继承类 Category Item Product 。抽象类 TableDependency 的构造函数为:

 1  protected  TableDependency( string  configKey) {
 2 
 3       string  dbName  =  ConfigurationManager.AppSettings[ " CacheDatabaseName " ];
 4       string  tableConfig  =  ConfigurationManager.AppSettings[configKey];
 5       string [] tables  =  tableConfig.Split(configurationSeparator);
 6 
 7       foreach  ( string  tableName  in  tables)
 8          dependency.Add( new  SqlCacheDependency(dbName, tableName));
 9  }
10 

   传 递了一个参数 configKey ,根据该参数从 web.config 文件中获取表名列表,同时在 web.config 中获取数据库名称。将表名列表中的所 有数据表添加到 AggregateCacheDependency 类型的 dependency 变量中。在此外使用了 .NET Framework 2.0 版中是新增的另一个与缓存有关的 SqlCacheDependency 类。这个类用于建立 ASP.NET 应用程序的 Cache 对象中存储的项和特定 SQL Server 数据库表之间的联系。 AggregateCacheDependency SqlCacheDependency 都从 CacheDependency 继承而来,但在 .NET 2.0 中还未提供 Oracle 等其它数据库对应的类。

   下面是 web.config 文件中与缓存相关的设置:
1  <!--  Cache dependency options. Possible values: PetShop.TableCacheDependency for SQL Server and keep empty for ORACLE  -->
2  < add  key ="CacheDependencyAssembly"  value ="PetShop.TableCacheDependency" />
3  <!--  CacheDatabaseName should match the name under caching section, when using TableCacheDependency  -->
4  < add  key ="CacheDatabaseName"  value ="MSPetShop4" />
5  <!--  *TableDependency lists table dependency for each instance separated by comma  -->
6  < add  key ="CategoryTableDependency"  value ="Category" />
7  < add  key ="ProductTableDependency"  value ="Product,Category" />
8  < add  key ="ItemTableDependency"  value ="Product,Category,Item" />
9 
   每个继承类都只有一个构造函数,通过设置基类的 configKey 参数变成了三个不同的类。 Product 类的构造函数为:

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

三 CacheDependency工厂

    继 承了抽象类TableDependency的Product、Category和Item类均需要在调用时创建各自的对象。由于它们的父类 TableDependency实现了接口IPetShopCacheDependency,因而它们也间接实现了 IPetShopCacheDependency接口,这为实现工厂模式提供了前提。

   在PetShop 4.0中,依然利用了配置文件和反射技术来实现工厂模式。命名空间PetShop.CacheDependencyFactory中,类DependencyAccess即为创建IPetShopCacheDependency对象的工厂类:

 1  public   static   class  DependencyAccess
 2  {       
 3       public   static  IPetShopCacheDependency CreateCategoryDependency()
 4      {
 5           return  LoadInstance( " Category " );
 6      }
 7       public   static  IPetShopCacheDependency CreateProductDependency()
 8      {
 9           return  LoadInstance( " Product " );
10      }
11       public   static  IPetShopCacheDependency CreateItemDependency()
12      {
13           return  LoadInstance( " Item " );
14      }
15       private   static  IPetShopCacheDependency LoadInstance( string  className)
16      {
17           string  path  =  ConfigurationManager.AppSettings[ " CacheDependencyAssembly " ];
18           string  fullyQualifiedClass  =  path  +   " . "   +  className;
19           return  (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
20      }
21  }

    在这个方法中通过配置文件中的设置和传进来的参数className,返回相对应的程序集和类。DependencyAccess类里面的其它三个方法,只是调用这个方法,传入不同的参数而已。

整个工厂模式的实现如图4-3所示:


   DependencyFacade 类提供的三个方法正好与 DependencyAccess 类的三个方法相对应,分别获取 Category Item Product AggregateCacheDependency 。在 DependencyFacade 类中还读取了 web.config 中的 CacheDependencyAssembly 设置,从而决定是调用 DependencyAccess 对应的方法,还是直接返回 null

 1  public   static   class  DependencyFacade
 2  {
 3       private   static   readonly   string  path  =  ConfigurationManager.AppSettings[ " CacheDependencyAssembly " ];
 4       public   static  AggregateCacheDependency GetCategoryDependency()
 5      {
 6           if  ( ! string .IsNullOrEmpty(path))
 7               return  DependencyAccess.CreateCategoryDependency().GetDependency();
 8           else
 9               return   null ;
10      }
11       public   static  AggregateCacheDependency GetProductDependency()
12      {
13           if  ( ! string .IsNullOrEmpty(path))
14               return  DependencyAccess.CreateProductDependency().GetDependency();
15           else
16               return   null ;
17          }
18       public   static  AggregateCacheDependency GetItemDependency()
19      {
20           if  ( ! string .IsNullOrEmpty(path))
21               return  DependencyAccess.CreateItemDependency().GetDependency();
22           else
23               return   null ;
24      }
25  }
26 
     DependencyFacade 类封装了获取 AggregateCacheDependency 类型对象的逻辑,如此一来,调用者可以调用相关方法获得创建相关依赖项的 AggregateCacheDepe ndency 类型对象:
1  AggregateCacheDependency dependency  =  DependencyFacade.GetCategoryDependency();
   比起直接调用 DependencyAccess 类的 GetDependency() 方法而言,除了方法更简单之外,同时它还对 CacheDependencyAssembly 配置节进行了判断,如果其值为空,则返回 null 对象。

  虽然DependencyAccess类创建了实现了IPetShopCacheDependency接口的类Category、Product、 Item,然而我们之所以引入IPetShopCacheDependency接口,其目的就在于获得创建了依赖项的 AggregateCacheDependency类型的对象。

四、 PetShop.Web 命名空间
   在 PetShop.Web App_Code 中,有四个静态类与缓存直接相关,分别是 CategoryDataProxy ItemDataProxy ProductDataProxy WebUtility 。其中前三个分别调用 DependencyFacade 对应的方法。 例如 GetCategoryName() 方法:
 1  public   static   string  GetCategoryName( string  categoryId)
 2  {
 3       Category category  =   new  Category();
 4        if  ( ! enableCaching)
 5               return  category.GetCategory(categoryId).Name;
 6        string  cacheKey  =   string .Format(CATEGORY_NAME_KEY, categoryId);
 7        //  检查缓存中是否存在该数据项;
 8        string  data  =  ( string )HttpRuntime.Cache[cacheKey];
 9        if  (data  ==   null )
10       {
11              //  通过web.config的配置获取duration值;
12              int  cacheDuration  =   int .Parse(ConfigurationManager.AppSettings[ " CategoryCacheDuration " ]);
13              //  如果缓存中不存在该数据项,则通过业务逻辑层访问数据库获取;
14             data  =  category.GetCategory(categoryId).Name;
15              //  通过Facade类创建AggregateCacheDependency对象;
16             AggregateCacheDependency cd  =  DependencyFacade.GetCategoryDependency();
17              //  将数据项以及AggregateCacheDependency 对象存储到缓存中;
18             HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High,  null );
19        }
20         return  data;
21  }
22 

   GetCategoryName ()方法首先会检查缓存中是否已经存在CategoryName数据项,如果已经存在,就通过缓存直接获取数据;否则将通过业务逻辑层调用数据访问层访问 数据库获得CategoryName,在获得了CategoryName后,会将新获取的数据连同DependencyFacade类创建的 AggregateCacheDependency对象添加到缓存中。
     WebUtility静态类被表示层的许多页面所调用,例如Product页面:


1  public  partial  class  Products : System.Web.UI.Page
2  {
3       protected   void  Page_Load( object  sender, EventArgs e)
4      {
5          Page.Title  =  WebUtility.GetCategoryName(Request.QueryString[ " categoryId " ]);
6      }
7  }
  
     显示页面
title 的逻辑是放在 Page_Load 事件方法中,因而每次打开该页面都要执行获取 CategoryName 的方法。如果没有采用缓存机制,当 Category 数据较多时,页面的显示就会非常缓慢。
   显示页面 title 的逻辑是放在 Page_Load 事件方法中,因而每次打开该页面都要执行获取 CategoryName 的方法。如果没有采用缓存机制,当 Category 数据较多时,页面的显示就会非常缓慢。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值