PetShop之ASP.NET缓存

《解剖PetShop》体系之四

四 PetShop之ASP.NET缓存

若果对微型电脑硬件系统有丰盛的问询,那么大家对于Cache那么些名词一定是如数家珍的。在CPU以及主板的芯片中,都引入了那种名为高速缓冲存储器(Cache)的技术。因为Cache的存取速度比内存快,由此引入Cache可以行得通的解决CPU与内存之间的速度不兼容难点。硬件系统可以采用Cache存储CPU访问几率高的那个数据,当CPU要求拜访这么些数量时,可以一贯从Cache中读取,而无需访问存取速度相对较慢的内存,从而进步了CPU的工作功用。软件设计借鉴了硬件设计中引入缓存的体制以创新整个系列的特性,尤其是对此一个数据库驱动的Web应用程序而言,缓存的行使是少不了的,毕竟,数据库查询可能是任何Web站点中调用最频仍但同时又是实施最缓慢的操作之一,大家不可以被它老迈的双腿拖缓大家前进的道路。缓存机制正是解决这一瑕疵的加快器。

4.1  ASP.NET缓存概述

作为.Net框架下支付Web应用程序的主打产品,ASP.NET丰硕考虑了缓存机制。通过某种方式,将系统须求的多寡对象、Web页面存储在内存中,使得Web站点在急需得到那一个数据时,不要求通过繁琐的数据库连接、查询和复杂的逻辑运算,就可以“触手可及”,如“十拿九稳”般简单而迅速,从而提升全部Web系统的性质。

ASP.NET提供了三种为主的缓存机制来提供缓存功效。一种是应用程序缓存,它同意开发者将次第生成的数据或报表工作对象放入缓存中。其它一种缓存机制是页输出缓存,利用它,可以直接得到存放在缓存中的页面,而不须要经过繁杂的对该页面的双重拍卖。

应用程序缓存其落实原理说来平淡无奇,仅仅是通过ASP.NET管理内存中的缓存空间。放入缓存中的应用程序数据对象,以键/值对的措施存储,那有利于用户在走访缓存中的数据项时,可以根据key值判断该项是还是不是存在缓存中。

放入在缓存中的数据对象其生命周期是境遇限制的,即便在任何应用程序的生命周期里,也无法担保该数量对象一向有效。ASP.NET可以对应用程序缓存举办管理,例如当数码项无效、过期或内存不足时移除它们。其它,调用者还足以由此CacheItemRemovedCallback委托,定义回调方法使得数据项被移除时亦可文告用户。

在.Net
Framework中,应用程序缓存通过System.Web.Caching.Cache类落成。它是一个密封类,不可能被持续。对于每一个利用程序域,都要创制一个Cache类的实例,其生命周期与使用程序域的生命周期保持一致。大家可以利用Add或Insert方法,将数据项添加到应用程序缓存中,如下所示:
Cache[“First”] = “First Item”;
Cache.Insert(“Second”, “Second Item”);

俺们还足以为应用程序缓存添加信赖项,使得敬爱项发生改变时,该数量项可以从缓存中移除:
string[] dependencies = {“Second”};
Cache.Insert(“Third”, “Third Item”,
new System.Web.Caching.CacheDependency(null, dependencies));

与之相应的是缓存中数据项的移除。前边提到ASP.NET可以自动管理缓存中项的移除,但我们也得以透过代码编写的方法显式的移除相关的数量项:
Cache.Remove(“First”);

争辩于应用程序缓存而言,页输出缓存的使用尤其宽广。它可以经过内存将处理后的ASP.NET页面存储起来,当客户端再一回访问该页面时,可以省去页面处理的进度,从而提升页面访问的特性,以及Web服务器的吞吐量。例如,在一个电子商务网站里,用户须求日常查询商品音信,那几个历程会涉嫌到数据库访问以及查找条件的匹配,在数据量较大的动静下,如此的探寻进程是较为耗时的。此时,利用页输出缓存就可以将首先次搜索获得的询问结果页存储在缓存中。当用户第二次查询时,就可以省去数据查询的进程,收缩页面的响应时间。

