简体   繁体   English

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

[英]How to remove all objects (reset ) from IMemoryCache in ASP.NET core

I can find a remove method to remove an object from IMemoryCache by its key.我可以找到一个remove方法,通过它的键从IMemoryCache中删除一个对象。 Is there a way to reset the whole cache and remove all objects?有没有办法重置整个缓存并删除所有对象?

Edit:编辑:

How to clear MemoryCache? 如何清除内存缓存? Dispose method provided in the link gives me an exception in asp.net 5. ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.Extensions.Caching.Memory.MemoryCache'.链接中提供的 Dispose 方法在 asp.net 5 中给了我一个异常。 ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.Extensions.Caching.Memory.MemoryCache'. ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.Extensions.Caching.Memory.MemoryCache'.

See Cache in-memory in ASP.NET Core , specifically the section on Cache dependencies .请参阅ASP.NET Core 中的 Cache in-memory ,特别是有关缓存依赖项的部分。

Using a CancellationTokenSource allows multiple cache entries to be evicted as a group使用 CancellationTokenSource 允许将多个缓存条目作为一个组逐出

This code worked for me:这段代码对我有用:

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();
    }
}

The easiest way is Compact(1.0) if it's available.如果可用,最简单的方法是Compact(1.0) This code will clear the memory cache using an extension method (tested in unit tests and on production on .NET core 2.2 and 3.1).此代码将使用扩展方法清除内存缓存(在单元测试和 .NET 核心 2.2 和 3.1 上的生产中测试)。 If Compact is not available, then fallback methods are used, starting with a public Clear method, followed by an internal Clear method.如果Compact不可用,则使用回退方法,从公共Clear方法开始,然后是内部Clear方法。 If none of those are available, an exception is thrown.如果这些都不可用,则抛出异常。

/// <summary>
/// Clear IMemoryCache
/// </summary>
/// <param name="cache">Cache</param>
/// <exception cref="InvalidOperationException">Unable to clear memory cache</exception>
/// <exception cref="ArgumentNullException">Cache is null</exception>
public static void Clear(this IMemoryCache cache)
{
    if (cache == null)
    {
        throw new ArgumentNullException("Memory cache must not be null");
    }
    else if (cache is MemoryCache memCache)
    {
        memCache.Compact(1.0);
        return;
    }
    else
    {
        MethodInfo clearMethod = cache.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public);
        if (clearMethod != null)
        {
            clearMethod.Invoke(cache, null);
            return;
        }
        else
        {
            PropertyInfo prop = cache.GetType().GetProperty("EntriesCollection", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Public);
            if (prop != null)
            {
                object innerCache = prop.GetValue(cache);
                if (innerCache != null)
                {
                    clearMethod = innerCache.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public);
                    if (clearMethod != null)
                    {
                        clearMethod.Invoke(innerCache, null);
                        return;
                    }
                }
            }
        }
    }

    throw new InvalidOperationException("Unable to clear memory cache instance of type " + cache.GetType().FullName);
}

This code can help if you're using the standard MemoryCache.如果您使用标准 MemoryCache,此代码会有所帮助。 Documentation: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#memorycachecompact文档: https : //docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#memorycachecompact

_cache.Compact(1.0);

My solution was to create a wrapper which re-expose existing few methods and add a missing method by replacing MemoryCache object with a brand new one.我的解决方案是创建一个包装器,它重新公开现有的几个方法,并通过用全新的对象替换 MemoryCache 对象来添加缺少的方法。 Worked just fine for me.对我来说工作得很好。 Code is below:代码如下:

public interface IMyMemoryCache : IMemoryCache
{
    void Reset();
}
public class MyMemoryCache: IMyMemoryCache
{
    IMemoryCache _memoryCache;

    public MyMemoryCache()
    {
        Reset();
    }
    public void Dispose()
    {
        _memoryCache.Dispose();
    }

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

    public ICacheEntry CreateEntry(object key)
    {
        return _memoryCache.CreateEntry(key);
    }

    public void Remove(object key)
    {
        _memoryCache.Remove(key);
    }

    public void Reset()
    {
        var existingCache = _memoryCache;
        _memoryCache = new MemoryCache(new MemoryCacheOptions());

        // Dispose existing cache (we override) in 10 minutes
        if (existingCache != null)
        {
            System.Threading.Tasks.Task.Delay(TimeSpan.FromMinutes(10))
                .ContinueWith(t =>
                {
                    existingCache.Dispose();
                });
        }
    }
}

The answer as of RC1 is that you can't do it out of the box from what I've read and been told (I did read on GitHub that there maybe a way to create triggers to facilitate this that are coming). RC1 的答案是,您无法根据我所阅读和被告知的内容开箱即用(我确实在 GitHub 上读到过,可能有一种方法可以创建触发器来促进即将到来的这一点)。

