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业务对象建立的代办对象则让“投闲散置”,仅仅看做一如既往种植设计方法的显得而“幸存”与整个系统的源代码中。

相关文章