页输出缓存分为整页缓存和一部分页缓存。大家可以通过@OutputCache指令完结对Web页面的出口缓存。它根本涵盖多少个参数:Duration和VaryByParam。Duration参数用于安装页面或控件进行缓存的小时,其单位为秒。如下的设置表示缓存在60秒内有效:
<%@ OutputCache Duration=“60“ VaryByParam=“none“ %>

比方没有当先Duration设置的限期值,当用户访问同一的页面或控件时,就可以直接在缓存中收获。
利用VaryByParam参数可以根据设置的参数值建立不一致的缓存。例如在一个输出天气预告结果的页面中,要是急需为一个ID为txtCity的TextBox控件建立缓存,其值将显得某城市的空气温度,那么大家能够拓展如下的设置:
<%@ OutputCache Duration=”60” VaryByParam=”txtCity” %>

如此一来,ASP.NET会对txtCity控件的值进行判断,唯有输入的值与缓存值相同,才从缓存中取出相应的值。那就使得地幸免了因为值的差别而造成出口错误的数量。

选择缓存的机制对品质的升官极度显著。通过ACT(Application Center
Test)的测试,可以窥见安装缓存后实施的品质比未安装缓存时的特性足足增强三倍多。

引入缓存看来是增进质量的“完美”解决方案,但是“金无足赤,人无完人”,缓存机制也有欠缺,那就是多少过期的标题。一旦应用程序数据或者页面结果值暴发的变动,那么在缓存有效期范围内,你所得到的结果将是晚点的、不精确的数目。大家可以想一想股票系统使用缓存所带来的天灾人祸,当您利用错误过期的多少去分析股市的变化莫测时,你会发觉赢得的结果真可以说是“失之毫厘,谬以千里”,看似大好的层面就会像赏心悦目的泡沫一样,用针一戳,转眼就消灭得无影无踪。

那么大家是或不是应当为了追求高质量,而不顾所谓“数据过期”所带来的隐患呢?鲜明,在相近于股票系统那种数量更新往往的一定情景下,数据过期的不得了表现甚至比低效的属性更令人难以接受。故而,大家须求在品质与数量科学性间作出权衡。所幸的是,.Net
Framework
2.0引入了一种新的缓存机制,它为我们的“鱼与熊掌兼得”带来了技术上的取向。

.Net 2.0引入的自定义缓存依赖项,更加是依照MS-SQL
Server的SqlCacheDependency特性,使得大家可以幸免“数据过期”的难题,它亦可基于数据库中相应数据的浮动,文告缓存,并移除这一个过期的数目。事实上,在PetShop
4.0中,就丰盛地利用了SqlCacheDependency特性。

4.2 SqlCacheDependency特性

SqlCacheDependency特性实际上是通过System.Web.Caching.SqlCacheDependency类来体现的。通过此类,可以在富有接济的SQL
Server版本(7.0,2000,2005)上监视特定的SQL
Server数据库表,并创建信赖于该表以及表中数据行的缓存项。当数据表或表中特定行的数量发生变更时,具有看重项的数量项就会失效,并活动从Cache中剔除该项,从而确保了缓存中不再保留过期的数额。
出于版本的原由,SQL Server 2005完全支持SqlCacheDependency特性,但对于SQL
Server 7.0和SQL Server
2000而言,就从未有过这么幸运了。毕竟那个制品出现在.Net Framework
2.0事先,由此它并没有完毕活动监视数据表数据变动,文告ASP.NET的功效。解决的不二法门就是应用轮询机制,通过ASP.NET进度内的一个线程以指定的小时距离轮询SQL
Server数据库,以跟踪数据的转移情状。

要使得7.0仍然2000版本的SQL
Server接济SqlCacheDependency特性,需求对数据库服务器执行有关的布置步骤。有三种办法配置SQL
Server:使用aspnet_regsql命令行工具,或者选取SqlCacheDependencyAdmin类。

