如何从ASP.NET核心中的IMemoryCache中删除所有对象(重置)

时间:2015-12-22 00:38:07

标签: c# asp.net-core asp.net-core-mvc

我可以找到一个remove方法,通过其键从IMemoryCache中删除对象。有没有办法重置整个缓存并删除所有对象?

编辑:

How to clear MemoryCache? 链接中提供的Dispose方法在asp.net 5中给出了一个例外。ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.Extensions.Caching.Memory.MemoryCache'.

8 个答案:

答案 0 :(得分:18)

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory 部分缓存依赖关系

  

使用CancellationTokenSource允许将多个缓存条目作为一个群体逐出

此代码对我有用:

public class CacheProvider 
{
    private static CancellationTokenSource _resetCacheToken = new CancellationTokenSource();
    private readonly IMemoryCache _innerCache;

    /* other methods and constructor removed for brevity */

    public T Set<T>(object key, T value) 
    {
        /* some other code removed for brevity */
        var options = new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.Normal).SetAbsoluteExpiration(typeExpiration);
        options.AddExpirationToken(new CancellationChangeToken(_resetCacheToken.Token));

        _innerCache.Set(CreateKey(type, key), value, options);

        return value;
    }

    public void Reset()
    {
        if (_resetCacheToken != null && !_resetCacheToken.IsCancellationRequested && _resetCacheToken.Token.CanBeCanceled)
        {
            _resetCacheToken.Cancel();
            _resetCacheToken.Dispose();
        }

        _resetCacheToken = new CancellationTokenSource();
    }
}

答案 1 :(得分:2)

RC1的答案是你不能从我读过的内容开箱即用(我在GitHub上读过,可能有一种创建触发器的方法来促进这种情况的发生) 。

目前,您可以获得Get,Set和Remove。我认为你的选择是:

  1. 创建一个可以跟踪所有密钥的缓存管理器包装,然后您可以根据需要批量删除这些项目。我不爱这个,但它会起作用。当然,如果您不是控制添加的人,那么缓存中可能存在您不知道的内容(您可以将您的计数与其数量进行比较)。如果将IMemoryCache转换为MemoryCache,则可以获取公开的Count属性。
  2. 分叉程序集并公开Keys和/或添加删除这些项的方法。有一个基础字典保存密钥。我做了这个,编译它,为它创建一个Nuget包然后替换RC1版本只是为了看看我是否可以(并且它工作)。不确定这是否是正确的方法,但这里是我的fork的提交,我只是添加了一个只读属性,我将键转储到对象列表(键存储为对象)。与过去的MemoryCache实现一样,如果你暴露了密钥,它们在被转储后可能会过时,但是如果你只是使用它们来清除所有密钥那么这应该没关系。
  3. https://github.com/blakepell/Caching/commit/165ae5ec13cc51c44a6007d6b88bd9c567e1d724

    昨晚我发布了这个问题试图弄清楚是否有一种很好的方法可以专门检查缓存中的内容(问我们为什么没有办法)。如果你不问你永远不会知道它是否重要,所以我想为什么不这样做。

    https://github.com/aspnet/Caching/issues/149

答案 2 :(得分:2)

有很多方法可以做到这一点。如果您拥有正确的IMemoryCache,则可以使用Compact(1.0),否则此黑客可以使用:

此代码将起作用(在单元测试中以及.NET core 2.2上的生产中进行测试):

PropertyInfo prop = cache.GetType().GetProperty("EntriesCollection", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Public);
object innerCache = prop.GetValue(cache);
MethodInfo clearMethod = innerCache.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public);
clearMethod.Invoke(innerCache, null);

答案 3 :(得分:1)

我的解决方案是将缓存中所有项目的新过期日期设置为1毫秒。然后它们过期,因此缓存刷新。

答案 4 :(得分:0)

我的解决方案是创建一个包装器,该包装器将现有的一些方法重新公开,并通过用全新的MemoryCache对象替换添加缺少的方法。 对我来说很好。 代码如下:

cert-manager-webhook

答案 5 :(得分:0)

如果您使用标准的MemoryCache,则此代码可以提供帮助。 文档:https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#memorycachecompact

_cache.Compact(1.0);

答案 6 :(得分:0)

我通过在 IMemoryCache 周围创建一个 FlushableMemoryCache 单例来解决它,它跟踪当前存储在缓存中的键,然后可以遍历它们以将它们全部刷新:

public interface IFlushableMemoryCache
{
    void Set<T>(string cacheId, object key, T value);
    bool TryGetValue<T>(object key, out T value);
    void Remove(string cacheId, object key);
    void Flush(string cacheId);
}


public class FlushableMemoryCache : IFlushableMemoryCache
{
    private readonly IMemoryCache _memoryCache;
    private readonly IDictionary<string, HashSet<object>> _keyDictionary;

    public FlushableMemoryCache(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
        _keyDictionary = new Dictionary<string, HashSet<object>>();
    }


    public void Set<T>(string cacheId, object key, T value)
    {
        _memoryCache.Set(key, value);

        if (_keyDictionary.ContainsKey(cacheId))
        {
            if (!_keyDictionary[cacheId].Contains(key))
            {
                _keyDictionary[cacheId].Add(key);
            }
        }
        else
        {
            _keyDictionary.Add(cacheId, new HashSet<object>(new[] { key }));
        }
    }

    public bool TryGetValue<T>(object key, out T value)
    {
        return _memoryCache.TryGetValue(key, out value);
    }

    public void Remove(string cacheId, object key)
    {
        _memoryCache.Remove(key);

        if (_keyDictionary.ContainsKey(cacheId) && _keyDictionary[cacheId].Contains(key))
        {
            _keyDictionary[cacheId].Remove(key);
        }
    }

    public void Flush(string cacheId)
    {
        foreach (var key in _keyDictionary[cacheId])
        {
            _memoryCache.Remove(key);
        }

        _keyDictionary[cacheId] = new HashSet<object>();
    }
}

使用此功能的服务需要提供该服务独有的 cacheId。这允许 Flush 只清除与特定服务相关的键,而不是缓存中的所有内容!

答案 7 :(得分:0)

  IMemoryCache _MemoryCache;
    public CacheManager(IMemoryCache MemoryCache)
    {
        _MemoryCache = MemoryCache;
    }
    public void Clear()
    {
        _MemoryCache.Dispose();
        _MemoryCache = new MemoryCache(new MemoryCacheOptions());
    }