ASP.net缓存绝对过期时间不起作用

15

我在HttpContext.Cache中存储了一个整数值,设置了一个绝对过期时间,在当前时间的5分钟后过期。然而,在等待了6分钟后(或更长时间),整数值仍然存在于Cache中(即使绝对过期时间已经过去)。这是我正在使用的代码:

public void UpdateCountFor(string remoteIp)
{
    // only returns true the first time its run
    // after that the value is still in the Cache
    // even after the absolute expiration has passed
    // so after that this keeps returning false
    if (HttpContext.Current.Cache[remoteIp] == null)
    {
        // nothing for this ip in the cache so add the ip as a key with a value of 1
        var expireDate = DateTime.Now.AddMinutes(5);
        // I also tried:
        // var expireDate = DateTime.UtcNow.AddMinutes(5); 
        // and that did not work either.
        HttpContext.Current.Cache.Insert(remoteIp, 1, null, expireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
    }
    else
    {
        // increment the existing value
        HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;
    }
}
第一次运行UpdateCountFor("127.0.0.1")时,它按预期将1插入到具有键"127.0.0.1"和从现在起5分钟的绝对过期时间的缓存中。每个后续运行都会递增缓存中的值。但是,在等待10分钟后,它仍然继续递增缓存中的值。该值从未过期,也未从缓存中删除。这是为什么呢?
我的理解是,绝对过期时间意味着该项将在大约那个时间被删除。我做错了什么吗?我有什么误解吗?
我期望该值在5分钟后从缓存中删除,但它仍然保留在那里,直到我重建项目为止。
所有这些都在我的本地机器上运行,使用.NET 4.0。

你的 web.config 文件中有任何奇怪的缓存配置设置吗? - Chris Marisic
不,我有默认的web.config文件。 - smoak
3个回答

16

原来这一行代码:

HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;

移除先前的值,并重新插入没有绝对或滑动过期时间的值。为了解决这个问题,我不得不创建一个帮助类,并像这样使用它:

public class IncrementingCacheCounter
{
    public int Count;
    public DateTime ExpireDate;
}

public void UpdateCountFor(string remoteIp)
{
    IncrementingCacheCounter counter = null;
    if (HttpContext.Current.Cache[remoteIp] == null)
    {
        var expireDate = DateTime.Now.AddMinutes(5);
        counter = new IncrementingCacheCounter { Count = 1, ExpireDate = expireDate };
    }
    else
    {
        counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
        counter.Count++;
    }
    HttpContext.Current.Cache.Insert(remoteIp, counter, null, counter.ExpireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}

这将解决问题并使计数器在绝对时间正确过期,同时仍然允许对其进行更新。


这是一个API过于贴心的情况。它不会强制你使用索引器来处理存储机制,因为你会发现这样做会导致一些奇怪的问题。 - Chris Marisic
1
我同意。最终,我只是使用 ilspy 查看源代码,找到了所有这些信息。 - smoak

4

尝试使用DateTime.UtcNow来计算超时时间,而不是datetime.Now。您可能会遇到以下描述的问题:

absoluteExpiration类型: System.DateTime 插入对象过期并从缓存中删除的时间。为了避免本地时间可能出现的问题,例如从标准时间更改为夏令时,请使用UtcNow而不是Now作为此参数值。如果您正在使用绝对过期,则slidingExpiration参数必须为NoSlidingExpiration。


改为使用DateTime.UtcNow.AddMinutes(5)也不起作用 :( - smoak
真遗憾,我认为你的代码是正确的。在我看来,它是正确的。它什么时候从缓存中移除?你可以添加一个回调函数到cache.insert中,以找出它何时被删除。也许这会给你一些线索,告诉你是什么触发了它。 - Chris Mullins
这就是问题所在,它根本没有从缓存中移除。这就是我遇到的问题。我期望它在5分钟后被移除,但它会一直留在那里,直到我重新构建项目。 - smoak
TimeSpan.FromMinutes(number_of_minutes) - 8bitcat

0

比Smoak发布的答案更简单的方法是使用下面更新后的代码,它不需要重新插入。这个方法之所以有效是因为类是引用类型。因此,当你在类实例内部更新计数器时,它不会导致缓存触发更新。

public class IncrementingCacheCounter
{
    public int Count;
}

public void UpdateCountFor(string remoteIp)
{
    IncrementingCacheCounter counter = null;
    if (HttpContext.Current.Cache[remoteIp] == null)
    {
        counter = new IncrementingCacheCounter { Count = 1};
        HttpContext.Current.Cache.Insert(remoteIp, counter, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
    }
    else
    {
        counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
        counter.Count++;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接