4.2.1  利用aspnet_regsql工具

aspnet_regsql工具位于Windows\Microsoft.NET\Framework\[版本]文本夹中。倘诺直白双击该工具的实施文书,会弹出一个带领对话框,提示大家成功相应的操作:

皇冠直营现金网官方网 1
图4-1 aspnet_regsql工具

如图4-1所示中的提示音讯,表达该引路主要用来配置SQL
Server数据库,如membership,profiles等音讯,要是要配备SqlCacheDependency,则必要以命令行的主意履行。以PetShop
4.0为例,数据库名为MSPetShop4,则下令为:
aspnet_regsql -S localhost -E -d MSPetShop4 -ed

以下是该工具的一声令下参数表明:
-?  突显该工具的帮衬意义;
-S  后接的参数为数据库服务器的称呼或者IP地址;
-U  后接的参数为数据库的登陆用户名;
-P  后接的参数为数据库的登陆密码;
-E  当使用windows集成验证时,使用该意义;
-d  后接参数为对哪一个数据库采取SqlCacheDependency作用;
-t  后接参数为对哪一个表选取SqlCacheDependency作用;
-ed  允许对数据库使用SqlCacheDependency功效;
-dd  禁止对数据库选用SqlCacheDependency作用;
-et  允许对数据表选拔SqlCacheDependency作用;
-dt  禁止对数据表选取SqlCacheDependency功用;
-lt  列出脚下数据库中有怎样表已经应用sqlcachedependency功用。

以地方的指令为例,表明将对名为MSPetShop4的数据库采纳SqlCacheDependency作用,且SQL
Server采取了windows集成验证格局。大家还足以对有关的数据表执行aspnet_regsql命令,如:
aspnet_regsql -S localhost -E -d MSPetShop4 -t Item -et
aspnet_regsql -S localhost -E -d MSPetShop4 -t Product -et
aspnet_regsql -S localhost -E -d MSPetShop4 -t Category -et

当执行上述的四条命令后,aspnet_regsql工具会在MSPetShop4数据库中建立一个名为AspNet_SqlCacheTablesForChangeNotification的新数据库表。该数据表包蕴几个字段。字段tableName记录要追踪的数据表的名号,例如在PetShop
4.0中,要记录的数据表就概括Category、Item和Product。notificationCreated字段记录开头追踪的时刻。changeId作为一个项目为int的字段,用于记录数据表数据爆发变化的次数。如图4-2所示:

皇冠直营现金网官方网 2
图4-2 AspNet_SqlCacheTablesForChangeNotification数据表

除此之外,执行该命令还会为MSPetShop4数据库添加一组存储进度,为ASP.NET提供查询追踪的数据表的意况,同时还将为运用了SqlCacheDependency的表添加触发器,分别对应Insert、Update、Delete等与数码变动相关的操作。例如Product数据表的触发器:
CREATE TRIGGER dbo.[Product_AspNet_SqlCacheNotification_Trigger] ON
[Product]
    FOR INSERT, UPDATE, DELETE AS BEGIN
    SET NOCOUNT ON
    EXEC dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure N’Product’
END

其中,AspNet_SqlCacheUpdateChangeIdStoredProcedure即是工具添加的一组存储进程中的一个。当对Product数据表执行Insert、Update或Delete等操作时,就会激活触发器,然后实施AspNet_SqlCacheUpdateChangeIdStoredProcedure存储进度。其履行的经过就是修改AspNet_SqlCacheTablesForChangeNotification数据表的changeId字段值:
CREATE PROCEDURE dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure
             @tableName NVARCHAR(450)
         AS
         BEGIN
             UPDATE dbo.AspNet_SqlCacheTablesForChangeNotification WITH
(ROWLOCK) SET changeId = changeId + 1
             WHERE tableName = @tableName
         END  
GO

