如何防止HttpRuntime.Cache删除ASP.NET 4.5中的项?

时间:2017-04-06 10:36:18

标签: asp.net asp.net-mvc caching httpruntime.cache cache-expiration

这是this question的后续行动,其中包含相互矛盾的答案。我也对与更新版本的ASP.NET相关的答案感兴趣。

我的应用程序使用HttpRuntime.Cache来缓存一些永不过期的模型列表。它们在应用程序预热时被加载,它们很少被更改,但是经常阅读。

我的代码如下:

private void ReportRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
    if (!ApplicationPoolService.IsShuttingDown())
    {
        var str = $"Removed cached item with key {key} and count {(value as IDictionary)?.Count}, reason {reason}";
        LoggingService.Log(LogLevel.Info, str);
    }
}

private IDictionary<int, T> ThreadSafeCacheAccessAction(Action<IDictionary<int, T>, bool> action = null)
{
    // refresh cache if necessary
    var dict = HttpRuntime.Cache[CacheDictKey] as IDictionary<int, T>;
    bool invalidated = false;
    if (dict == null)
    {
        lock (CacheLockObject)
        {
            // getting expiration times from model attribute
            var cacheInfo = typeof(T).GetCustomAttributes(typeof(UseInCachedRepositoryAttribute), inherit: true).FirstOrDefault() as UseInCachedRepositoryAttribute;
            int absoluteExpiration = cacheInfo?.AbsoluteExpiration ?? Constants.Cache.IndefiniteRetention;
            int slidingExpiration = cacheInfo?.SlidingExpiration ?? Constants.Cache.NoSlidingExpiration;

            dict = _modelRepository.AllNoTracking.ToList().Where(item => item.PkId != 0).ToDictionary(item => item.PkId, item => item);
            HttpRuntime.Cache.Insert(CacheDictKey, dict, dependencies: null,
                absoluteExpiration: DateTime.Now.AddMinutes(absoluteExpiration),
                slidingExpiration: slidingExpiration <= 0 ? Cache.NoSlidingExpiration : TimeSpan.FromMinutes(slidingExpiration),
                priority: CacheItemPriority.NotRemovable,
                onRemoveCallback: ReportRemovedCallback);

            invalidated = true;
        }
    }

根据提供的文档here,我还在web.config中包含了以下标记:

<caching>
  <cache disableExpiration="true" disableMemoryCollection="true" />
</caching>

但是,有时会调用ReportRemovedCallback来删除项目。我的感觉是忽略了web.config中的缓存配置(文档清楚地表明它已经过时),CacheItemPriority.NotRemovable仅表示&#34;非常高的优先级&#34;,而不是&#34;永远不会删除&# 34。

问题: 有没有办法说服HttpRuntime.Cache永远不会删除某些项目?或者我应该考虑另一种缓存机制?

1 个答案:

答案 0 :(得分:0)

好的,所以我挖的更多,而且没有明确的答案,但是this old docs的以下配置似乎适用于任何拒绝过期的试验:

  

未明确配置以下默认缓存元素   机器配置文件或根Web.config文件,但是   .NET中应用程序返回的默认配置   框架版本2.0。

<cache disableMemoryCollection="false" 
  disableExpiration="false" privateBytesLimit="0" 
  percentagePhysicalMemoryUsedLimit="90" 
  privateBytesPollTime="00:02:00" /> 

因此,如果物理内存使用率超过90%,它将驱逐缓存项。由于操作系统倾向于使用几乎所有物理内存来进行系统缓存(由任务管理器报告),因此听起来不太可能。

替代

我花了MemoryCache进行旋转,因为它与HttpRuntime.Cache非常相似。它提供了类似的功能,但缺少CacheEntryUpdateCallback(你可以提供它,但如果它与null不同则会引发InvalidArgumentException)。

现在我的代码如下:

var dict = MemoryCache.Default.Get(CacheDictKey) as IDictionary<int, T>;

if (dict == null)
{
    lock (CacheLockObject)
    {
        // getting expiration times from model attribute
        var cacheInfo = typeof(T).GetCustomAttributes(typeof(UseInCachedRepositoryAttribute), inherit: true).FirstOrDefault() as UseInCachedRepositoryAttribute;
        int absoluteExpiration = cacheInfo?.AbsoluteExpiration ?? Constants.Cache.IndefiniteRetention;
        int slidingExpiration = cacheInfo?.SlidingExpiration ?? Constants.Cache.NoSlidingExpiration;

        dict = _modelRepository.AllNoTracking.ToList().Where(item => item.PkId != 0).ToDictionary(item => item.PkId, item => item);

        var cacheItemPolicy = new CacheItemPolicy
        {
            AbsoluteExpiration = DateTime.Now.AddMinutes(absoluteExpiration),
            SlidingExpiration = slidingExpiration <= 0 ? Cache.NoSlidingExpiration : TimeSpan.FromMinutes(slidingExpiration),
            Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable,
            // throws InvalidArgumentException
            // UpdateCallback = CacheEntryUpdateCallback
        };
        MemoryCache.Default.Add(CacheDictKey, dict, cacheItemPolicy);
    }
}

经过一些测试后,没有额外删除,w3wp.exe的内存消耗也按预期增加。

this answer中提供了更多详细信息。