### ASP.NET Caching

Caching
缓存，就是将一些生成代价比较大的常用数据，保存起来重用
。一般数据都保存在内存中，因为从内存中读取数据比从数据库等其他地方要快。
ASP.NET 通过两种方式支持缓存：通过 Cache API 存储任意数据，通过页面输出缓存经常被访问的页面。
让我们来看一个例子。
一个电子商务的站点， 它的目录一般一周更新一次。站点提供了一套用户界面让客户订购产品。当一个客户浏览目录时，系统将通过网络去查询数据库，进行各种计算，最后返回结果。
从服务器查询这些目录数据的操作是很频繁的。我们知道，这些数据一周才变化一次。所以以下的几种操作将带来性能上的损失。
1. 执行 ASP.NET 的程序生成对数据库的查询语句。
2. 通过网络，和数据库服务器进行通讯。
3. 数据库服务器编译执行查询（或是执行储存过程）。
缓存机制可以减少许多这样的工作，提高应用的性能和伸缩性。我们能缓存结果以便能静态的处理客户请求，来提高性能。同时，由于减少了处理每个请求使用的资源，因此也提高了系统的伸缩性。
Cache API
对于 ASP 开发人员来说，把常用的数据保存在内存里，并不是一个全新的概念。在 ASP 里，有两个对象完成它。
&#8226; Session 对象
&#8226; Application 对象
Session 用来保存单个用户在多个请求间共享的数据，虽然在 ASP.NET 中有一些小的改动，但这些改动主要是在应用级的，对 Session 对象来说，仍然是一个保存键与键值对的集合。Application 对象在 ASP.NET 也被保存下来了，同样是键与键值对的集合。在 ASP 和 ASP.NET 中，我们都可以使用以下代码来操作 Application 对象
Application("SomeInterestingData") = "Example data"
Response.Write(Application("SomeInterestingData")
我们可以使用同样的方法访问 Session 对象。
ASP.NET 带来了一个新的键与键值的对象—Cache.除了存储键与键值对外，Cache 对象还提供了另外的一些存储短期数据的新功能：
依赖—当一个键插入 Cache 对象时，我们可以设置它的依赖性。当依赖的对象改变时，这个键将被删除。现在支持的依赖对象有文件，其他的键和时间。
自动失效—没有依赖的键值，当使用频率不高时，将被自动删除。
支持回调—当一个键将被删除时，我们可以得到一个事件，在这个事件中来更新键值或取消删除操作。
当我们在使用 Cache 对象时，必须注意这一点：
使用 Cache 对象中的键值之前,必须每一次都检查键值是否存在。
由于在 Cache 对象中的键值由于其依赖或者使用频率较低，都会被删除，所以，每一次使用 Cache 中的对象，都必须检查是否存在。
例如，我们可以用如下一段代码来返回 DataSet.
Private Function LoadDataSet() As DataSet
Dim sqlConnection As SQLConnection
Dim sqlAdapater As SQLDataSetCommand
Dim datasetProducts As New DataSet()
Dim sqlDSN As String
Dim sqlSelect As String
' Connection String and Select statement
sqlDSN = "server=localhost;uid=sa;pwd=;database=grocertogo"
sqlSelect = "Select * From Products"
' Connect
sqlConnection = new SQLConnection(sqlDSN)
sqlAdapater = new SQLDataSetCommand(sqlSelect, sqlConnection)
' Fill dataset create product table
sqlAdapter1.FillDataSet(datasetProducts, "products")
Return products
End Function
我们很容易用 Cache 对象来改写这段代码，使只在 DataSet 不存在在 Cache 中才调用 LoadDataSet()。
Public Function GetProductData() As DataSet
If (IsNothing(Cache("ProductData")) Then
Cache("ProductData") = LoadDataSet()
Return Cache("ProductData")
End Function
Cache 对象在许多地方和 Application 对象有许多相似之处，而最大的不同是 Cache 支持了依赖。
Cache Dependencies
依赖可以使键值在文件改变时，或是在某一个指定时间被删除
。让我们看一下每种依赖。
文件依赖(File-based Dependency)
文件依赖是指，当磁盘上的一个文件改变时，删除 Cache 对象中的对应一项。下面让我们看一个从 XML 文件中读取数据的例子。
Dim dom As XmlDocument()
dom.Load(Server.MapPath("product.xml")
Cache("ProductData") = dom
当 product.xml 文件内容改变时，我们应该使 Cache 中的数据失效。假设 product.xml 在 aspx 文件的同一目录中，我们可以使用以下的代码：
Dim dependency as new CacheDependency(Server.MapPath("product.xml"))
Cache.Insert("ProductData", dom, dependency)
在这段代码中，我们建立了一个 CacheDependecy 类的实例 dependency，并将 product.xml 的路径传给这个实例。然后使用 Insert 方法建立一个依赖于文件的键值。
时间依赖(Time-based Dependency)
时间依赖就是在指定时间删除 Cache 对象中的一项。同样我们可以使用 Insert 方法来加入有时间依赖的键值。
绝对时间—设置一个绝对的时间。例如，10 分钟以后，删除此项。
相对时间—当一个 Cache 有若干时间没有被访问，删除此项。
以下是一段代码，使用相对时间依赖，使得 ProductData 当有 10 分钟没有被访问的话，将被删除。每一次对 ProductData 的访问，都将使 ProductData 保持另一个 10 分钟的有效。
' 10 minute time span
Cache.Insert("ProductData", LoadDataSet(), Nothing, DateTime.MaxValue, TimeSpan.FromSeconds(10))
以上是对 Cache API 的简单讨论，大家从这里可以看出这套接口的简单易用。ASP.NET 使用这套 API 实现了页面输出缓存。
页面输出缓存(Page Output Caching)
ASP.NET 的页面输出缓存将把这个页面的内容放在 Cache 中。我们已经看过如何将一个数据集放入缓存。那么，能不能将整个页面缓存呢？这样的话，就不需要对每一个请求都执行代码，可以直接从内存中获取结果，这将带来巨大的性能提高。
对页面缓存有两种接口，高层接口和底层接口。这里我们只讨论高层接口。（对于底层接口，可以参考 MSDN 中的相关文章）
高层接口包括两个页面指示符，一个表明页面刷新的时间间隔，一个表明当某个参数变化时刷新页面。
<%@ OutputCache Duration="10" % VaryByParam="None">
将这个指示符放在页面上部，页面结果将被缓存 10 秒。10 秒以后，这个页面将被重新执行。下面是一个例子。
<%@ OutputCache Duration="10" % VaryByParam="None">
<Script runat="server">
Public Sub Page_Load()
span1.InnerHtml = DateTime.Now.ToString("r")
End Sub
</Script>
<font size=6>The time is: <font color=red><span id="span1" runat="server"/></font></font>
这个页面在第一次请求时被执行一次，显示执行时的时间。这个结果将在缓存里保存 10 秒。假如我们在 10：30：12 时请求这个页面，在此后的 10 秒内，我们将会看到同样的输出。
结论
对于 ASP 程序员来说，缓存是一项易用而强大的新技术。它的使用和 ASP 原来的 Application，session 对象有许多相似的地方，只是在 Cache 对象中的数据会自动失效。Cache 还支持文件，时间的依赖，更是支持回调。缓存技术还被用来实现了页面缓存。