Listing Problem
The MemoryCache.GetEnumerator () block warns: "Retrieving an enumerator for an instance of MemoryCache is a resource-intensive and blocking operation. The enumerator should not be used in production applications."
This is why , explained in the pseudo code of the GetEnumerator () implementation:
Create a new Dictionary object (let 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
Since the implementation breaks the cache into several Dictionary objects, it must put everything together into one collection to pass a counter. Each call to GetEnumerator performs the complete copy process described above. The newly created dictionary contains references to the original internal key and value objects, so your actual cached data values ββare not duplicated.
The warning in the documentation is correct. Avoid GetEnumerator () - including all the answers above that use LINQ queries.
The best and most flexible solution
Here's an effective way to clear the cache, which simply builds on your existing change monitoring infrastructure. It also provides the flexibility to clear either the entire cache or just a named subset and does not have any of the problems discussed above.
// 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; } } }
Thomas F. Abraham Mar 13 '14 at 19:26 2014-03-13 19:26
source share