如何:从缓存中移除项时通知应用程序

更新:2007 年 11 月

在大多数缓存方案中,当从缓存中移除项后,直到再次需要此项时,才需要将其放回缓存中。典型的开发模式是在使用项之前始终检查该项是否已在缓存中。如果项位于缓存中,则可以使用。如果不在缓存中,则应再次检索该项,然后将其添加回缓存。

但是,在某些情况下,如果从缓存中移除项时通知应用程序,可能非常有用。例如,您可能具有一个缓存的报告,创建该报告需花费大量的时间进行处理。当该报告从缓存中移除时,您希望重新生成该报告,并立即将其置于缓存中,以便下次请求该报告时,用户不必等待对此报告进行处理。

为了在从缓存中移除项时能够发出通知,ASP.NET 提供了 CacheItemRemovedCallback 委托。该委托定义编写事件处理程序时使用的签名,当对从缓存中移除项进行响应时会调用此事件处理程序。ASP.NET 还提供 CacheItemRemovedReason 枚举,用于指定移除缓存项的原因。

通常,通过在管理尝试检索的特定缓存数据的业务对象中创建处理程序,来实现回调。例如,您可能有一个 ReportManager 对象,该对象具有两种方法,即 GetReport 和 CacheReport。GetReport 报告方法检查缓存以查看报告是否已缓存;如果没有,该方法将重新生成报告并将其缓存。CacheReport 方法具有与 CacheItemRemovedCallback 委托相同的函数签名;从缓存中移除报告时,ASP.NET 会调用 CacheReport 方法,然后将报告重新添加到缓存中。

当从缓存中移除项时通知应用程序

  1. 创建一个类,负责从缓存中检索项并处理回调方法,以将项添加回缓存中。

  2. 在该类中,创建用于将项添加到缓存中的方法。

  3. 在该类中,创建用于从缓存中获取项的方法。

  4. 创建用于处理缓存项移除回调的方法。该方法必须具备与 CacheItemRemovedCallback 委托相同的函数签名。从缓存中删除项时,会在该方法中执行要运行的逻辑,如重新生成项并将其添加回缓存中。