4.2.2  利用SqlCacheDependencyAdmin类

咱俩也得以接纳编程的章程来来管理数据库对SqlCacheDependency特性的使用。该类蕴涵了多少个基本点的艺术:

DisableNotifications 为特定数据库禁用 SqlCacheDependency对象更改通知
DisableTableForNotifications 为数据库中的特定表禁用SqlCacheDependency对象更改通知
EnableNotifications 为特定数据库启用SqlCacheDependency对象更改通知
EnableTableForNotifications 为数据库中的特定表启用SqlCacheDependency对象更改通知
GetTablesEnabledForNotifications 返回启用了SqlCacheDependency对象更改通知的所有表的列表

表4-1 SqlCacheDependencyAdmin类的第一格局

皇冠直营现金网官方网,假定大家定义了如下的数据库连接字符串:
const string connectionStr = “Server=localhost;Database=MSPetShop4”;

那么为数据库MSPetShop4启用SqlCacheDependency对象更改公告的兑现为:
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
   {
       SqlCacheDependencyAdmin.EnableNotifications(connectionStr);
   }
}

为多少表Product启用SqlCacheDependency对象更改公告的落到实处则为:
SqlCacheDependencyAdmin.EnableTableForNotifications(connectionStr,
“Product”);

假若要调用表4-1中所示的连锁方法,要求注意的是访问SQL
Server数据库的帐户必须具有创立表和仓储进程的权柄。假如要调用EnableTableForNotifications方法,还亟需具有在该表上创制SQL
Server触发器的权位。

尽管说编程情势给予了程序员更大的得心应手,但aspnet_regsql工具却提供了更简便的方法完毕对SqlCacheDependency的配备与治本。PetShop
4.0运用的正是aspnet_regsql工具的主意,它编写了一个文件名为InstallDatabases.cmd的批处理公事,其中包含了对aspnet_regsql工具的施行,并因而安装程序去调用该文件,落成对SQL
Server的布署。

4.3 在PetShop 4.0中ASP.NET缓存的落到实处

PetShop作为一个B2C的宠物网上商店,须要丰硕考虑访客的用户体验,要是因为数据量大而招致Web服务器的响应不立刻,页面和查询数据迟迟得不到结果,会因而而破坏客户走访网站的心气,在耗尽耐心的等候后,可能会失掉这一有些客户。无疑,那是尤其不好的结果。因此在对其展开系统架构设计时,整个系统的质量就显得殊为主要。但是,大家无法半涂而废,因为在意于品质而忽视数据的没错。在PetShop
3.0本子以及从前的本子,因为ASP.NET缓存的局限性,这一难题并没有博得很好的解决。PetShop
4.0则引入了SqlCacheDependency特性,使得系统对缓存的处理比较此前大为改观。

4.3.1  CacheDependency接口

PetShop
4.0引入了SqlCacheDependency特性,对Category、Product和Item数据表对应的缓存进行了SQL
Cache
Invalidation技术。当对应的数据表数据暴发变动后,该技术可以将有关项从缓存中移除。达成这一技术的中央是SqlCacheDependency类,它三番一回了CacheDependency类。然则为了有限协助一切架构的可扩张性,大家也允许设计者建立自定义的CacheDependency类,用以扩充缓存敬服。那就有需要为CacheDependency建立抽象接口,并在web.config文件中展开布署。

在PetShop
4.0的命名空间PetShop.ICacheDependency中,定义了名为IPetShopCacheDependency接口,它仅包括了一个接口方法:
public interface IPetShopCacheDependency
{      
    AggregateCacheDependency GetDependency();
}

AggregateCacheDependency是.Net Framework
2.0新增的一个类,它担负监视器重项对象的聚合。当以此集合中的任意一个借助项对象暴发变动时,该看重项对象对应的缓存对象都将被活动移除。
AggregateCacheDependency类起到了组合CacheDependency对象的效率,它能够将多个CacheDependency对象竟然不一致类其余CacheDependency对象与缓存项建立关系。由于PetShop需求为Category、Product和Item数据表建立看重项,因此IPetShopCacheDependency的接口方法GetDependency()其目的就是回来建立了那些珍贵项的AggregateCacheDependency对象。

