19.2 输出缓存
前面已经阐述过,输出缓存可以缓存ASP.NET页所生成的部分响应或所有响应。可以在发出请求的浏览器、响应请求的Web服务器以及请求或响应流中任何其他具有缓存功能的设备(如代理服务器)上缓存页。缓存提供了一个强有力的方式来提高Web应用程序的性能。缓存功能允许利用缓存满足对页的后续请求,这样就不需要再次运行最初创建该页的代码。对站点中访问最频繁的页进行缓存可以充分地提高Web服务器的吞吐量。
19.2.1 使用@OutputCache指令以声明的方式设置缓存
@OutputCache指令以声明的方式控制ASP.NET页或页中包含的用户控件的输出缓存策略,如下所示:
<%@OutputCache Duration="#ofseconds"
Location="Any|Client|Downstream|Server|None|
ServerAndClient"
Shared="True|False"
VaryByControl="controlname"
VaryByCustom="browser|customstring"
VaryByHeader="headers"
VaryByParam="parametername"
VaryByContentEncoding="encodings"
CacheProfile="cache profile name|''"
NoStore="true|false"
SqlDependency="database/table name pair|CommandNotification"
%>
其中,@OutputCache指令的相关特性解释如下:
1)Duration特性为必选项,用于设置页或用户控件进行缓存的时间(以秒计)。如下面的代码设置缓存时间为20秒:
Duration="20"
在页或用户控件上设置Duration特性为来自对象的HTTP响应建立了一个过期策略,并将自动缓存页或用户控件输出。
2)Location特性是OutputCacheLocation枚举值之一,默认值为Any(即Location="Any")。它指定有效值,用于控制资源的输出缓存HTTP响应的位置。其中,OutputCacheLocation枚举值如表19-1所示。
最后需要注意的是,包含在用户控件(.ascx文件)中的@OutputCache指令不支持此特性。
3)CacheProfile该特性表示与该页关联的缓存设置的名称,默认值为空字符串("")。与Location特性一样,包含在用户控件(.ascx文件)中的@OutputCache指令不支持该特性。在页(.aspx文件)中指定此属性时,属性值必须与outputCacheSettings节下面的outputCacheProfiles元素中的一个可用项的名称匹配。如果此名称与配置文件项不匹配,将引发异常。
4)NoStore特性决定了是否阻止敏感信息的二级存储。同样,包含在用户控件文件(.ascx文件)中的@OutputCache指令不支持该特性。如果将该特性设置为true,则等效于在请求期间执行以下代码:
Response. Cache.SetNoStore();
5)Shared特性用于确定用户控件输出是否可以由多个页共享。默认值为false。值得注意的是,包含在ASP.NET页(.aspx文件)中的@OutputCache指令不支持该特性。
6)SqlDependency特性标识一组数据库/表名称对的字符串值,页或控件的输出缓存依赖于这些名称对。值得注意的是,SqlCacheDependency类监视输出缓存所依赖的数据库中的表,因此当更新表中的项时,使用基于表的轮询将从缓存中移除这些项。如果以值CommandNotification使用通知(在Microsoft SQL Server 2005或以上版本),则最终会使用SqlDependency类向SQL Server 2005或以上版本的服务器注册查询通知。并且SqlDependency特性的CommandNotification值仅在网页(.aspx)中有效,而用户控件只能将基于表的轮询用于@OutputCache指令。
7)VaryByCustom特性表示自定义输出缓存要求的任意文本。如果特性的赋值为browser,缓存将因浏览器名称和主要版本信息的不同而异;如果输入自定义字符串,则必须在应用程序的Global.asax文件中重写GetVaryByCustomString方法。
8)VaryByHeader特性表示分号分隔的HTTP标头列表,用于使输出缓存发生变化。将该特性设为多标头时,对于每个指定标头组合,输出缓存都包含一个不同版本的请求文档。
这里需要说明的是,设置VaryByHeader特性将启用在所有HTTP 1.1版缓存中缓存项,而不仅仅在ASP.NET缓存中进行缓存。包含在用户控件文件(.ascx文件)中的@OutputCache指令不支持该特性。
9)VaryByParam特性表示分号分隔的字符串列表,用于使输出缓存发生变化。默认情况下,这些字符串对应于使用GET方法特性发送的查询字符串值,或者使用POST方法发送的参数。将该特性设置为多个参数时,对于每个指定参数组合,输出缓存都包含一个不同版本的请求文档。可能的值包括none、星号(*)以及任何有效的查询字符串或POST参数名称。
需要特别注意的是,在ASP.NET页和用户控件中使用@OutputCache指令时,必须要设置该特性或VaryByControl特性。如果没有包含它们,则发生分析器错误。如果不希望通过指定参数来改变缓存内容,请将值设置为none。如果希望通过所有的参数值改变输出缓存,请将特性设置为星号(*)。
10)VaryByControl特性表示一个分号分隔的字符串列表,用于更改用户控件的输出缓存,而这些字符串代表用户控件中声明的ASP.NET服务器控件的ID属性值。与VaryByParam特性一样,在ASP.NET页和用户控件中使用@OutputCache指令时,必须要设置该特性或VaryByParam特性。如果没有包含它们,则发生分析器错误。
11)VaryByContentEncodings特性表示以分号分隔的字符串列表,用于更改输出缓存。将VaryByContentEncodings特性用于Accept-Encoding标头,可确定不同内容编码获得缓存响应的方式。
学习完OutputCache指令之后,就可以在页面或者用户控件中以声明方式设置输出缓存,而自己需要做的仅仅是在页面和用户控件中设置一个OutputCache指令。例如,下面在页面(.aspx)加入一个OutputCache指令:
<%@OutputCache Duration="20"VaryByParam="None"%>
在上面这个OutputCache指令中,Duration特性告诉ASP.NET将页面缓存20秒。为了测试它,可以在后台代码里加入如下代码:
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(DateTime.Now.ToString());
}
现在运行测试页面,会发现一个有趣的行为。第一次访问页面时会显示当前的时间,如果在短时间(小于20秒)后刷新页面,页面的时间不会更新,而ASP.NET会自动传送缓存的HTML输出。缓存过期(超过20秒)后,ASP.NET将会接受到一个新的请求,再次运行页面,将产生一个新的缓存副本,并在以后的20秒内使用它。
当然,系统并不会因为你请求在缓存中保存页面20秒,而保证为你保存20秒。相反,有时候系统可能会因为内存紧张而提前把页面从缓存中移除。这样也可以保证你可以自由地使用缓存,而无须担心因为大量地使用缓存而影响应用程序。
上面示例将VaryByParam特性设置为None,它告诉ASP.NET只要保存缓存页面的一个副本就可以了,这对所有场景都适用。如果对页面的请求在URL中添加了查询字符串(如WebForm1.aspx?id=1),这也不会产生任何差异,只要缓存没有过期,ASP.NET就会重用相同的输出。
其实,对于“对页面的请求在URL中添加了查询字符串”这种情况,ASP.NET缓存提供了另外的设置方案,即可以将VaryByParam特性设置为“*”。它表示页面要使用查询字符串,同时告诉ASP.NET按照不同的查询字符串参数来缓存页面的独立副本。如下面的代码所示:
<%@OutputCache Duration="20"VaryByParam="*"%>
现在当请求带有查询字符串信息的页面时,ASP.NET会首先检查查询字符串。如果字符串和以前的请求匹配且该页面的缓存副本存在,那么它将被重用。否则,ASP.NET会创建一个新的页面并单独缓存它。
除了将VaryByParam特性设置为“*”之外,通常,通过名称明确地指定重要的查询字符串变量会更好一些。如下面的代码所示:
<%@OutputCache Duration="20"VaryByParam="id"%>
这样,ASP.NET会在查询字符串中查找id参数。使用不同的id参数的请求被分别缓存,而其他所有参数被忽略。当然,也可以使用分号分隔,来指定多个参数。如下面的代码所示:
<%@OutputCache Duration="20"VaryByParam="id;list"%>
这样,ASP.NET会按照查询字符串中的id和list参数来分别缓存独立版本的页面。
19.2.2 ASP.NET中的缓存配置
虽然在页面直接设置@OutputCache指令是一种相对简单且清晰的方法,但如果创建了数十个缓存页面,并且每个缓存页面的缓存设置相同,由于某种原因,需要修改它们的缓存设置,如将缓存时间从20秒改到10秒,则不得不逐个页面地进行修改,然后重新编译这些页面。而这些重复的设置工作对于程序员来说是一件极其讨厌、容易出错(因为大意而少设置一个页面)的工作,因此应该避免。
针对上面的问题,ASP.NET允许你对一组页面应用相同的缓存设置,即在配置文件Web.config中定义缓存设置,这些设置和一个名字关联,然后就可以使用这个名字对多个页面应用这些设置了。这样,只需要修改Web.config中的缓存设置,而无须逐个页面地进行修改,从而增加了生产力。
下面的代码创建了一个名为CacheProfile1的OutputCacheProfile,它将缓存时间定义为10秒:
<configuration>
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheProfile1"duration="10"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
</configuration>
现在就可以在页面里通过CacheProfile特性来使用这个缓存配置了。如下面的代码所示:
<%@OutputCache CacheProfile="CacheProfile1"VaryByParam="None"%>
当然,还可以设置其他的一些特性。如下面的代码所示:
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheProfile1"duration="10"varyByParam="*"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
除此之外,还可以在cache元素里配置ASP.NET缓存的各种行为。如下面的代码所示:
<configuration>
<system.web>
<caching>
<cache disableMemoryCollection="true|false"
disableExpiration="true|false"
percentagePhysicalMemoryUsedLimit="number"
privateBytesLimit="number"
privateBytesPollTime="HH:MM:SS"/>
……
</caching>
</system.web>
</configuration>
其中:
1)disableMemoryCollection获取或设置一个值,该值指示当计算机处于内存压力下时是否禁止执行缓存内存回收。
2)disableExpiration获取或设置一个值,该值指示是否禁用缓存过期。如果禁用,则缓存项不会过期,并且不会对过期缓存项执行后台清理。
3)privateBytesLimit获取或设置一个值,该值指示在缓存开始刷新过期项并尝试回收内存之前应用程序的最大专用字节大小。此限制同时包括缓存所使用的内存量以及运行应用程序的正常内存开销。如果设置为零,则指示ASP.NET将使用自己的试探法确定何时开始回收内存。
4)percentagePhysicalMemoryUsedLimit获取或设置一个值,该值指示在缓存开始刷新过期项并尝试回收内存之前应用程序可使用的最大计算机物理内存百分比。该内存使用率同时包括缓存使用的内存以及正运行的应用程序的正常内存使用率。如果设置为零,则指示ASP.NET将使用自己的试探法确定何时开始回收内存。
5)privateBytesPollTime获取或设置一个值,该值指示两次轮询应用程序专用字节内存使用量之间的时间间隔。
19.2.3 自定义缓存控制
其实,除了按浏览器类型和参数进行不同的输出缓存行为外,还可以根据定义的方法所返回的不同字符串对页输出的多个版本进行缓存。这样的代码检查任意合适的信息并返回一个字符串,ASP.NET使用这个字符串实现缓存。如果代码为不同的请求生成了相同的字符串,ASP.NET就会重用缓存页面;如果代码生成了新值,ASP.NET会产生一个新的缓存版本并独立保存它。
若要以声明的方式设置自定义字符串,请在@OutputCache指令中包括VaryByCustom特性,并将该属性设置为要作为进行不同输出缓存行为的依据的字符串。例如,下面的指令将根据自定义字符串“browser”改变页输出。
<%@OutputCache Duration="10"VaryByParam="None"
VaryByCustom="browser"%>
接下来,你需要在应用程序的Global.asax文件中重写GetVaryByCustomString方法以指定自定义字符串的输出缓存行为。
被重写的GetVaryByCustomString方法接受在VaryByCustom特性中设置的字符串,作为它的custom参数。例如,有些页可能根据请求浏览器的次版本进行缓存。对于这些页,可以将VaryByCustom特性设置为"browser"。然后,在被重写的GetVaryByCustomString方法中,可以检查custom参数,并根据custom参数的值是否为"browser"返回不同的字符串。详细示例如下面的代码所示:
public override string GetVaryByCustomString(HttpContext context,
string custom)
{
if(custom=="browser")
{
string bName;
bName=Context.Request.Browser.Browser;
bName+=Context.Request.Browser.MajorVersion.ToString();
return bName;
}
else
{
return base.GetVaryByCustomString(context, custom);
}
}
其实,GetVaryByCustomString方法的基本实现已经包含了基于浏览器缓存的逻辑,也就是说,完全可以不编写前面所示的方法。GetVaryByCustomString方法的基本实现基于浏览器的名称和主版本号创建缓存字符串。如果希望改变这种实现逻辑,那么可以像上面一样重写GetVaryBy CustomString方法。
除此之外,还可以根据指定的HTTP标头的值对某页的多个版本进行缓存。当请求页时,可以指定按传递到应用程序的单个标头、多个标头或所有标头进行缓存。如下面的示例将页缓存20秒,并根据随Accept-Language HTTP标头传递的值设置要缓存的页的版本:
<%@OutputCache Duration="20"VaryByParam="None"
VaryByHeader="Accept-Language"%>
19.2.4 使用HttpCachePolicy类以编程的方式设置缓存
在ASP.NET中,除了可以使用@OutputCache指令以声明的方式来设置缓存之外,还可以使用HttpCachePolicy类的SetCacheability方法为页指定HttpCacheability值,从而以编程方式设置该页的可缓存性。可以通过Res ponse类的Cache属性来访问该方法,Res ponse.Cache属性提供HttpCachePolicy类的一个实例。
其中,HttpCacheability枚举中的值用来设置可缓存性,它提供了用于设置Cache-Control HTTP标头的枚举值。如表19-2所示,前三个值与Cache-Control HTTP头设置直接对应,后三个值为特殊值。
HttpCachePolicy类包含了用于设置缓存特定的HTTP标头的方法和用于控制ASP.NET页输出缓存的方法。它的常用方法如表19-3所示。
除了表19-3中的这些方法之外,HttpCachePolicy类还提供了如下3个属性:
1)VaryByContentEncodings获取用于改变输出缓存的Content-Encoding标头的列表。
2)VaryByHeaders获取用于改变缓存输出的所有HTTP标头的列表。
3)VaryByParams获取影响缓存的参数的列表,这些参数由HTTP GET或HTTP POST接收。
现在就可以在页的代码中调用Response对象的Cache属性的SetCacheability方法来为页指定HttpCacheability值,从而以编程方式设置该页的可缓存性。示例代码如下所示:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);
Response.Cache.VaryByParams["*"]=true;
Response.Write(DateTime.Now.ToString());
}
19.2.5 部分页缓存
我们知道,有时候缓存整个页面是根本不现实的,因为页的某些部分可能在每次请求时都需要更改。而在这种情况下,只能够缓存页的一部分。要完成这样的功能,有两个选择:部分页缓存和缓存后替换。
对于部分页缓存,可以找出页中需要缓存的内容,把它们封装到一个专用的用户控件内,然后缓存该控件的输出。例如,如果创建的页显示大量动态内容(如股票信息),但也有某些部分是静态的(如每周摘要),则可以在用户控件中创建这些静态部分并将用户控件配置为缓存。这样,通过创建用户控件来缓存内容,可以将页上需要花费宝贵的处理器时间来创建的某些部分(例如数据库查询)与页的其他部分分离开,而只需占用很少服务器资源的部分可以在每次请求时动态生成。因此,也可以将部分页缓存称为控件缓存或者片段缓存。
在标识了要缓存的页的部分,并创建了用以包含这些部分中的每个部分的用户控件后,就必须要确定这些用户控件的缓存策略。可以使用@OutputCache指令,或者在代码中使用PartialCachingAttribute类,以声明的方式为用户控件设置这些策略。
例如,在用户控件文件(.ascx文件)里使用@OutputCache指令,如下面的代码所示:
<%@OutputCache Duration="20"VaryByParam="None"%>
若要在代码中设置缓存参数,可以在用户控件的类声明中使用一个特性。例如,如果在类声明的元数据中包括下面的特性,则该内容的一个版本将在输出缓存中存储20秒:
[PartialCaching(20)]
public partial class CachedControl:System.Web.UI.UserControl
{
……
}
在以声明的方式创建可缓存的用户控件时,可以包括一个ID特性,以便以编程方式引用该用户控件实例。但是,在代码中引用用户控件之前,必须验证在输出缓存中是否存在该用户控件。缓存的用户控件只在首次请求时动态生成,在指定的时间到期之前,从输出缓存满足所有的后续请求。确定用户控件已实例化后,可以从包含页以编程方式操作该用户控件。例如,如果通过声明方式将MyUserControl的ID分配给用户控件,则可以使用下面的代码检查它是否存在。
protected void Page_Load(object sender, EventArgs e)
{
if(MyUserControl!=null)
{
……
}
}
除此之外,还可以为页和页上的用户控件设置不同的输出缓存持续时间值。如果页的输出缓存持续时间长于用户控件的输出缓存持续时间,则页的输出缓存持续时间优先。也就是说,如果页的输出缓存设置为100秒,而用户控件的输出缓存设置为50秒,则包括用户控件在内的整个页将在输出缓存中存储100秒,而与用户控件较短的时间设置无关。
不过,如果页的输出缓存持续时间比用户控件的输出缓存持续时间短,则即使已为某个请求重新生成该页的其余部分,也将一直缓存用户控件直到其持续时间到期为止。也就是说,如果页的输出缓存设置为50秒,而用户控件的输出缓存设置为100秒,则页的其余部分每到期两次,用户控件才到期一次。
19.2.6 缓存后替换
缓存后替换与部分页缓存正好相反。它对页进行缓存,但是页中的某些片段是动态的,因此不会缓存这些片段。例如,如果创建的页在设定的时间段内完全是静态的(例如新闻报道页),可以设置为缓存整个页。如果为缓存的页添加旋转广告横幅,则在页请求之间广告横幅不会变化。然而,使用缓存后替换,可以对页进行缓存,但可以将特定部分标记为不可缓存。在本例中,将广告横幅标记为不可缓存,它们将在每次页请求时动态创建,并添加到缓存的页输出中。
在ASP.NET中,可以使用如下三种方法来实现缓存后替换。
1.以声明的方式使用Substitution控件
在ASP.NET中,Substitution控件为要缓存大部分内容的页提供了一种缓存局部页的简化解决方案。可以对整页进行输出缓存,然后使用Substitution控件指定页中免予缓存的部分。需要缓存的区域只执行一次,然后从缓存读取,直至该缓存项到期或被清除;而动态区域则在每次请求页时执行。由于不必对这些部分进行封装以缓存在Web用户控件中,因此,此缓存模型简化了主要是静态内容的页的代码。
其中,Substitution控件的声明如下:
<asp:Substitution
EnableTheming="True|False"
EnableViewState="True|False"
ID="string"
MethodName="string"
OnDataBinding="DataBinding event handler"
OnDisposed="Disposed event handler"
OnInit="Init event handler"
OnLoad="Load event handler"
OnPreRender="PreRender event handler"
OnUnload="Unload event handler"
runat="server"
SkinID="string"
Visible="True|False"
/>
Substitution控件执行时,会调用一个返回字符串的方法,该方法返回的字符串即为要在页中的Substitution控件的位置上显示的内容。与此同时,可以使用Substitution控件的MethodName属性来指定要在Substitution控件执行时调用的回调方法的名称。这里指定的回调方法必须是包含Substitution控件的页或用户控件的静态方法。回调方法的签名必须与接受HttpContext参数并返回字符串的HttpResponseSubstitutionCallback委托的签名匹配。
下面的代码示例演示了如何以声明方式将Substitution控件添加到输出缓存网页。加载页面时,将在Label标签中向用户显示当前的日期和时间,并且,此区域仅20秒便缓存和更新一次。而当Substitution控件执行时,将调用GetCurrentDateTime方法。GetCurrentDateTime返回的字符串(当前的日期和时间)将显示给用户。每次刷新页时,都不会缓存和更新页中的这一部分。其中,页面代码如下所示:
<%@Page Language="C#"AutoEventWireup="true"
CodeBehind="WebForm1.aspx.cs"Inherits="_19_1.WebForm1"%>
<%@OutputCache Duration="20"VaryByParam="none"%>
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1"runat="server">
Substitution1:
<asp:Substitution ID="Substitution1"
MethodName="GetCurrentDateTime"runat="Server">
</asp:Substitution>
<br/>
label:
<asp:Label ID="label1"runat="Server">
</asp:Label>
<br/>
<br/>
<asp:Button ID="RefreshButton"Text="刷新页面"
runat="Server">
</asp:Button>
</form>
</body>
</html>
定义好页面之后,就需要在后台代码里定义GetCurrentDateTime方法并给label控件赋值。如下面的代码所示:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
label1.Text=DateTime.Now.ToString();
}
public static string GetCurrentDateTime(HttpContext context)
{
return DateTime.Now.ToString();
}
}
示例运行结果如图19-1所示,每次单击“刷新页面”按钮时,只要页面的缓存时间没有过期,label 1控件中显示的时间就不会变。而Substitution1控件的时间则在每次单击“刷新页面”按钮时动态变化。
图 19-1 示例运行结果
2.以编程方式使用Substitution控件API
若要以编程方式为缓存页创建动态内容,则可以在页代码中将某个方法的名称作为参数传递给WriteSubstitution方法来调用该方法。该方法处理动态内容的创建,它采用单个HttpContext参数并返回一个字符串。该返回字符串是将在给定位置被替换的内容。如下面的示例代码所示:
public partial class WebForm1:System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(DateTime.Now.ToString());
Response.Write("<br/>");
Response.WriteSubstitution(new
HttpResponseSubstitutionCallback(GetCurrentDateTime));
}
public static string GetCurrentDateTime(HttpContext context)
{
return DateTime.Now.ToString();
}
}
其实,通过调用WriteSubstitution方法来代替以声明方式使用Substitution控件的一个好处是可以调用任意对象的方法,而不只是调用Page或UserControl对象的静态方法。除此之外,调用WriteSubstitution方法可以将客户端可缓存性更改为服务器可缓存性,以便该页不会在客户端上进行缓存。这样,可以确保以后对该页的请求能够再次调用该方法以生成动态内容。
3.以隐式方式使用AdRotator控件
AdRotator服务器控件在内部实现对缓存后替换的支持。如果将AdRotator控件放在页面上,则无论是否缓存父页,都将在每次请求时呈现其特有的广告。因此,包含AdRotator控件的页面只在服务器端进行缓存。
19.2.7 检查缓存页的有效性
上面已经阐述过,用户请求缓存页时,ASP.NET会根据在该页中定义的缓存策略来确定缓存输出是否仍然有效。如果缓存输出有效,则将输出发送到客户端,并且不重新处理该页。
其实,除了上面的方法之外,还可以以编程的方式来检查缓存页的有效性。ASP.NET提供了使用验证回调在该验证检查期间运行代码的功能,因此可以编写自定义逻辑来检查该页是否有效。利用验证回调,可以使在使用缓存依赖项的正常进程之外的缓存页无效。
如果要以编程的方式来检查缓存页的有效性,首先需要定义HttpCacheValidateHandler类型的事件处理程序,并包括检查缓存页响应的有效性的代码。
HttpCacheValidateHandler委托表示一个方法,在从缓存提供某个缓存项之前将调用该方法来验证该项。它的原型如下:
public delegate void HttpCacheValidateHandler(
HttpContext context,
Object data,
ref HttpValidationStatus validationStatus
)
其中,context参数包含有关当前请求的信息的HttpContext对象;data参数用于验证缓存项的用户提供的数据;validationStatus参数则是HttpValidationStatus的枚举值,委托应设置该值来指示验证的结果,即验证处理程序必须返回下列HttpValidationStatus值之一:
1)Invalid指示缓存页无效,将从缓存中移除该页,并且该请求将被作为缓存未命中处理。
2)IgnoreThisRequest指示将请求视为缓存未命中处理。因此,将重新处理该页,但不会使缓存页无效。
3)Valid指示缓存页有效。
如下面的ValidateCacheOutput验证处理程序所示,该处理程序确定查询字符串变量status包含值“invalid”还是“ignore”。如果状态值为“invalid”,则该方法返回Invalid,并且使该页在缓存中无效;如果状态值为“ignore”,则该方法返回IgnoreThisRequest,并且该页仍保留在缓存中,但为该请求生成一个新响应;否则该方法返回Valid,指示缓存页有效。
public static void ValidateCacheOutput(HttpContext context,
Object data, ref HttpValidationStatus status)
{
if(context.Request.QueryString["Status"]!=null)
{
string pageStatus=context.Request.QueryString["Status"];
if(pageStatus=="invalid")
{
status=HttpValidationStatus.Invalid;
}
else if(pageStatus=="ignore")
{
status=HttpValidationStatus.IgnoreThisRequest;
}
else
{
status=HttpValidationStatus.Valid;
}
}
else
{
status=HttpValidationStatus.Valid;
}
}
定义好ValidateCacheOutput验证处理程序之后,就可以从其中一个页生命周期事件(如页的Load事件)中调用AddValidationCallback方法,将ValidateCacheOutput验证处理程序作为第一个参数传递。如下面的代码所示:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.AddValidationCallback(
new HttpCacheValidateHandler(ValidateCacheOutput),null);
}
19.2.8 使用缓存键依赖项缓存页输出
在ASP.NET中,缓存依赖允许让被缓存的项目依赖于其他资源,这样当哪个资源发生变化时,缓存项目就会被自动移除。
也就是说,可以将缓存中某一项的生存期配置为依赖于其他应用程序元素,如某个文件或数据库等。当缓存项依赖的元素更改时,ASP.NET将从缓存中移除该项。例如,如果网站显示一份报告,该报告是应用程序通过XML文件创建的,则可以将该报告放置在缓存中,并将其配置为依赖于该XML文件。当XML文件更改时,ASP.NET会从缓存中移除该报告。当代码请求该报告时,代码会先确定该报告是否在缓存中,如果不在,代码会重新创建该报告。因此,始终都有最新版本的报告可用。
简单地讲,ASP.NET有三种类型的依赖:依赖于其他缓存项目、依赖于文件或者文件夹与依赖于数据库查询。
其中,如果要使缓存的页输出依赖于另一缓存项,可以在页面的代码中调用Response对象的AddCacheItemDependency方法,将创建依赖项的缓存项的名称作为cacheKey参数传递。
如下面的代码示例演示如何在名为Cache1的项上设置依赖项。当此项被修改或移除时,将从缓存中移除页输出。
protected void Page_Load(object sender, EventArgs e)
{
Response.AddCacheItemDependency("Cache1");
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);
}
最后,值得注意的是,不能在用户控件中调用AddCacheItemDependency方法。不过,在指定@OutputCache指令的任何用户控件中,都可以创建描述缓存键依赖项的CacheDependency对象,并将其分配给UserControl对象的Dependency属性。
19.2.9 使用文件依赖项缓存页输出
其实,使缓存的页输出依赖于单个文件与使缓存的页输出依赖于另一缓存项的处理方法相似,可以在页代码中调用Response对象的AddFileDependency方法,将创建依赖项的文件的路径作为方法的filename参数传递。
如下面的代码示例在TextFile.txt文件上设置一个文件依赖项。当文件发生更改时,将从缓存中移除页输出。
protected void Page_Load(object sender, EventArgs e)
{
string fileDependencyPath=Server.MapPath("TextFile.txt");
Response.AddFileDependency(fileDependencyPath);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);
}
如果要使缓存的页输出依赖于文件组(多个文件),可以首先在页代码中创建一个包含该页所要依赖的文件的路径的String数组或ArrayList。然后调用AddFileDependencies方法,并将数组作为filenames参数传递。
如下面的代码示例创建包含TextFile.txt和XMLFile.xml文件的文件路径的字符串数组,并使页输出依赖于这两个文件。如果修改了其中任何一个文件,将从缓存中移除页输出。
protected void Page_Load(object sender, EventArgs e)
{
string[]fileDependencies;
string textFileDependency=Server.MapPath("TextFile.txt");
string xmlFileDependency=Server.MapPath("XMLFile.xml");
fileDependencies=new String[]{textFileDependency, xmlFileDependency};
Response.AddFileDependencies(fileDependencies);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);
}