测试缓存项回调

  1. 创建一个 ASP.NET 网页,该网页将调用类中用于将项添加到缓存中的方法。

    下面的代码示例演示如何调用 ReportManager 类的 GetReport 方法(在此过程后面的示例中定义)。然后将在使用页面的 Page_Load 方法期间显示 Label 控件 Label1 中的报告。

    protected void Page_Load(object sender, EventArgs e)
    {
        this.Label1.Text = ReportManager.GetReport();
    }
    
    Protected Sub Page_Load(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles Me.Load
        Me.Label1.Text = ReportManager.GetReport()
    End Sub
    
  2. 在浏览器中请求 ASP.NET 页并查看报告。

    报告是在首次请求页时创建的,在缓存中的报告被移除之前,后续请求都将访问缓存中的报告。

示例

下面的代码示例演示一个名为 ReportManager 的、用于在从缓存中删除项时处理通知的完整类。该类管理字符串形式的报告,此报告表示一个长期运行的进程。

尽管该示例使用声明为 static(在 Visual Basic 中为 Shared)的类,但并不是必须使用静态类。不过,删除缓存项时,用于处理回调的方法必须存在。例如,不应在 ASP.NET 页中实现回调处理程序,因为在从缓存中删除项之前该页可能已被释放,因此用于处理回调的方法将不可用。为了确保从缓存中删除项时处理回调的方法仍然存在,请使用该方法的静态类。但是,静态类的缺点是需要保证所有静态方法都是线程安全的。

7kxdx246.alert_caution(zh-cn,VS.90).gif警告:

请不要在页面中将 CacheItemRemovedCallback 设置为一个方法。除了在释放页面后回调无法使用页面方法以外,将回调指向页面方法还会阻碍垃圾回收将页面使用的内存回收。由于回调包含对页面的引用,而垃圾回收器不会从内存中移除包含任何引用的项,因此会出现这种情况。在加载应用程序期间,这可能会导致内存很快被用光。

该示例类包括以下功能:

  • 私有成员,用于跟踪报告是否已从缓存中移除。

  • 名为 CacheReport 的方法,用于将项以 MyReport 的名称添加到缓存中,并将该项设置为在添加到缓存中后一分钟过期。该方法还会将 ReportRemovedCallback 方法传递给 onRemoveCallback 参数,从而注册 ReportRemoveCallback 方法,以便在从缓存中删除项时进行调用。

  • 名为 GetReport 的方法,用于从缓存中获取项。该方法确定名为 MyReport 的项是否存在于缓存中。如果该项不存在,则该方法将调用 CacheReport,,将该项添加到缓存中。

  • 名为 ReportRemovedCallback 的方法,用于处理缓存项移除回调。ReportRemovedCallback 具有与 CacheItemRemovedCallback 委托相同的函数签名。该方法将变量 _reportRemovedFromCache 设置为 true,然后通过 CacheReport 方法将项添加回缓存中。

using System;
using System.Web;
using System.Web.Caching;
public static class ReportManager
{
    private static bool _reportRemovedFromCache = false;
    static ReportManager()
    { }

    public static String GetReport()
    {
        lock (typeof(ReportManager))
        {
            if (HttpContext.Current.Cache["MyReport"] != null)
                return (string)HttpRuntime.Cache["MyReport"];
            else
            {
                CacheReport();
                return (string)HttpRuntime.Cache["MyReport"];
            }
        }
    }

    public static void CacheReport()
    {
        lock (typeof(ReportManager))
        {
            HttpRuntime.Cache.Add("MyReport",
                CreateReport(), null, Cache.NoAbsoluteExpiration,
                new TimeSpan(0, 1, 0),
                System.Web.Caching.CacheItemPriority.Default,
                new CacheItemRemovedCallback(ReportRemovedCallback));
        }
    }

    private static string CreateReport()
    {
        System.Text.StringBuilder myReport = 
            new System.Text.StringBuilder();
        myReport.Append("Sales Report<br />");
        myReport.Append("2005 Q2 Figures<br />");
        myReport.Append("Sales NE Region - $2 million<br />");
        myReport.Append("Sales NW Region - $4.5 million<br />");
        myReport.Append("Report Generated: " + DateTime.Now.ToString() 
            + "<br />");
        myReport.Append("Report Removed From Cache: " + 
            _reportRemovedFromCache.ToString());
        return myReport.ToString();
    }

    public static void ReportRemovedCallback(String key, object value, 
        CacheItemRemovedReason removedReason)
    {
        _reportRemovedFromCache = true;
        CacheReport();
    }
}
Imports System
Imports System.Web
Imports System.Web.Caching
Public Class ReportManager
    Private Shared _reportRemovedFromCache As Boolean = False
    Shared Sub New()
    End Sub

    Private Sub New()
    End Sub

    Public Shared Function GetReport() As String
        SyncLock (GetType(ReportManager))
            If HttpContext.Current.Cache("MyReport") IsNot Nothing Then
                Return CStr(HttpRuntime.Cache("MyReport"))
            Else
                CacheReport()
                Return CStr(HttpRuntime.Cache("MyReport"))
            End If
        End SyncLock
    End Function

    Public Shared Sub CacheReport()
        SyncLock (GetType(ReportManager))
            HttpRuntime.Cache.Add("MyReport", CreateReport(), _
            Nothing, Cache.NoAbsoluteExpiration, New TimeSpan(0, 1, 0), _
            System.Web.Caching.CacheItemPriority.Default, _
            New CacheItemRemovedCallback(AddressOf ReportRemovedCallback))
        End SyncLock
    End Sub

    Private Shared Function CreateReport() As String
        Dim myReport As New System.Text.StringBuilder()
        myReport.Append("Sales Report<br />")
        myReport.Append("2005 Q2 Figures<br />")
        myReport.Append("Sales NE Region - $2 million<br />")
        myReport.Append("Sales NW Region - $4.5 million<br />")
        myReport.Append("Report Generated: " & _
            DateTime.Now.ToString() & "<br />")
        myReport.Append("Report Removed From Cache: " _
            & _reportRemovedFromCache.ToString())
        Return myReport.ToString()
    End Function

    Public Shared Sub ReportRemovedCallback(ByVal key As String, _
            ByVal value As Object, ByVal removedReason _
            As CacheItemRemovedReason)
        _reportRemovedFromCache = True
        CacheReport()
    End Sub
End Class

请参见

任务

如何:将项添加到缓存中

如何:检索缓存项的值

如何:从 ASP.NET 缓存中删除项

概念

ASP.NET 缓存概述

ASP.NET 中的缓存配置

缓存应用程序数据