缓存方式
ASP.NET中的缓存大体上分为两种方式:应用程序缓存和页面输出缓存。前者用编程的方式来缓存数据,这是本文主要说明的东西;后者是通过配置每个页面或是Web.config文件来实现的。而页面输出缓存有两种方式,一是缓存整个页面,一是缓存部分页面……这部分的内容以后再说。
自动移除
ASP.NET从缓存中自动移除数据的原因是以下之一:
服务器内存过低;
缓存项过期;
缓存项依赖改变。
应用程序缓存依赖
缓存依赖可以有以下几种方式:键和值依赖;文件依赖,缓存项依赖于一个外部文件,比如XML文件,如果这个文件被修改或删除,则缓存项将被移除;SQL依赖,缓存项依赖于Microsoft SQL Server 2005、SQL Server 2000、或是SQL Server 7.0数据库教程的一个表,对于SQL Server 2005,缓存项可以依赖表中的一条记录;聚合(Aggregate)依赖,缓存项同时依赖于前边的几种情况,比如一个缓存项同时依赖于另一个缓存项和外部文件,如果其中一个改变,则缓存项将被移除;自定义依赖。
如何向缓存中添加缓存项
你可以在应用程序中使用Cache对象访问缓存项。使用Cache对象的Insert方法将一个缓存项添加到应用程序缓存中,该方法有很多重载方法,让你用不同的缓存策略将一个缓存项添加到缓存中。
如果你用Insert方法向缓存中添加缓存项,并且缓存中存在同名缓存项,那么,缓存中的同名缓存项将被替换(覆盖)。
你也可以使用Add方法将缓存项添加到缓存中,添加的方法同Insert一样。Add方法会返回你添加到缓存中的对象。另外,如果使用Add方法添加缓存项,并且缓存中存在同名缓存项,那么这个同名缓存项将不会被替换,也不会产生异常。
本文说明根据不同的缓存策略将缓存项添加到应用程序缓存的方法:
通过设置键和值,直接将一个缓存项添加到应用程序缓存。
通过Insert方法将缓存项添加到应用程序缓存。
添加一个带缓存依赖的缓存项到应用程序缓存。当缓存依赖发生变化时,这个缓存项将被从缓存中移除。你可以设置基于其他缓存项、文件或是多个对象的缓存依赖。
添加一个带过期策略的缓存项到应用程序缓存。除了可以设置缓存依赖外,你也可以设置缓存项在一段时间后过期(滑动过期时间)或是在一个规定的时间过期(绝对过期时间),不能同时规定这两个过期时间。
添加一个带缓存优先级的缓存项到应用程序缓存。缓存优先级可以帮助.NET框架决定哪个缓存项先被移除。
通过Add方法添加一个缓存项到应用程序缓存。
除了以上的缓存依赖外,你可以在数据库的表上创建缓存依赖,或是自定义缓存依赖。通过CacheItemRemovedCallback委托,当从缓存中移除缓存项时,会提示应用程序。
以下是将缓存项添加到应用程序的具体方法:
1) 通过设置键和值,直接将一个缓存项添加到应用程序缓存。下面的代码演示键为“CacheItem1”,值为“Cached Item 1”的缓存项添加到缓存中:
Cache["CacheItem1"] = "Cached Item 1";
2) 通过Insert方法将缓存项添加到应用程序缓存。下面的代码演示用Insert方法,将键为“CacheItem2”,值为“Cached Item 2”的缓存项添加到缓存中:
Cache.Insert("CacheItem2", "Cached Item 2");
3) 添加一个带缓存依赖的缓存项到应用程序缓存,这个缓存依赖可以是其他缓存项,文件,或是与多个对象依赖。当缓存依赖发生变化时,这个缓存项将被从缓存中移除。下面三个例子演示调用Insert方法,并把CacheDependency对象的实例传给该方法,向缓存添加缓存项。
下面的代码演示用Insert方法,将键为“CacheItem3”的缓存项添加到缓存中,该缓存项依赖于名为“CacheItem2”的缓存项:
string[] dependencies = { "CacheItem2" };Cache.Insert("CacheItem3", "Cached Item 3", new System.Web.Caching.CacheDependency(null, dependencies));
下面的代码演示调用Insert方法,将键为“CacheItem4”的缓存项添加到缓存中,该缓存项依赖于文件XMLFile.xml:
Cache.Insert("CacheItem4", "Cached Item 4",new System.Web.Caching.CacheDependency(Server.MapPath("XMLFile.xml")));
下面的例子演示如何用Insert方法创建多缓存依赖,一个缓存项同时依赖于键为“CacheItem1”的缓存项和文件XMLFile.xml。这里使用了聚合缓存依赖的类AggregateCacheDependency类。
System.Web.Caching.CacheDependency dep1 = new System.Web.Caching.CacheDependency(Server.MapPath("XMLFile.xml"));string[] keyDependencies2 = { "CacheItem1" };System.Web.Caching.CacheDependency dep2 = new System.Web.Caching.CacheDependency(null, keyDependencies2);System.Web.Caching.AggregateCacheDependency aggDep = new System.Web.Caching.AggregateCacheDependency();aggDep.Add(dep1);aggDep.Add(dep2);Cache.Insert("CacheItem5", "Cached Item 5", aggDep);
4) 添加一个带过期策略的缓存项到应用程序缓存。除了可以设置缓存依赖外,你也可以设置缓存项在一段时间后过期(滑动过期时间)或是在一个规定的时间过期(绝对过期时间),不能同时规定这两个过期时间。
下面代码演示了时间为1分钟的绝对过期时间:
Cache.Insert("CacheItem6", "Cached Item 6", null, DateTime.Now.AddMinutes(1d), System.Web.Caching.Cache.NoSlidingExpiration);
下面代码演示了时间为10分钟的滑动过期时间:
Cache.Insert("CacheItem7", "Cached Item 7", null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0));
5) 添加一个带缓存优先级的缓存项到应用程序缓存。缓存优先级可以帮助.NET框架决定哪个缓存项先被移除。代码如下:
Cache.Insert("CacheItem8", "Cached Item 8", null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
6) 通过Add方法添加一个缓存项到应用程序缓存。代码如下:
string CachedItem9 = (string)Cache.Add("CacheItem9", "Cached Item 9", null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Default, null);
如何从缓存中检索缓存项
规定缓存项的键值就可以从缓存中检索缓存项的数据。然而,因为保存在缓存中的数据是不稳定的,可能会被ASP.NET移除,所以,推荐的开发方式为应该先确定缓存项是否存在,如果不存在,应该先添加该缓存项到缓存,然后再检索该缓存项。
下面的例子演示,确定名为“CacheItem”的缓存项是否存在,如果存在,则把缓存项的值赋给变量cachedString;否则,把缓存项添加到缓存中,再把该缓存项的值赋给变量cachedString。代码如下:
string cachedString;if (Cache["CacheItem"] != null){ cachedString = (string)Cache["CacheItem"];}else{ Cache.Insert("CacheItem", "Hello, World."); cachedString = (string)Cache["CacheItem"];}
如何移除ASP.NET缓存中的缓存
ASP.NET缓存中的数据是不稳定的,不会被永久地存储,可能会由于以下原因之一而从缓存中被自动移除:
缓存满了
缓存项已经过期
缓存依赖的项改变了
除了可以自动移除缓存项外,你可以显示移除缓存项。另外,当你用Insert方法和Add方法向缓存中添加的缓存项已经存在时,那么,原来的缓存项将被自动删除。
显示移除缓存项
调用Remove方法,将你想要移除的缓存项的键值传给它。下面的代码演示,移除键值为“MyData1”的缓存项:
Cache.Remove("MyData1");
如何通知应用程序缓存项已被移除
在大多数的缓存情况下,当一个缓存项被移除后,你并不需要立刻把该缓存项重新放回到缓存中,除非你再次使用它。典型的做法是,从使用该缓存项开始,就一直检查它。如果该缓存项在缓存中,那么就使用它;否则,重新生成要缓存的内容并把它添加到缓存中再使用。
然而,在有些情况下,通知应用程序,缓存项被移除是非常有用的。比如,你缓存了一个需要一定时间才能生成的报告,当从缓存中移除该报告后(这个报告估计过期了),你想重新生成,并且把它立即放到缓存中,以便下次请求使用,这样,用户就不需要再等待。关键在于,生成这个报告是需要时间的,如果能事先将该报告缓存起来,当用户需要的时候,就可以直接从缓存中获得。
为了使应用程序注意到缓存项已经被移除,ASP.NET提供了CacheItemRemovedCallback委托。委托定义了一个事件的声明,用来对缓存项从缓存中移除做出响应。另外,ASP.NET还提供CacheItemRemovedReason枚举,用来规定缓存项被移除的原因。
例子
下面的例子演示了当缓存项被移除后,ReportManager类如何处理信息,该类管理一个字符串形式的报告。
尽管该类使用了static修饰符,但并不是必须的。然而,处理回调的方法必须不过,删除缓存项时,用于处理回调的方法必须存在。例如,不应在ASP.NET 页中实现回调处理程序,因为在从缓存中删除项之前该页可能已被释放,因此用于处理回调的方法将不可用。为了确保从缓存中删除项时处理回调的方法仍然存在,请使用该方法的静态类。但是,静态类的缺点是需要保证所有静态方法都是线程安全的。
注意
Do not set the CacheItemRemovedCallback to a method in a page. In addition to a page method not being available for a callback after the page is disposed of, pointing the callback to a page method can prevent the memory used by the page from being reclaimed by garbage collection. This happens because the callback contains a reference to the page and the garbage collector will not remove an item from memory if the item has any references. During periods of application load, this could cause memory to be used up very quickly.(有兴趣的话可以看一下这段话)
ReportManager 类的代码
1) 私有成员变量 "_reportRemovedFromCache" 用于判断报告是否被移除;
2) GetReport 方法从缓存中获得报告。如果该报告已经存在,则获得;否则,创建并添加报告到缓存中;
3) CacheReport 方法用于创建并添加报告到缓存中。
在这个共有的成员函数中,使用了六个参数的 Add 方法,其中,第一个和第二个参数分别是缓存项的键和值;第三个参数是缓存依赖,本例子没有缓存依赖,所以为 null;第四和第五个参数是关于过期时间的;第六个参数是缓存优先级;第七个参数是 CacheItemRemovedCallback 委托,只要与 CacheItemRemovedCallback 具有相同签名的函数都可以,本例使用了成员函数 ReportRemovedCallback。
4) CreateReport 方法用于创建报告,一个字符串而已;
5) ReportRemovedCallback 方法用于报告回调。也就是,重新生成报告,并将报告添加到缓存中。
缓存项被移除后提醒应用程序
1) 创建一个类,负责从缓存中检索缓存项,并处理callback方法以便将缓存项重新添加到缓存中。
2) 在这个类中,创建一个方法用来将缓存项添加到缓存中。
3) 在这个类中,创建一个方法,用来从缓存中获得该缓存项。
4) 创建一个方法,用来处理缓存项移除的回调。这个方法必须具有同 CacheItemRemovedCallback 委托的签名相同。
在这个方法中,当缓存项从缓存中移除后,比如,重新生成缓存项,并把它重新添加到缓存,完成你需要的逻辑。
测试缓存项的回调
1) 创建一个ASP.NET Web页面,调用类中的方法,将缓存项添加到缓存中。
下面的代码演示,在Page_Load事件里,调用ReportManager类中的GetReport方法,返回报告的内容,显示在Lable控件:
protected void Page_Load(object sender, EventArgs e){ this.Label1.Text = ReportManager.GetReport();}
2) 在浏览器中请求ASP.NET页面,查看报告。这个报告在第一次请求页面时会被创建,接下来的请求将会从缓存中访问这个报告,知道这个报告被移除。
总结
在ASP.NET下使用缓存技术,我个人觉得有以下几点:
1) 提高应用程序性能的手段在应用程序开发的各个阶段都能体现出来,既有小细节,又有大方向。比如,保证设计一个好的软件逻辑结构;保证使用一个合适的数据结构,是集合、树形结构,还是链表。凡是程序都是由数据结构组成的,一谈到数据结构就一定会涉及到数据的插入、删除、修改和检索、排序这几个基本操作,不同的数据结构对各种操作的性能有很大的差异。上学时,在《数据结构》的书里讲得很清楚,只是当时没什么实践,体会不到罢了;另外,对于类中成员函数的形式参数,是使用值传递,还是引用传递。值传递时,在被调函数中会创建该参数的副本,这肯定会消耗系统内存,而引用传递只是传递一个该数据结构的地址而已……总之,这要求你在实践中慢慢地积累
2) 而使用应用程序缓存是提高应用程序性能的一个非常重要的手段。既可以节省系统资源,有可以提高对用户的相应时间。所以说,缓存技术很有意义。
3) 在前面的几篇里,你也许已经发现了,缓存无非涉及了四个操作,添加、检索(读取)、删除(移除)和回调。“添加”缓存项后,需要时就“检索(读取)”它,不需要或缓存项过期了就“删除(移除)”它,而“回调”是将以上几个操作同应用程序的事件结合起来。很合乎情理。其实,就缓存本身而已,它的操作很简单,一条语句,顶多两条语句就完成了。因此,真正的问题不是前面所说的“添加”、“检索”、“删除”和“回调”如何操作,而是在什么情况下使用缓存,缓存什么东西,如何在软件逻辑结构中实现缓存技术,这才是关键问题