簡體   English   中英

如何從 ASP.NET 核心中的 IMemoryCache 中刪除所有對象(重置)

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

我可以找到一個remove方法,通過它的鍵從IMemoryCache中刪除一個對象。 有沒有辦法重置整個緩存並刪除所有對象?

編輯:

如何清除內存緩存? 鏈接中提供的 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'.

請參閱ASP.NET Core 中的 Cache in-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();
    }
}

如果可用,最簡單的方法是Compact(1.0) 此代碼將使用擴展方法清除內存緩存(在單元測試和 .NET 核心 2.2 和 3.1 上的生產中測試)。 如果Compact不可用,則使用回退方法,從公共Clear方法開始,然后是內部Clear方法。 如果這些都不可用,則拋出異常。

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

如果您使用標准 MemoryCache,此代碼會有所幫助。 文檔: https : //docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#memorycachecompact

_cache.Compact(1.0);

我的解決方案是創建一個包裝器,它重新公開現有的幾個方法,並通過用全新的對象替換 MemoryCache 對象來添加缺少的方法。 對我來說工作得很好。 代碼如下:

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

RC1 的答案是,您無法根據我所閱讀和被告知的內容開箱即用(我確實在 GitHub 上讀到過,可能有一種方法可以創建觸發器來促進即將到來的這一點)。

目前,為您提供了獲取、設置和刪除。 我認為您的選擇是:

  1. 創建一個緩存管理器包裝器來跟蹤您的所有密鑰,然后您可以根據需要批量刪除這些項目。 我不喜歡這個,但它會起作用。 當然,如果您不是控制添加的人,則緩存中可能存在您不知道的內容(您可以將您的計數與要查看的計數進行比較)。 如果將 IMemoryCache 轉換為 MemoryCache,則可以獲得公開的 Count 屬性。
  2. 分叉程序集並公開密鑰和/或添加刪除這些項目的方法。 有一個保存鍵的底層字典。 我這樣做了,編譯了它,為它創建了一個 Nuget 包,然后替換了 RC1 版本只是為了看看我是否可以(並且它有效)。 不確定這是否是正確的方法,但這是對我的 fork 的提交,我剛剛添加了一個只讀屬性,我將鍵轉儲到對象列表(鍵存儲為對象)。 與過去的 MemoryCache 實現一樣,如果您公開密鑰,它們在轉儲后可能會過時,但如果您只是使用它們來清除所有內容,那么這無關緊要。

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

我昨晚發布了這個問題,試圖找出是否有一種專門檢查緩存中內容的好方法(問為什么我們沒有辦法)。 如果你不問,你永遠不會知道這是否重要,所以我想為什么不。

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

我的解決方案是將緩存中所有項目的新到期日期設置為 1 毫秒。 然后它們過期,因此緩存刷新。

我通過在 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只清除與特定服務相關的鍵,而不是緩存中的所有內容!

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

不要使用緊湊型解決方案 - 它只是不起作用。

我在整個項目中都使用 IMemoryCache。 在特定時間,我有 42k 個條目。 調用 Compact(1.0) 后,還剩下 14k。

這里似乎描述了唯一的工作方式: How to retrieve a list of Memory Cache keys in asp.net core?

適應了這個問題,我最終使用它如下:

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

不要忘記投票鏈接的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM