使用 SqlCacheDependency 类在 ASP.NET 中缓存
ASP.NET 允许您使用 SqlCacheDependency 类创建依赖于数据库中表或行的缓存项。当表中或特定行中发生更改时,带有依赖项的项便会失效,并会从缓存中移除。可以在 Microsoft SQL Server 7.0、SQL Server 2000 和 SQL Server 2005 中设置表的依赖项。如果您使用 SQL Server 2005,还可以设置特定记录的依赖项。
在某些方案中,使用带有 SQL 依赖项的缓存可显著提高应用程序的性能。例如,假定您正在构建一个从数据库显示产品信息的电子商务应用程序。如果不进行缓存,则每当用户要查看产品时,应用程序都必须从数据库请求数据。您可以在某一时刻将产品信息缓存一天,由于产品信息已经在内存中,因此可确保较快的响应时间,但是,当产品信息发生变化时,缓存的产品信息就会失去与数据库中数据的同步,且不同步的时间最长可达一天。
使用 SQL 缓存依赖项可以缓存产品信息,并创建一个数据库表或行更改的依赖项。当且仅当数据更改时,基于该数据的缓存项便会失效并会从缓存中移除。下次从缓存中请求该项时,如果该项不在缓存中,便可以再次向缓存中添加更新后的版本,并且可确保具有最新的数据。
SQL 缓存依赖项还可用于页输出缓存。例如,可以创建一个名为 ViewProduct.aspx 的页,用于显示有关特定产品的信息。可以将该页的缓存策略设置为 SQL 依赖项,就如为手动添加到缓存中的项所设置的依赖项一样。该页便会一直存储在缓存中,直至所依赖的表或行发生更改为止。当数据发生更改时,便会重新创建页,并将新创建的页再次存储在输出缓存中。
有关更多信息,请参见 ASP.NET 缓存概述。
功能
ASP.NET SQL 缓存依赖项提供以下功能:
-
SQL 缓存依赖项可用于应用程序缓存和页输出缓存。
-
可在 SQL Server 7.0 及更高版本中使用 SQL 缓存依赖项。
-
可以在网络园(一台服务器上存在多个处理器)或网络场(多台服务器运行同一应用程序)中使用 SQL 缓存依赖项。
-
与 SQL 缓存依赖项关联的数据库操作比较简单,因此不会给服务器带来很高的处理成本。
-
在应用程序和 SQL Server 中配置 SQL 缓存依赖项不需要很精深的 SQL 知识。ASP.NET 中包括可以自动执行此配置的工具。另外,还可以使用 SqlCacheDependencyAdmin 类以编程方式配置 SQL 缓存依赖项。
SQL Server 7.0 和 SQL Server 2000 实现
ASP.NET 为 SQL Server 7.0 和 SQL Server 2000 的缓存依赖项实现了一个轮询模型。ASP.NET 进程内的一个线程会以指定的时间间隔轮询 SQL Server 数据库,以确定数据是否已更改。如果数据已更改,缓存依赖项便会失效,并从缓存中移除。可以在 Web.config 文件中以声明方式指定应用程序中的轮询间隔,也可以使用 SqlCacheDependency 类以编程方式指定此间隔。
对于 SQL Server 7.0 和 SQL Server 2000,SQL 缓存依赖项仅限于表级别的数据更改。可以将 ASP.NET 配置为轮询数据库来确定表中的更改,但不能确定特定行中的更改。
启用 SQL 缓存
为了在 SQL Server 7.0 和 SQL Server 2000 中使用 SQL 缓存依赖项,必须先将 SQL Server 配置为支持缓存依赖项。ASP.NET 提供了一些实用工具,可用于配置 SQL Server 上的 SQL 缓存,其中包括一个名为 Aspnet_regsql.exe 的工具和 SqlCacheDependencyAdmin 类。有关如何在 SQL Server 上启用 SQL 缓存依赖项的更多信息,请参见如何:使用缓存键依赖项缓存页输出。
SQL Server 2005 实现
SQL Server 2005 为缓存依赖项实现的模型不同于 SQL Server 7.0 和 SQL Server 2000 中的缓存依赖项模型。在 SQL Server 2005 中,不需要执行任何特殊的配置步骤来启用 SQL 缓存依赖项。此外,SQL Server 2005 还实现了一种更改通知模型,可以向订阅了通知的应用程序服务器发送通知,而不是依赖早期版本的 SQL Server 中必需的轮询模型。
SQL Server 2005 缓存依赖项在接收通知的更改类型方面更具灵活性。SQL Server 2005 监控对特定 SQL 命令的结果集的更改。如果数据库中发生了将修改该命令的结果集的更改,依赖项便会使缓存的项失效。此功能使得 SQL Server 2005 可以提供行级别的通知。
对用于测试更改的查询有一些要求。必须提供完全限定的表名,其中包括所有者名称(例如 dbo.authors)。总之,SQL 2005 通知支持 Select 查询和存储过程,支持多个查询和嵌套查询,但不支持聚合操作(例如 COUNT(*))。有关 SQL Server 2005 支持哪些查询以及通知规则的更多信息,请参见“SQL Books Online”(SQL 联机丛书)中的主题“Creating a Query for Notification”(创建通知查询)。
在 ASP.NET 应用程序中配置 SQL 缓存
在对 SQL Server 7.0 或 SQL Server 2000 进行了缓存依赖项配置后,或是在 SQL Server 2005 中创建了适当的命令依赖项后,就可以配置您的应用程序来使用 SQL 缓存依赖项,如同配置任何其他缓存依赖项一样。例如,可以在 Web.config 文件中创建一个缓存配置文件,然后在应使用 SQL 缓存依赖项的每个页中引用该缓存配置文件。还可以在通过 SqlCacheDependency 类以编程方式启用 SQL 缓存依赖项后再使用它。有关更多信息,请参见如何:使用缓存键依赖项缓存页输出。
注意:此类在 .NET Framework 2.0 版中是新增的。
在以下两者之间建立关系:一是在 ASP.NET 应用程序的 Cache 对象中存储的项;二是特定 SQL Server 数据库表或 SQL Server 2005 查询的结果。无法继承此类。
命名空间:System.Web.Caching
程序集:System.Web(在 system.web.dll 中)
Public NotInheritable Class SqlCacheDependency Inherits CacheDependency
Dim instance As SqlCacheDependency
public sealed class SqlCacheDependency : CacheDependency
public ref class SqlCacheDependency sealed : public CacheDependency
public final class SqlCacheDependency extends CacheDependency
public final class SqlCacheDependency extends CacheDependency
SqlCacheDependency 类在所有受支持的 SQL Server 版本 (7.0, 2000, 2005) 上监视特定的 SQL Server 数据库表,以便在该表发生更改时,自动从 Cache 中删除与该表关联的项。
数据库表发生更改时,将自动删除缓存项,并向 Cache 中添加新版本的项。
在使用 SQL Server 2005 数据库时,SqlCacheDependency 类还支持与 System.Data.SqlClient.SqlDependency 类进行集成。使用 SQL Server 2005 的查询通知机制来检测使 SQL 查询结果无效的数据更改。与 SQL 查询关联的任何缓存项都将从 System.Web.Caching.Cache 中移除。
在使用 SQL Server 2005 时,可以使用 SqlCacheDependency 类向应用程序的 Cache 添加依赖于 SQL Server 数据库表或 SQL 查询的项。还可以将此类与 @ OutputCache 指令一起使用,以生成依赖于 SQL Server 数据库表的输出缓存的页或用户控件。最后,在使用 SQL Server 2005 时,可以将 SqlCacheDependency 类与 @ OutputCache 页指令一起使用,以生成依赖于 SQL 查询结果的输出缓存的页。对于用户控件,@ OutputCache 指令不支持使用 SQL Server 2005 的查询通知。
注意 |
---|
为使此类在使用基于表的通知时正常工作,必须为数据库及要设置依赖项的任何表启用通知。可通过使用 SqlCacheDependencyAdmin 类的方法或 Aspnet_regsql.exe 命令行工具启用通知。同时,应用程序的 Web.config 文件中必须包含正确的配置设置。 将 SqlCacheDependency 对象与 SQL Server 2005 查询通知一起使用不需要任何显式配置。开发人员应参考 SQL Server 2005 联机丛书,以了解使用查询通知时允许的 Transact-SQL 查询类型的限制。 |
下面是一个 ASP.NET Web.config 文件示例,该文件对 SQL Server 数据库表启用了基于表的依赖项。
<configuration> <connectionStrings> <add name="Pubs" connectionString="Data Source=(local); Initial Catalog=pubs; Integrated Security=true"; providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <caching> <sqlCacheDependency enabled = "true" pollTime = "60000" > <databases> <add name="pubs" connectionString = "pubs" pollTime = "9000000" /> </databases> </sqlCacheDependency> </caching> </system.web> </configuration>
Topic | Location |
---|---|
Walkthrough: Using ASP.NET Output Caching with SQL Server | Building ASP .NET Web Applications in Visual Studio |
逐步解說:將 ASP.NET 輸出快取功能與 SQL Server 搭配使用 | 在 Visual Studio 中建置 ASP .NET Web 應用程式 |
下面的代码示例使用 SqlDataSource 和 GridView 控件显示数据库表。加载页时,该页试图创建 SqlCacheDependency 对象。创建 SqlCacheDependency 对象后,该页向 Cache 添加一个项,该项在 SqlCacheDependency 对象上有一个依赖项。应使用与此处所示类似的异常处理。
<%@ Page Language="VB" Debug="True" %> <%@ import Namespace="System.Data.SqlClient" %> <script runat="server"> Sub Page_Load(Src As Object, E As EventArgs) ' Declare the SqlCacheDependency instance, SqlDep. Dim SqlDep As SqlCacheDependency ' Check the Cache for the SqlSource key. ' If it isn't there, create it with a dependency ' on a SQL Server table using the SqlCacheDependency class. If Cache("SqlSource") Is Nothing ' Because of possible exceptions thrown when this ' code runs, use Try...Catch...Finally syntax. Try ' Instantiate SqlDep using the SqlCacheDependency constructor. SqlDep = New SqlCacheDependency("Northwind", "Categories") ' Handle the DatabaseNotEnabledForNotificationException with ' a call to the SqlCacheDependencyAdmin.EnableNotifications method. Catch exDBDis As DatabaseNotEnabledForNotificationException Try SqlCacheDependencyAdmin.EnableNotifications("Northwind") ' If the database does not have permissions set for creating tables, ' the UnauthorizedAccessException is thrown. Handle it by redirecting ' to an error page. Catch exPerm As UnauthorizedAccessException Response.Redirect(".\ErrorPage.htm") End Try ' Handle the TableNotEnabledForNotificationException with ' a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method. Catch exTabDis As TableNotEnabledForNotificationException Try SqlCacheDependencyAdmin.EnableTableForNotifications( _ "Northwind", "Categories") ' If a SqlException is thrown, redirect to an error page. Catch exc As SqlException Response.Redirect(".\ErrorPage.htm") End Try ' If all the other code is successful, add MySource to the Cache ' with a dependency on SqlDep. If the Categories table changes, ' MySource will be removed from the Cache. Then generate a message ' that the data is newly created and added to the cache. Finally Cache.Insert("SqlSource", Source1, SqlDep) CacheMsg.Text = "The data object was created explicitly." End Try Else CacheMsg.Text = "The data was retrieved from the Cache." End If End Sub </script> <html> <head> </head> <body> <form runat="server"> <p> </p> <p> <asp:SqlDataSource id="Source1" runat="server" SelectCommand="SELECT * FROM [Categories]" UpdateCommand="UPDATE [Categories] SET [CategoryName]=@CategoryName,[Description]=@Description,[Picture]=@Picture WHERE [CategoryID]=@CategoryID" ConnectionString="server='localhost';user id='bjctest';password='bjctest'; Database='Northwind'"></asp:SqlDataSource> <asp:GridView id="GridView1" runat="server" KeyFieldNames="CategoryID" AllowSorting="True" AllowPaging="True" DataSourceID="Source1"></asp:GridView> </p> <p> </p> <p> <asp:Label id="CacheMsg" runat="server"></asp:Label> </p> </form> </body> </html>
注意:此类在 .NET Framework 2.0 版中是新增的。
在使用基于轮询的依赖项时,执行 SQL Server 数据库上需要的管理任务以支持 SqlCacheDependency 类。无法继承此类。
命名空间:System.Web.Caching
程序集:System.Web(在 system.web.dll 中)
Public NotInheritable Class SqlCacheDependencyAdmin
可对静态类的成员直接进行访问,无需类的实例。
public static class SqlCacheDependencyAdmin
public ref class SqlCacheDependencyAdmin abstract sealed
public final class SqlCacheDependencyAdmin
public final class SqlCacheDependencyAdmin
可以使用此类的方法以编程方式为 SQL Server 数据库及其表启用和禁用 SqlCacheDependency 对象更改通知。此外,此类使用 GetTablesEnabledForNotifications 方法获取有关为数据库中的哪些表启用更改通知的信息。但是,也可以使用 Aspnet_regsql 命令行工具管理 SQL Server 数据库及其表的更改通知。
注意 |
---|
若要调用此类中的任何方法,用于访问 SQL Server 数据库的帐户必须具有创建表和存储过程的权限。若要启用特定表的通知,必须具有在该表上创建 SQL Server 触发器的权限。有关如何设置数据库权限的更多信息,请参见 SQL Server 文档。有关 ASP.NET 进程使用的帐户的更多信息,请参见 ASP.NET 模拟。 |
下面的代码示例是一个简单的 ASP.NET 页,用于启用和禁用 SQL Server 数据库表的更改通知。该示例使用 SqlCacheDependencyAdmin 对象管理数据库(在名为 MyConnectionString 的连接字符串中指定)中的更改通知。该示例包括两个文件,即一个定义用户界面的 .aspx 文件和一个包含 ASP.NET 事件的源代码的代码隐藏文件。
第一个示例为定义用户界面的 .aspx 文件。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="cacheDependencyAdmincs.aspx.cs" Inherits="cacheDependencyAdmincs" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html > <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Cache Dependency Administration</title> </head> <body> <form id="form1" runat="server"> <table> <tr> <td colspan="2"> Database support for change notifications: </td> </tr> <tr> <td align="center"> <asp:Button ID="enableNotification" runat="server" Text="On" OnClick="enableNotification_Click" /> </td> <td align="center"> <asp:Button ID="disableNotification" runat="server" Text="Off" OnClick="disableNotification_Click" /> </td> </tr> <tr> <td colspan="2"> <asp:Label ID="enabledTablesMsg" runat="server" Text="Tables enabled for change notification:" /> </td> </tr> <tr> <td colspan="2"> <asp:ListBox ID="enabledTables" runat="server" SelectionMode="multiple" /><br /> <asp:Button ID="disableTable" runat="server" Text="Disable selected table(s)" OnClick="disableTable_Click" /> </td> </tr> <tr> <td colspan="2"> <asp:Label ID="tableEnableMsg" runat="server" Text="Enable change notification on table:" /> </td> </tr> <tr> <td colspan="2"> <asp:TextBox ID="tableName" runat="server" /><br /> <asp:Button ID="enableTable" runat="server" Text="Enable table(s)" OnClick="enableTable_Click" /> <asp:Label id="enableTableErrorMsg" runat="server" Visible="false" /> </td> </tr> </table> </form> </body> </html>
第二个示例为包含页事件的源代码的代码隐藏文件。
Partial Class cacheDependencyAdminvb Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) 'Put the page into a default state. enabledTables.Visible = True disableTable.Visible = True enabledTablesMsg.Text = "Tables enabled for change notification:" tableName.Visible = True enableTable.Visible = True tableEnableMsg.Text = "Enable change notification on table(s):" enableTableErrorMsg.Text = "" End Sub Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Try Dim enabledTablesList As String() enabledTablesList = SqlCacheDependencyAdmin.GetTablesEnabledForNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString) If enabledTablesList.Length > 0 Then enabledTables.DataSource = enabledTablesList enabledTables.DataBind() Else enabledTablesMsg.Text = "No tables are enabled for change notifications." enabledTables.Visible = False disableTable.Visible = False End If Catch ex As DatabaseNotEnabledForNotificationException enabledTables.Visible = False disableTable.Visible = False enabledTablesMsg.Text = "Cache notifications are not enabled in this database." tableName.Visible = False enableTable.Visible = False tableEnableMsg.Text = "Must enable database for notifications before enabling tables." End Try End Sub Protected Sub enableNotification_Click(ByVal sender As Object, ByVal e As System.EventArgs) SqlCacheDependencyAdmin.EnableNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString) End Sub Protected Sub disableNotification_Click(ByVal sender As Object, ByVal e As System.EventArgs) SqlCacheDependencyAdmin.DisableNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString) End Sub Protected Sub disableTable_Click(ByVal sender As Object, ByVal e As System.EventArgs) For Each item As ListItem In enabledTables.Items If item.Selected Then SqlCacheDependencyAdmin.DisableTableForNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString, _ item.Text) End If Next End Sub Protected Sub enableTable_Click(ByVal sender As Object, ByVal e As System.EventArgs) Try If tableName.Text.Contains(";") Then Dim tables As String() tables = tableName.Text.Split(New [Char]() {";"c}) For i As Integer = 0 To tables.Length - 1 tables(i) = tables(i).Trim Next SqlCacheDependencyAdmin.EnableTableForNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString, _ tables) Else SqlCacheDependencyAdmin.EnableTableForNotifications( _ ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString, _ tableName.Text) End If Catch ex As HttpException enableTableErrorMsg.Text = "<br />" & _ "An error occured enabling a table.<br />" & _ "The error message was: " & _ ex.Message enableTableErrorMsg.Visible = True End Try End Sub End Class
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.Web.Caching; public partial class cacheDependencyAdmincs : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Put page in default state. enabledTables.Visible = true; disableTable.Visible = true; enabledTablesMsg.Text = "Tables enabled for change notification:"; tableName.Visible = true; enableTable.Visible = true; tableEnableMsg.Text = "Enable change notification on table(s):"; enableTableErrorMsg.Visible = false; } protected void Page_PreRender(object sender, EventArgs e) { try { string[] enabledTablesList = SqlCacheDependencyAdmin.GetTablesEnabledForNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString); if (enabledTablesList.Length > 0) { enabledTables.DataSource = enabledTablesList; enabledTables.DataBind(); } else { enabledTablesMsg.Text = "No tables are enabled for change notifications."; enabledTables.Visible = false; disableTable.Visible = false; } } catch (DatabaseNotEnabledForNotificationException ex) { enabledTables.Visible = false; disableTable.Visible = false; enabledTablesMsg.Text = "Cache notifications are not enabled in this database."; tableName.Visible = false; enableTable.Visible = false; tableEnableMsg.Text = "Must enable database for notifications before enabling tables"; } } protected void enableNotification_Click(object sender, EventArgs e) { SqlCacheDependencyAdmin.EnableNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString); } protected void disableNotification_Click(object sender, EventArgs e) { SqlCacheDependencyAdmin.DisableNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString); } protected void disableTable_Click(object sender, EventArgs e) { foreach (ListItem item in enabledTables.Items) { if (item.Selected) { SqlCacheDependencyAdmin.DisableTableForNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, item.Text); } } } protected void enableTable_Click(object sender, EventArgs e) { try { if (tableName.Text.Contains(";")) { string[] tables = tableName.Text.Split(new Char[] { ';' }); for (int i = 0; i < tables.Length; i++) tables[i] = tables[i].Trim(); SqlCacheDependencyAdmin.EnableTableForNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, tables); } else { SqlCacheDependencyAdmin.EnableTableForNotifications( ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, tableName.Text); } } catch (HttpException ex) { enableTableErrorMsg.Text = "<br />" + "An error occured enabling a table.<br />" + "The error message was: " + ex.Message; enableTableErrorMsg.Visible = true; } } }