如何清除MemoryCache?

问题描述 投票:0回答:13

我使用 MemoryCache 类创建了一个缓存。我向其中添加了一些项目,但是当我需要重新加载缓存时,我想先清除它。最快的方法是什么?我应该遍历所有项目并一次删除它们还是有更好的方法?

c# caching memory .net-4.0 memorycache
13个回答
76
投票

Dispose
现有的MemoryCache并创建一个新的MemoryCache对象。


61
投票

枚举的问题

MemoryCache.GetEnumerator() 备注部分警告:“检索 MemoryCache 实例的枚举器是一项资源密集型且阻塞的操作。因此,不应在生产应用程序中使用枚举器。”

原因如下,在 GetEnumerator() 实现的伪代码中进行了解释:

Create a new Dictionary object (let's call it AllCache) For Each per-processor segment in the cache (one Dictionary object per processor) { Lock the segment/Dictionary (using lock construct) Iterate through the segment/Dictionary and add each name/value pair one-by-one to the AllCache Dictionary (using references to the original MemoryCacheKey and MemoryCacheEntry objects) } Create and return an enumerator on the AllCache Dictionary

由于该实现将缓存拆分为多个 Dictionary 对象,因此它必须将所有内容合并到一个集合中,以便交回枚举器。每次调用 GetEnumerator 都会执行上面详述的完整复制过程。新创建的字典包含对原始内部键和值对象的引用,因此您的实际缓存数据值不会重复。

文档中的警告是正确的。避免使用 GetEnumerator() —— 包括上面所有使用 LINQ 查询的答案。

更好、更灵活的解决方案

这是一种清除缓存的有效方法,它简单地构建在现有的变更监控基础设施上。它还提供了清除整个缓存或仅清除指定子集的灵活性,并且没有上面讨论的任何问题。

// By Thomas F. Abraham (http://www.tfabraham.com) namespace CacheTest { using System; using System.Diagnostics; using System.Globalization; using System.Runtime.Caching; public class SignaledChangeEventArgs : EventArgs { public string Name { get; private set; } public SignaledChangeEventArgs(string name = null) { this.Name = name; } } /// <summary> /// Cache change monitor that allows an app to fire a change notification /// to all associated cache items. /// </summary> public class SignaledChangeMonitor : ChangeMonitor { // Shared across all SignaledChangeMonitors in the AppDomain private static event EventHandler<SignaledChangeEventArgs> Signaled; private string _name; private string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); public override string UniqueId { get { return _uniqueId; } } public SignaledChangeMonitor(string name = null) { _name = name; // Register instance with the shared event SignaledChangeMonitor.Signaled += OnSignalRaised; base.InitializationComplete(); } public static void Signal(string name = null) { if (Signaled != null) { // Raise shared event to notify all subscribers Signaled(null, new SignaledChangeEventArgs(name)); } } protected override void Dispose(bool disposing) { SignaledChangeMonitor.Signaled -= OnSignalRaised; } private void OnSignalRaised(object sender, SignaledChangeEventArgs e) { if (string.IsNullOrWhiteSpace(e.Name) || string.Compare(e.Name, _name, true) == 0) { Debug.WriteLine( _uniqueId + " notifying cache of change.", "SignaledChangeMonitor"); // Cache objects are obligated to remove entry upon change notification. base.OnChanged(null); } } } public static class CacheTester { public static void TestCache() { MemoryCache cache = MemoryCache.Default; // Add data to cache for (int idx = 0; idx < 50; idx++) { cache.Add("Key" + idx.ToString(), "Value" + idx.ToString(), GetPolicy(idx)); } // Flush cached items associated with "NamedData" change monitors SignaledChangeMonitor.Signal("NamedData"); // Flush all cached items SignaledChangeMonitor.Signal(); } private static CacheItemPolicy GetPolicy(int idx) { string name = (idx % 2 == 0) ? null : "NamedData"; CacheItemPolicy cip = new CacheItemPolicy(); cip.AbsoluteExpiration = System.DateTimeOffset.UtcNow.AddHours(1); cip.ChangeMonitors.Add(new SignaledChangeMonitor(name)); return cip; } } }
    

38
投票
来自

http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

解决方法是:

List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList(); foreach (string cacheKey in cacheKeys) { MemoryCache.Default.Remove(cacheKey); }
    

25
投票
var cacheItems = cache.ToList(); foreach (KeyValuePair<String, Object> a in cacheItems) { cache.Remove(a.Key); }
    

13
投票
如果性能不是问题,那么这个漂亮的单行就可以解决问题:

cache.ToList().ForEach(a => cache.Remove(a.Key));
    

10
投票
好像有一个

Trim的方法。

所以要清除所有内容,你只需要做

cache.Trim(100)

编辑: 深入研究后,似乎不值得花时间去研究 Trim

https://connect.microsoft.com/VisualStudio/feedback/details/831755/memorycache-trim-method-doesnt-evict-100-of-the-items

如何清除 System.Runtime.Caching.MemoryCache


4
投票
遇到了这个,并在此基础上写了一个稍微更有效的并行清除方法:

public void ClearAll() { var allKeys = _cache.Select(o => o.Key); Parallel.ForEach(allKeys, key => _cache.Remove(key)); }
    

3
投票
你也可以这样做:

Dim _Qry = (From n In CacheObject.AsParallel() Select n).ToList() For Each i In _Qry CacheObject.Remove(i.Key) Next
    

1
投票
您可以处置MemoryCache.Default缓存,然后将私有字段单例重新设置为null,以使其重新创建MemoryCache.Default。

var field = typeof(MemoryCache).GetField("s_defaultCache", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, null);
    

1
投票
我只对清除缓存感兴趣,并在使用 c# GlobalCachingProvider 时发现这是一个选项

var cache = GlobalCachingProvider.Instance.GetAllItems(); if (dbOperation.SuccessLoadingAllCacheToDB(cache)) { cache.Clear(); }
    

0
投票
马格利特答案的稍微改进版本。

var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList(); foreach (string cacheKey in cacheKeys) { MemoryCache.Default.Remove(cacheKey); }
    

0
投票
这个讨论也在这里进行:

https://learn.microsoft.com/en-us/answers/answers/983399/view.html

我在那里写了一个答案,我将在这里转录:

using System.Collections.Generic; using Microsoft.Extensions.Caching.Memory; using ServiceStack; public static class IMemoryCacheExtensions { static readonly List<object> entries = new(); /// <summary> /// Removes all entries, added via the "TryGetValueExtension()" method /// </summary> /// <param name="cache"></param> public static void Clear(this IMemoryCache cache) { for (int i = 0; i < entries.Count; i++) { cache.Remove(entries[i]); } entries.Clear(); } /// <summary> /// Use this extension method, to be able to remove all your entries later using "Clear()" method /// </summary> /// <typeparam name="TItem"></typeparam> /// <param name="cache"></param> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool TryGetValueExtension<TItem>(this IMemoryCache cache, object key, out TItem value) { entries.AddIfNotExists(key); if (cache.TryGetValue(key, out object result)) { if (result == null) { value = default; return true; } if (result is TItem item) { value = item; return true; } } value = default; return false; } }
    

0
投票
我使用 IMemeryCache 在我的控制器中添加 DI 容器,并使用此代码来清除缓存:

private readonly IMemoryCache _memoryCache; public AdminController(IMemoryCache memoryCache) { _memoryCache = memoryCache; } [HttpGet("ClearCache")] public IActionResult ClearCache() { ClearMemoryCache(); return Ok(); } private void ClearMemoryCache() { var cacheImplType = typeof(MemoryCache); var entriesField = cacheImplType.GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); var entries = (IDictionary)entriesField?.GetValue(_memoryCache); entries?.Clear(); }

它对我有用。

© www.soinside.com 2019 - 2024. All rights reserved.