.NET中使用自定义缓存依赖

在 ASP.NET 1.x 中,我们可以使用 CacheDependency 来实现缓存依赖策略,但由于这个类是 sealed 的,我们无法继承这个类来实现我们自己的策略。但是到了 ASP.NET 2.0,我们已经可以从这个类派生出自己的缓存依赖类了。

假定我们要设计一个页面,需要从博客园首页获取最新的贴子信息。为了提高性能,我们希望页面数据仅当博客园首页有更新时才重新生成,否则的话就直接从缓存中获取。如何实现?

一、设计 BlogCacheDependency 类

先分析一下,首先,毫无疑问的,这个类应当从 CacheDependency 派生出来,然后它才能在 Cache 的 Insert 方法中使用,或者被用在 AggregateDependency 类中。

其次,从博客园提供的 RSS 以及页面设计的角度考虑,可以在缓存中放置 RSS 数据,显示的时候使用一个样式转换。而在检查依赖性的时候,我们只需要简单地比较一下当前的 RSS 与网站的 RSS 是否相同就可以了。

一个比较重要的问题是:我们何时去检查比较 RSS 数据?在每次请求的时候吗?显然不行,这样一来跟不使用缓存几乎没什么区别,甚至实际上加重了无谓的负担。考虑在没有请求的时候进行检查呢?我们可以使用一个 Timer 来控制,让它定期去检查一个是否有更新,如果有更新则通知依赖发生了改变。

我们知道 CacheDependency 类有一个 HasChanged 属性,但是当 BlogCacheDependency 检查到依赖改变时如何告诉它的基类呢?这就是在 ASP.NET 2.0 中 CacheDependency 类中新增的 NotifyDependencyChanged 方法的使命了。

此外为了便于重用,BlogCacheDependency 类须得有一个 feed 数据,用来保存我们要获取的 RSS 数据的 URL。还要有一个时间间隔,便于在使用的时候调整刷新速度。

好,看看实际的实现代码:

1public class BlogCacheDependency : CacheDependency
2{
3 private Timer _tickTimer;
4 private int _timeInterval;
5 private XPathNavigator _rss;
6 private string _feed;
7
8 public XPathNavigator RSS
9 {
10 get
11 {
12 return _rss;
13 }
14 }
15
16 public BlogCacheDependency(string feed,int timeInterval)
17 {
18 _feed = feed;
19 _timeInterval = timeInterval;
20 _rss = GetRSS();
21 _tickTimer = new Timer(new TimerCallback(CheckDependencyCallback),
22 this,_timeInterval * 1000,_timeInterval * 1000);
23 }
24
25 private XPathNavigator GetRSS()
26 {
27 XPathDocument rssDoc = new XPathDocument(_feed);
28 return rssDoc.CreateNavigator();
29 }
30
31 public void CheckDependencyCallback(object sender)
32 {
33 BlogCacheDependency bcd = sender as BlogCacheDependency;
34 XPathNavigator newRSS = GetRSS();
35 if (newRSS.OuterXml != _rss.OuterXml)
36 {
37 bcd.NotifyDependencyChanged(bcd,EventArgs.Empty);
38 }
39 }
40
41 protected override void DependencyDispose()
42 {
43 _tickTimer = null;
44 base.DependencyDispose();
45 }
46}

这里,BlogCacheDependency 的构造函数中使用 _tickTimer 实现了一个定时检查更新的机制,它根据设定的时间间隔去调用 CheckDependencyCallback 方法。

而 CheckDependencyCallback 方法则将两个 RSS 信息进行比较,如果不同,则调用 NotifyDependencyChanged 方法通知基类,相应的缓存依赖已经发生了变化,缓存中的数据应当被清除。

二、页面设计

下面是页面代码(有删节),其中显示了 BlogCacheDependency 的使用方法:
1<script runat="server">
2 protected void Page_Load(object sender,EventArgs e)
3 {
4 string feed = "http://www.cnblogs.com/RSS.aspx";
5 if (Cache[feed] == null)
6 {
7 BlogCacheDependency bcd = new BlogCacheDependency(feed,600);
8 Cache.Insert(feed,bcd.RSS,bcd);
9 Label1.Text = "当前数据为刚刚获取,并已更新入缓存!";
10 }
11 else
12 {
13 Label1.Text = "当前数据系从缓存中取得!";
14 }
15 RssXml.XPathNavigator = Cache[feed] as System.Xml.XPath.XPathNavigator;
16 RssXml.TransformSource = "translate.xsl";
17 }
18</script>
19
20<body>
21 <form id="form1" runat="server">
22 博客园最新贴子:
23 <br />
24 <asp:Xml ID="RssXml" runat="server" />
25 <br />
26 <asp:Label ID="Label1" runat="server" ForeColor="red" />
27 </form>
28</body>

本例中设定的访问博客园首页最新贴子列表,时间间隔为600秒,即每10分钟检查一次更新情况。

值得注意的地方:

1、BlogCacheDependency 类中的 DependencyDispose 方法作何用?它与 Dispose 方法有何区别?让我们想一想,如果说某一次检查更新时,已经发现依赖变化了,但是却一直没有再次发送请求,那么这时会不会始终连续不断按间隔地执行 CheckDependencyCallback 方法呢?如果真的如此的话,那岂不是完全多余,因为只要查到一次有变化就不必再查了嘛。而如果我们进行跟踪或是记录日志的话可以发现,实际上只要查到依赖变化以后就不会再次 Check 了。奥妙在哪里?想一想就能知道 NotifyDependencyChanged 方法大有玄机,而且之所以会有 DependencyDispose 方法的原因其实也就在这里。其中的设计思想,值得我们细细品味吧。

2、通过这个例子,我们可以继续完善,如果定时检测更新状态的是数据表呢?我们不可能每张数据表都建立一个定时器进行更新状态检测吧,这样就和进行实际的数据访问没有区别了。所以我们是否可以考虑在应用程序下运行着一个主线程,它负责检测各数据表的更新状态。如果发现有数据表更新了,则将状态保存到一个静态变量中,而在自定义依赖程序中只要定时去访问这个变量就OK了,不是吗?

相关文章

什么是设计模式一套被反复使用、多数人知晓的、经过分类编目...
单一职责原则定义(Single Responsibility Principle,SRP)...
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强...
适配器模式将一个类的接口转换成客户期望的另一个接口,使得...
策略模式定义了一系列算法族,并封装在类中,它们之间可以互...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,...