4.3.2  CacheDependency实现

CacheDependency的贯彻正是为Category、Product和Item数据表建立了对应的SqlCacheDependency类型的依靠项,如代码所示:
public abstract class TableDependency : IPetShopCacheDependency
{
    // This is the separator that’s used in web.config
    protected char[] configurationSeparator = new char[] { ‘,’ };

    protected AggregateCacheDependency dependency = new
AggregateCacheDependency();
    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;
    }
}

亟待树立信赖项的数据库与数码表都配置在web.config文件中,其设置如下:
<add key=”CacheDatabaseName” value=”MSPetShop4″/>
<add key=”CategoryTableDependency” value=”Category”/>
<add key=”ProductTableDependency” value=”Product,Category”/>
<add key=”ItemTableDependency” value=”Product,Category,Item”/>

按照各类数据表间的借助关系,由此分化的数据表须求建立的信赖性项也是不均等的,从安排文件中的value值可以见见。然则无论建立依赖项的数据,其创制的一颦一笑逻辑都是一般的,由此在统筹时,抽象了一个联名的类TableDependency,并由此确立带参数的构造函数,落成对信赖项的建立。由于接口方法GetDependency()的兑现中,重临的对象dependency是在受保险的构造函数制造的,由此那里的落到实处格局也足以视作是Template
Method方式的灵活运用。例如TableDependency的子类Product,就是选用父类的构造函数建立了Product、Category数据表的SqlCacheDependency依赖:
public class Product : TableDependency
{
    public Product() : base(“ProductTableDependency”) { }
}

借使须要自定义CacheDependency,那么创设敬爱项的不二法门又有不一致。不过无论是创设SqlCacheDependency对象,仍然自定义的CacheDependency对象,都是将那些看重项添加到AggregateCacheDependency类中,因此大家也可以为自定义CacheDependency建立专门的类,只要已毕IPetShopCacheDependency接口即可。

4.3.3  CacheDependency工厂

延续了抽象类TableDependency的Product、Category和Item类均要求在调用时创制各自的靶子。由于它们的父类TableDependency完成了接口IPetShopCacheDependency,因此它们也直接完结了IPetShopCacheDependency接口,那为兑现工厂格局提供了前提。

在PetShop
4.0中,仍旧采取了计划文件和反光技术来兑现工厂格局。命名空间PetShop.CacheDependencyFactory中,类DependencyAccess即为创设IPetShopCacheDependency对象的工厂类:
public static class DependencyAccess
{       
    public static IPetShopCacheDependency CreateCategoryDependency()
    {
        return LoadInstance(“Category”);
    }
    public static IPetShopCacheDependency CreateProductDependency()
    {
        return LoadInstance(“Product”);
    }
    public static IPetShopCacheDependency CreateItemDependency()
    {
        return LoadInstance(“Item”);
    }
    private static IPetShopCacheDependency LoadInstance(string
className)
    {
        string path =
ConfigurationManager.AppSettings[“CacheDependencyAssembly”];
        string fullyQualifiedClass = path + “.” + className;
        return
(IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
    }
}
漫天工厂方式的落成如图4-3所示:

皇冠直营现金网官方网 3
 图4-3 CacheDependency工厂

固然如此DependencyAccess类创立了完结了IPetShopCacheDependency接口的类Category、Product、Item,然则大家由此引入IPetShopCacheDependency接口,其目标就在于得到创造了依靠项的AggregateCacheDependency类型的对象。大家得以调用对象的接口方法GetDependency(),如下所示:
AggregateCacheDependency dependency =
DependencyAccess.CreateCategoryDependency().GetDependency();

为了便利调用者,如同大家得以对DependencyAccess类举行改正,将本来的CreateCategoryDependency()方法,修改为创制AggregateCacheDependency类型对象的点子。

不过那样的做法扰攘了作为工厂类的DependencyAccess的本身任务,且创造IPetShopCacheDependency接口对象的行为依然有可能被调用者调用,所以保留原有的DependencyAccess类依旧是有必不可少的。

在PetShop
4.0的统筹中,是因此引入Facade情势以方便调用者尤其简约地取得AggregateCacheDependency类型对象。

4.3.4  引入Facade模式

应用Facade格局可以将一部分复杂的逻辑进行打包,以方便调用者对那个扑朔迷离逻辑的调用。就类似提供一个联合的假相一般,将中间的子系统封装起来,统一为一个高层次的接口。一个金榜题名的Facade情势示意图如下所示:

皇冠直营现金网官方网 4
图4-4 Facade模式

Facade形式的目标并非要引入一个新的功力,而是在现有功用的功底上提供一个更高层次的虚幻,使得调用者可以一直调用,而不用关爱内部的贯彻形式。以CacheDependency工厂为例,我们须求为调用者提供得到AggregateCacheDependency对象的便捷方法,由此创造了DependencyFacade类:
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;
    }
}

