利用缓存,可以极大改善Web应用程序的性能。
一.缓存概述
ASP.NET2.0 Framework支持下面类型的缓存:
l 页面输出缓存
l 局部页面缓存
l 数据源缓存
l 数据缓存
二.使用页面输出缓存
当一个页面被缓存时,被请求的页面内容并不会每次都重新生成,该页面对应到的.net类也不会执行。
以下代码设置页面缓存60秒:
<%@ OutputCache Duration="60" VaryByParam="none" %> |
需要注意的是,页面并不绝对保证会缓存到我们指定的时间。当服务器内存资源不足时,缓存项会自动从缓存中移除。
1.使用参数改变输出缓存
VaryByParam属性。
<%@ OutputCache Duration="60" VaryByParam="firstName" %> |
<%@ OutputCache Duration="60" VaryByParam="firstName;lastName" %> |
可以给VaryByParam属性赋两个特殊的值:
l None:导致所有的QueryString和表单参数被忽略。只有一个版本的页面缓存;
l *:导致传递到页面的QueryString或表单参数的任意变化都会创建一个新版本的页面缓存。
2.基于控件变化的输出缓存
VaryByControl属性
<%@ OutputCache Duration="60" VaryByControl="ddlCategory"%> |
上面的“ddlCategory”是页面中一个下拉列表框控件的ID。
3.基于头变化的输出缓存
VaryByHeader属性
<%@ OutputCache Duration="60" VaryByHeader="User-Agent"%> |
不推荐使用VaryByHeader属性。因为User-Agent头有任何变化(比如浏览器的主次版本号、操作系统、SP版本号、.Net Framework版本号等)都会导致创建新的页面缓存版本。
4.基于浏览器变化的输出缓存
VaryByCustom属性
<%@ OutputCache Duration="60" VaryByParam="none" VaryByCustom="browser"%> |
VaryByCustom属性接收一个特殊的“browser”值。这样浏览器的参数中只有两个被认为是重要并需要考虑的:浏览器类型和它的主版本号。
5.创建页面输出缓存文件依赖
可以在一个缓存页面和磁盘上的一个文件或一组文件之间创建一个依赖。当文件被修改后,缓存页面自动失效并在下次页面请求时重新生成。
protected void Page_Load(object sender, EventArgs e) { Response.AddFileDependency(MapPath("Category.xml")); } |
6.使用编程方式设置页面输出缓存过期
可以使用Response.RemoveOutputCacheItem()方法,以编程的方式,从缓存中移除一个页面。
例如,一个页面(ShowCategory.aspx)用于显示所有的类别信息,一个页面(AddCategory.aspx)用于添加类别信息。此时,我们希望将以编程的方式,在类别表更新时移除第一个页面的缓存。
以下是AddCategory.aspx.cs中的部分代码:
protected void btnAddCategory_Click(object sender, EventArgs e) { //往数据库中添加类别信息 HttpResponse.RemoveOutputCacheItem( Page.ResolveUrl("~/ShowCategory.aspx")); Response.Redirect("~/ShowCategory.aspx"); } |
7.创建页面输出缓存配置
除了为应用程序的每个页面设置页面输出缓存,也可以在Web配置文件中配置页面输出缓存,从而将这些设置应用到多个页面,使网站更易于管理。
<system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <addname="myCacheProfile"duration="3600" varyByParam="none"/> </outputCacheProfiles> </outputCacheSettings> </caching> </system.web> |
ShowCategory.aspx:
<%@ OutputCache CacheProfile="myCacheProfile"%> |
三.使用部分页面缓存
部分页面缓存在页面既包含动态内容又包含静态内容时才有意义。
有两种方式可以实现部分页面缓存:缓存后替换和用户控件。
1.使用缓存后替换
(1)使用声明方式
需要用到Substitution控件
Default.aspx.cs:
public static string GetDateTime(HttpContext context) { return DateTime.Now.ToString(); } |
Default.aspx:
<asp:Substitution ID="Substitution1" runat="server" MethodName="GetDateTime"/> |
(2)使用编程方式
不需要使用Substitution控件
Default.aspx.cs:
public static string GetUserName(HttpContext context) { return context.User.Identity.Name; } |
Default.aspx:
<% Response.WriteSubstitution(GetUserName); %> |
注意:Substitution控件的MethodName属性所指定的方法,必须是静态方法。
2.用户控件缓存
缓存后替换只在处理字符串文本时比较合适。如果需要处理更复杂的部分页面缓存,则应该利用用户控件。
将没有缓存的用户控件拖放到缓存了的页面后,该用户控件也自动缓存,这时只能用SUBSTITUTION控件了
要想在没有缓存的页面上部分缓存就可以用用户控件,只要在该用户控件上设置缓存就可以了
3.共享用户控件的输出缓存
ucSharedCategory.ascx:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ucSharedCategory.ascx.cs" Inherits="ucSharedCategory" %> <%@ OutputCache Duration="60" VaryByParam="none" Shared="true"%> |
缓存应用程序页面和数据(二)
一.使用DataSource缓存
包括SqlDataSource、ObjectDataSource、XmlDataSource3个数据源控件。
1.优点:
l 可以在数据更新时自动重新载入数据;
l 可以在多个页面间共享相同的数据。比如,当两个SqlDataSource数据源控件的SelectCommand、SelectParameters和ConnectionString属性相同,则这两个控件将共享相同的缓存数据。
2.使用绝对过期策略(缺省)
适用情形:数据的修改不频繁。
示例:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1"> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" SelectCommand="SELECT * FROM [Categories]" EnableCaching="True" CacheDuration="60" OnSelecting="SqlDataSource1_Selecting"> </asp:SqlDataSource> |
3.使用相对过期策略
适用情形:缓存的数据量比较大,或者数据频繁被修改。这样就可以缓存最常被访问的数据。
方法:设置DataSource数据源控件的CacheExpirationPolicy="Sliding"。
4.使用ObjectDataSource控件缓存
ObjectDataSource控件支持和SqlDataSource控件一样的缓存属性。
如果要在多个ObjectDataSource之间共享相同的数据,则它们的TypeName、SelectMethod和SelectParameters属性必须一样。
5.使用XmlDataSource控件缓存
和前面两个数据源控件不一样,XmlDataSource控件默认情况下就启用了缓存功能。如果Xml文件被修改,则XmlDataSource控件自动重新载入修改过的Xml文件。
6.创建数据源控件键值依赖
可以在数据源控件和应用程序中的一个缓存项目之间创建一个键值依赖。这样,如果缓存项目的值发生了更改,则所有依赖该缓存项目的数据源控件就会重新载入数据。
(1)DataSourceKeyDependency.aspx:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1"> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" SelectCommand="SELECT * FROM [Categories]" EnableCaching="True" CacheDuration="60" CacheExpirationPolicy="Sliding" CacheKeyDependency="Category" OnSelecting="SqlDataSource1_Selecting"> </asp:SqlDataSource> |
(2)Global.asax:
void Application_Start(object sender, EventArgs e) { HttpContext.Current.Cache.Insert("Category",DateTime.Now, null,DateTime.MaxValue, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable,null); } |
(3)AddCategoryDataSourceKeyDependency.aspx:(用于向类别表中插入一条记录)
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs" Inherits="Default3" %> <!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"> 类别名称:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br /> 类别描述:<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><br /> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /> </form> </body> </html> |
(4)AddCategoryDataSourceKeyDependency.aspx.cs:
protected void Button1_Click(object sender, EventArgs e) { string strConn = WebConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString; string strSql = "insert into Categories(CategoryName,Description) values(@categoryName,@description)"; SqlConnection cn = new SqlConnection(strConn); SqlCommand cm = new SqlCommand(); cm.Connection = cn; cm.CommandText = strSql; cm.Parameters.AddWithValue("categoryName",TextBox1.Text); cm.Parameters.AddWithValue("description",TextBox2.Text); cn.Open(); cm.ExecuteNonQuery(); cn.Close(); Cache.Insert("Category", DateTime.Now); Response.Redirect("Default2.aspx"); } |
当插入一条新的类别信息时,Category项目会重新插入到缓存中,此时,每一个依赖于这个键值的DataSource会自动重新载入数据。
二.使用数据缓存
Cache对象。
Cache对象是ASP.NET中所有缓存的根本机制。
每一个ASP.NET应用程序,都会创建一个Cache对象的实例。
1.添加项目到缓存
Insert()方法
protected void Page_Load(object sender, EventArgs e) { DataTable dt = (DataTable)Cache["categories"]; if (dt == null) { dt = GetCategories(); Cache.Insert("categories", dt); } GridView1.DataSource = dt; GridView1.DataBind(); } private DataTable GetCategories() { string strConn = WebConfigurationManager. ConnectionStrings["NorthwindConnectionString"]. ConnectionString; string strSql = "select categoryID,categoryName from categories"; SqlConnection cn = new SqlConnection(strConn); SqlDataAdapter da = new SqlDataAdapter(strSql,cn); DataTable dt = new DataTable(); da.Fill(dt); return dt; } |
2.使用绝对过期策略添加项目
Cache.Insert("categories", dt, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration); |
3.使用相对过期策略添加项目
Cache.Insert("categories", dt, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(10)); |
4.使用依赖添加到项目
当添加项目到Cache中时,可以让项目依赖一个外部对象。这样如果外部对象被修改,则项目自动从缓存中移除。
ASP.NET2.0包括3种缓存依赖类:
l CacheDependency:用于创建一个文件依赖或缓存键值依赖;
l SqlCacheDependency:用于创建一个对SQL Server数据库查询的依赖;
l AggregateCacheDependency:用于使用多个CacheDependency对象创建依赖。可以使用该对象组合文件和SQL依赖。
CacheDependency类是基类。其他两个类都从该类继承。
现在,让我们将上述缓存到Cache中的项目依赖于category.txt文本文件。
(1) 基于Categories数据表创建一个触发器:
CREATE TRIGGER t ON Categories AFTER INSERT,UPDATE,DELETE AS DECLARE @v1 varchar(200) DECLARE @v2 varchar(100) SET @v1 = cast(datepart(year,getdate()) as varchar) + cast(datepart(month,getdate()) as varchar) + cast(datepart(day,getdate()) as varchar) + cast(datepart(hour,getdate()) as varchar) + cast(datepart(minute,getdate()) as varchar) + cast(datepart(second,getdate()) as varchar) + cast(datepart(ms,getdate()) as varchar) SET @v2 = '@echo ' + @v1 + '>e:"prog"asp.net"Chapter16"Category.txt' EXECUTE master..xp_cmdshell @v2 |
(2) ShowCategory.aspx.cs:
DataTable dt = (DataTable)Cache["category"]; if (dt == null) { string fileName = MapPath("~/Category.txt"); CacheDependency dep = new CacheDependency(fileName); dt = GetCategories(); Cache.Insert("category", dt,dep); } GridView1.DataSource = dt; GridView1.DataBind(); |
5.指定缓存项目优先级
CacheItemPriority属性
三.使用基于轮询的SQL缓存依赖
ASP.NET2.0新增了一个功能非常强大的SQL缓存依赖功能。该功能允许在指定的数据库中发生数据更改时,自动地重新从数据库中载入数据。
在使用基于轮询的缓存依赖之前,必须执行下面两个步骤的配置:
(1)必须对数据库及其中的若干个数据表启用SQL缓存依赖;
(2)必须在Web.config配置文件中配置SQL缓存依赖。
1.配置数据库
(1)对数据库启用SQL缓存依赖
F:">aspnet_regsql -E -S . -ed -d Northwind 为 SQL 缓存依赖项启用该数据库。 . 已完成。 F:"> |
或者:
F:">aspnet_regsql -C "data source=.;initial catalog=Test;integrated security=ssp i" -ed 为 SQL 缓存依赖项启用该数据库。 . 已完成。 F:"> |
(2)对数据表启用SQL缓存依赖
F:">aspnet_regsql -E -S . -ed -d Northwind -et -t Categories 为 SQL 缓存依赖项启用该数据库。 . 已完成。 为 SQL 缓存依赖项启用该表。 已完成。 F:"> |
或者:
F:">aspnet_regsql -C "data source=.;initial catalog=Test;integrated security=ssp i" -et -t Categories 为 SQL 缓存依赖项启用该数据库。 . 已完成。 为 SQL 缓存依赖项启用该表。 已完成。 F:"> |
2.配置Web.config配置文件
<system.web> <caching> <sqlCacheDependencyenabled="true"pollTime="5000"> <databases> <addname="Northwind" connectionStringName="NorthwindConnectionString"/> </databases> </sqlCacheDependency> </caching> </system.web> |
3.对页面输出缓存使用基于轮询的SQL缓存依赖
PollingSqlOutputCache.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile=" PollingSqlOutputCache.aspx.cs" Inherits="PollingSqlOutputCache" %> <%@ OutputCache Duration="999999" VaryByParam="none" SqlDependency="Northwind:Categories" %> <!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>对页面输出缓存使用基于轮询的SQL缓存依赖</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1"> <Columns> <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" InsertVisible="False" ReadOnly="True" SortExpression="CategoryID" /> <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" SortExpression="CategoryName" /> <asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" SelectCommand="SELECT [CategoryID], [CategoryName], [Description] FROM [Categories]"> </asp:SqlDataSource> </div> </form> </body> </html> |
4.对DataSource缓存使用基于轮询的SQL缓存依赖
PollingDataSourceCache.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile=" PollingDataSourceCache.aspx.cs" Inherits="PollingDataSourceCache" %> <!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>对DataSource缓存使用基于轮询的SQL缓存依赖</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1"> <Columns> <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" InsertVisible="False" ReadOnly="True" SortExpression="CategoryID" /> <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" SortExpression="CategoryName" /> <asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" EnableCaching="true" SqlCacheDependency="Northwind:Categories" SelectCommand="SELECT [CategoryID], [CategoryName], [Description] FROM [Categories]"> </asp:SqlDataSource> </div> </form> </body> </html> |
5.对数据缓存使用基于轮询的SQL缓存依赖
PollingDataCache.aspx:
略……
PollingDataCache.aspx.cs:
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Data.SqlClient; using System.Web.Configuration; using System.Web.Caching; public partial class PollingDataCache : System.Web.UI.Page { private DataTable GetCategories() { string strConn = WebConfigurationManager. ConnectionStrings["NorthwindConnectionString"]. ConnectionString; string strSql = "select CategoryID,CategoryName from Categories"; SqlConnection cn = new SqlConnection(strConn); SqlDataAdapter da = new SqlDataAdapter(strSql, cn); DataTable dt = new DataTable(); da.Fill(dt); return dt; } protected void Page_Load(object sender, EventArgs e) { DataTable dt = (DataTable)Cache["Category"]; if (dt == null) { dt = GetCategories(); SqlCacheDependency dep = new SqlCacheDependency("Northwind", "Categories"); Cache.Insert("Category", dt, dep); } GridView1.DataSource = dt; GridView1.DataBind(); } } |
缓存应用程序页面和数据(三)
四.使用基于通知的SQL缓存依赖
优点:ASP.NET应用程序不必定时地访问数据库以查看是否有数据的修改。
缺点(限制):
l 查询必须使用两部分的表名(例如:dbo.Northwind)
l 查询必须包含一个显式的列名列表(不能使用*)
l 查询不能引用视图、继承表、临时表或表变量
l 查询不能引用大对象类型,如text、ntext、image列等
l 查询不能包含子查询、外连接等
l 查询不能使用distinct、compute、compute by或者insert关键字
l 查询不能使用聚合函数
可以对推SQL缓存依赖使用存储过程,但是,每一个存储过程中的SELECT语句必须满足上述的条件。
第一步:配置数据库启用SQL Server2005 Service Broker。
第二步:配置应用程序开始监听通知。
1.为推SQL缓存依赖配置数据库
(1)执行下面的查询来检查Service Broker是否已经为特定的数据库激活:
SELECT name,is_broker_enabled FROM sys.databases |
(2)如果没有开启,则执行alter database命令来启用它:
ALTER DATABASE Northwind SET ENABLE_BROKER |
(3)最后,ASP.NET进程必须有足够的权限来订阅查询通知。
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO "COMPUTENAME"ASPNET" |
2.为推SQL缓存依赖配置应用程序
在应用程序可以接收到变更通知之前,必须启用查询通知监听器。在Global.asax文件中启用监听器。
Global.asa:
<%@ Application Language="C#" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.Web.Configuration" %> <script runat="server"> void Application_Start(object sender, EventArgs e) { string strConn = WebConfigurationManager. ConnectionStrings["TestConnectionString"]. ConnectionString; SqlDependency.Start(strConn); }
void Application_End(object sender, EventArgs e) { string strConn = WebConfigurationManager. ConnectionStrings["TestConnectionString"]. ConnectionString; SqlDependency.Stop(strConn); }
void Application_Error(object sender, EventArgs e) { } void Session_Start(object sender, EventArgs e) { } void Session_End(object sender, EventArgs e) { } </script> |