0

这是对这个问题的跟进,其中包含相互矛盾的答案。我也对与更新版本的 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;
        }
    }

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

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

但是,有时ReportRemovedCallback会要求删除项目。我的感觉是来自 web.config 的缓存配置被忽略(文档清楚地表明它已经过时),这CacheItemPriority.NotRemovable意味着只有“非常高的优先级”,而不是“从不删除”。

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

4

1 回答 1

1

好的,所以我已经挖掘了更多并且没有明确的答案,但是这个旧文档中的以下配置似乎适用于拒绝过期的试验:

以下默认缓存元素未在机器配置文件或根 Web.config 文件中显式配置,而是由 .NET Framework 2.0 版中的应用程序返回的默认配置。

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

因此,如果物理内存使用率高于 90%,它将逐出缓存项。由于操作系统倾向于将几乎所有物理内存用于系统缓存(由任务管理器报告),这并不像听起来那么不可能。

选择

我试用了MemoryCache,因为它与HttpRuntime.Cache. 它提供了类似的功能,但缺少CacheEntryUpdateCallback(您可以提供它,但如果它与 不同,则会抱怨 InvalidArgumentException null)。

现在我的代码如下:

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预期的内存消耗增加。

此答案中提供了更多详细信息。

于 2017-04-06T13:15:19.030 回答