DependencyFacade类封装了获取AggregateCacheDependency类型对象的逻辑,如此一来,调用者可以调用相关方法得到创制连锁器重项的AggregateCacheDependency类型对象:
AggregateCacheDependency dependency =
DependencyFacade.GetCategoryDependency();

比起一向调用DependencyAccess类的GetDependency()方法而言,除了艺术更简约之外,同时它还对CacheDependencyAssembly配置节举办了判断,若是其值为空,则赶回null对象。

在PetShop.Web的App_Code文件夹下,静态类WebUtility的GetCategoryName()和GetProductName()方法调用了DependencyFacade类。例如GetCategoryName()方法:
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);

     // 检查缓存中是或不是存在该数量项;
     string data = (string)HttpRuntime.Cache[cacheKey];
     if (data == null)
     {
           // 通过web.config的配置获取duration值;
           int cacheDuration =
int.Parse(ConfigurationManager.AppSettings[“CategoryCacheDuration”]);
           //
如若缓存中不存在该数据项,则经过作业逻辑层访问数据库获取;
           data = category.GetCategory(categoryId).Name;
           // 通过Facade类创建AggregateCacheDependency对象;
           AggregateCacheDependency cd =
DependencyFacade.GetCategoryDependency();
           // 将数据项以及AggregateCacheDependency 对象存储到缓存中;
           HttpRuntime.Cache.Add(cacheKey, data, cd,
DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration,
CacheItemPriority.High, null);
      }
      return data;
}

GetCategoryName()方法首先会检讨缓存中是或不是曾经存在CategoryName数据项,倘若已经存在,就因而缓存直接获取数据;否则将由此业务逻辑层调用数据访问层访问数据库得到CategoryName,在赢得了CategoryName后,会将新获得的多寡及其DependencyFacade类创设的AggregateCacheDependency对象添加到缓存中。

WebUtility静态类被表示层的众多页面所调用,例如Product页面:
public partial class Products : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Page.Title =
WebUtility.GetCategoryName(Request.QueryString[“categoryId”]);
    }
}

呈现页面title的逻辑是放在Page_Load事件措施中,由此每趟打开该页面都要实践获取CategoryName的方法。即便没有使用缓存机制,当Category数据较多时,页面的显得就会非凡缓慢。

4.3.5  引入Proxy模式

作业逻辑层BLL中与Product、Category、Item有关的事体方法,其落成逻辑是调用数据访问层(DAL)对象访问数据库,以取得相关数据。为了一字不苟系统质量,我们就须要为这个完毕方式增添缓存机制的逻辑。当大家操作扩展了缓存机制的事情对象时,对于调用者而言,应与BLL业务对象的调用保持一致。也即是说,大家要求引入一个新的指标去控制原来的BLL业务对象,这一个新的对象就是Proxy形式中的代理对象。