Currently, you are provided Get, Set and Remove.目前,为您提供了获取、设置和删除。 I see your options as:我认为您的选择是:

  1. Create a cache manager wrapper that will track all of your keys, you can then remove those items in bulk as you see fit.创建一个缓存管理器包装器来跟踪您的所有密钥,然后您可以根据需要批量删除这些项目。 I'm not in love with this but it would work.我不喜欢这个,但它会起作用。 Of course, if you're not the one controlling the adding there could be things in the cache you are unaware of (you could compare your count to it's count to see).当然,如果您不是控制添加的人,则缓存中可能存在您不知道的内容(您可以将您的计数与要查看的计数进行比较)。 If you cast IMemoryCache as MemoryCache you can get the Count property which is exposed.如果将 IMemoryCache 转换为 MemoryCache,则可以获得公开的 Count 属性。
  2. Fork the assembly and expose the Keys and/or add a method to remove those items.分叉程序集并公开密钥和/或添加删除这些项目的方法。 There is an underlying dictionary that holds the keys.有一个保存键的底层字典。 I did this, compiled it, create a Nuget package for it and then replaced the RC1 version just to see if I could (and it worked).我这样做了,编译了它,为它创建了一个 Nuget 包,然后替换了 RC1 版本只是为了看看我是否可以(并且它有效)。 Not sure if this is the right way but here's the commit to my fork, I just added a read only property where I dumped the keys to an object list (the keys are stored as objects).不确定这是否是正确的方法,但这是对我的 fork 的提交,我刚刚添加了一个只读属性,我将键转储到对象列表(键存储为对象)。 As with past MemoryCache implementations, if you exposed the keys they could be stale after they're dumped, but if you're just using them to clear all then that shouldn't matter.与过去的 MemoryCache 实现一样,如果您公开密钥,它们在转储后可能会过时,但如果您只是使用它们来清除所有内容,那么这无关紧要。

https://github.com/blakepell/Caching/commit/165ae5ec13cc51c44a6007d6b88bd9c567e1d724 https://github.com/blakepell/Caching/commit/165ae5ec13cc51c44a6007d6b88bd9c567e1d724

I posted this issue last night trying to figure out if there's a good way to inspect what's in the cache specifically (asking why don't we have a way to).我昨晚发布了这个问题,试图找出是否有一种专门检查缓存中内容的好方法(问为什么我们没有办法)。 If you don't ask you'll never know if it would have mattered so I figured why not.如果你不问,你永远不会知道这是否重要,所以我想为什么不。

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

my solution was to set new expiration date to all the items in cache to 1 millisecond.我的解决方案是将缓存中所有项目的新到期日期设置为 1 毫秒。 Then they expired and hence cache flush.然后它们过期,因此缓存刷新。

I solved it by creating a FlushableMemoryCache singleton around IMemoryCache, which tracks the keys currently stored in the cache and then can just iterate over them to flush them all:我通过在 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>();
    }
}

The services which make use of this will need to provide a cacheId which is unique to that service.使用此功能的服务将需要提供该服务独有的cacheId That allows the Flush to only clear keys related to the specific service, and not everything in the cache!这允许Flush只清除与特定服务相关的键,而不是缓存中的所有内容!

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

Don't use the compact-solution - it just doesn't work.不要使用紧凑型解决方案 - 它只是不起作用。

I use IMemoryCache throughout my entire project.我在整个项目中都使用 IMemoryCache。 At a specific time I had like 42k entries.在特定时间,我有 42k 个条目。 After calling Compact(1.0) there were still 14k left.调用 Compact(1.0) 后,还剩下 14k。

The only working way seems to be described here: How to retrieve a list of Memory Cache keys in asp.net core?这里似乎描述了唯一的工作方式: How to retrieve a list of Memory Cache keys in asp.net core?

Adapted to this problem I ended up using it as follows:适应了这个问题,我最终使用它如下:

public static class MemoryCacheExtensions
{
    private static readonly Func<MemoryCache, object> GetEntriesCollection = Delegate.CreateDelegate(
        typeof(Func<MemoryCache, object>),
        typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetGetMethod(true),
        throwOnBindFailure: true) as Func<MemoryCache, object>;

    public static IEnumerable GetKeys(this IMemoryCache memoryCache) =>
        ((IDictionary)GetEntriesCollection((MemoryCache)memoryCache)).Keys;

    public static IEnumerable<T> GetKeys<T>(this IMemoryCache memoryCache) =>
        GetKeys(memoryCache).OfType<T>();

    public static void Clear(this IMemoryCache memoryCache) => ((IDictionary)GetEntriesCollection((MemoryCache)memoryCache)).Clear();
}

Don't forget to upvote the linked answer.不要忘记投票链接的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 XUnit如何模拟IMemoryCache ASP.NET Core - XUnit how to mock IMemoryCache ASP.NET Core ASP.NET 内核将带 IMemoryCache 的内存缓存转换为 Redis - ASP.NET Core Converting In-Memory Cache with IMemoryCache to Redis 为什么在ASP.Net Core中获取IMemoryCache的多个实例? - Why getting multiple instances of IMemoryCache in ASP.Net Core? 在 ASP.net 核心应用程序中使用 IMemoryCache 和 Unity DI Container - Use IMemoryCache with Unity DI Container in ASP.net core application 如果*不*注入依赖项,是否可以在 ASP.NET Core 中使用 IMemoryCache? - Possible to use IMemoryCache in ASP.NET Core if it is *not* dependency injected? 测试ASP.NET Core IMemoryCache的正确方法 - Proper way of testing ASP.NET Core IMemoryCache ASP.NET 内核 - 更新缓存列表的 1 个元素<element>在 IMemoryCache 中使用 EF Core</element> - ASP.NET Core - Update 1 Element of a cached List<Element> in IMemoryCache using EF Core ASP.NET Core 2所有用户密码重置为admin - ASP.NET Core 2 all users password reset as admin ASP.NET核心。 重置所有身份验证 - ASP.NET CORE. Reset all authentications 如何在 ASP.NET Core 中删除当前用户的声明? - How to remove claims from current user in ASP.NET Core?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM