Asp.Net - Can the absolute expiry for a Cache with

2019-05-07 10:02发布

问题:

I have the a test harness detailed below. This has two labels on the page that are set within the page_load which is hit every second due to the updatepanel and timer.

Label1 is set to a datetime value that is stored in the Cache. Label2 is set to the current datetime.

The cache is set with an absolute expiry of 5 seconds from now and there is an update callback on the cache to re-set the datetime and make it valid for another 5 seconds.

The problem I have is that I'm seeing the cache update every 20 seconds, not every 5 seconds like I would expect. If I set the time to 30 seconds then it updates every 40.

This appears to indicate that the cache will only expire every 20 seconds. Does anyone know of a way of reducing this time? If I just insert into the cache with an expiry of 5 seconds and no Callback then it works as I would expect and is removed every 5 seconds.

ASPX:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CacheTest.aspx.cs" Inherits="CacheTest" %>

<!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>Cache Test</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server" Text="" />
                <br />
                <asp:Label ID="Label2" runat="server" Text="" />
                <asp:Timer ID="Timer1" Interval="1000" runat="server" />
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

Code behind:

using System;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class CacheTest : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DateTime CachedDateTime = (DateTime)(Cache["DateTime"] ?? DateTime.MinValue);
        if (CachedDateTime == DateTime.MinValue)
        {
            CachedDateTime = System.DateTime.Now;
            Cache.Insert("DateTime", CachedDateTime, null, DateTime.Now.AddSeconds(5), Cache.NoSlidingExpiration, CacheDateTimeUpdateCallback);
        }
        Label1.Text = CachedDateTime.ToString();
        Label2.Text = DateTime.Now.ToString();
    }

    private void CacheDateTimeUpdateCallback(string key, CacheItemUpdateReason cacheItemUpdateReason, out object value, out CacheDependency dependencies, out DateTime absoluteExipriation, out TimeSpan slidingExpiration)
    {
        value = System.DateTime.Now;
        dependencies = null;
        absoluteExipriation = DateTime.Now.AddSeconds(5);
        slidingExpiration = Cache.NoSlidingExpiration;
    }
}

回答1:

One problem with the code you posted is that you're putting a DateTime into the cache in the callback method, but checking for a Nullable<DateTime> in Page_Load.

Having said that, this isn't the problem. After a quick look with Reflector it looks like a quirk of the Cache implementation: when you use a callback, it is called from a Timer that runs every 20 seconds.

I would speculate that the reason is to do with reentrancy. There's nothing to stop you accessing the cache from within your callback method. And doing so when it's in the middle of trying to remove an item may not be safe (for example, if you try to access the cache item it's in the middle of removing, there's potentially an infinite loop).

So when you have a callback, the implementation defers removal of your cache item until its timer runs.