以PetShop.BLL.Product业务对象为例,PetShop为其成立了代理对象ProductDataProxy,并在GetProductByCategory()等格局中,引入了缓存机制,例如:
public static class ProductDataProxy
{

    private static readonly int productTimeout =
int.Parse(ConfigurationManager.AppSettings[“ProductCacheDuration”]);
    private static readonly bool enableCaching =
bool.Parse(ConfigurationManager.AppSettings[“EnableCaching”]);
       
    public static IList
GetProductsByCategory(string category)
    {
        Product product = new Product();

        if (!enableCaching)
            return product.GetProductsByCategory(category);

        string key = “product_by_category_” + category;
        IList data = (IList )HttpRuntime.Cache[key];

        // Check if the data exists in the data cache
        if (data == null)
        {
            data = product.GetProductsByCategory(category);

            // 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(key, data, cd,
DateTime.Now.AddHours(productTimeout), Cache.NoSlidingExpiration,
CacheItemPriority.High, null);
        }
        return data;
    }
}

与工作逻辑层Product对象的GetProductsByCategory()方法比较,扩大了缓存机制。当缓存内不存在相关数据项时,则平素调用业务逻辑层Product的GetProductsByCategory()方法来获取数据,并将其与相应的AggregateCacheDependency对象一起存储在缓存中。

引入Proxy形式,落成了在缓存级别上对事情对象的包装,增强了对事情对象的主宰。由于揭穿在对象外的法子是千篇一律的,因此对于调用方而言,调用代理对象与真正对象并不曾精神的分别。

从任务分开与分支设计的角度分析,我更期待那几个Proxy对象是被定义在事情逻辑层中,而不像在PetShop的设计那样,被剪切到代表层UI中。别的,如果急需考虑程序的可增添性与可替换性,大家还足以为真正对象与代理对象建立统一的接口或抽象类。不过,单以PetShop的表示层调用来看,拔取静态类与静态方法的艺术,或许尤其合理。大家要求谨记,“过度设计”是软件设计的警戒线。

要是急需对UI层选取缓存机制,将应用程序数据存放到缓存中,就可以调用那个代理对象。以ProductsControl用户控件为例,调用方式如下:
productsList.DataSource =
ProductDataProxy.GetProductsByCategory(categoryKey);

productsList对象属于自定义的CustomList类型,那是一个派生自System.Web.UI.WebControls.DataList控件的类,它的DataSource属性可以承受IList集合对象。
但是在PetShop
4.0的布署中,对于类似于ProductsControl类型的控件而言,拔取的缓存机制是页输出缓存。大家可以从ProductsControl.ascx页面的Source代码中窥见线索:
<%@ OutputCache Duration=”100000″ VaryByParam=”page;categoryId” %>

与ASP.NET 1.x的页输出缓存分裂的是,在ASP.NET
2.0中,为ASP.NET用户控件新引入了CachePolicy属性,该属性的档次为ControlCachePolicy类,它以编程情势贯彻了对ASP.NET用户控件的出口缓存设置。我们能够透过安装ControlCachePolicy类的Dependency属性,来设置与该用户控件相关的看重性项,例如在ProductsControl用户控件中,进行如下的装置:
protected void Page_Load(object sender, EventArgs e)
{
    this.CachePolicy.Dependency =
DependencyFacade.GetProductDependency();
}

应用页输出缓存,并且选拔ControlCachePolicy设置输出缓存,可以将业务数据与任何页面放入到缓存中。那种方法比起应用程序缓存而言,在性质上有很大的抓好。同时,它又经过引入的SqlCacheDependency特性有效地幸免了“数据过期”的败笔,由此在PetShop
4.0中被大规模运用。相反,之前为Product、Category、Item业务对象建立的代理对象则被“投闲散置”,仅仅作为一种设计方法的体现而“幸存”与所有系统的源代码中。

相关文章