Das Problem mit der Aufzählung
Der Abschnitt MemoryCache.GetEnumerator () Remarks warnt: "Das Abrufen eines Enumerators für eine MemoryCache-Instanz ist eine ressourcenintensive und blockierende Operation. Daher sollte der Enumerator nicht in Produktionsanwendungen verwendet werden."
Im Pseudocode der GetEnumerator () -Implementierung wird der folgende Grund erläutert:
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
Da die Implementierung den Cache auf mehrere Dictionary-Objekte aufteilt, muss alles in einer einzigen Sammlung zusammengefasst werden, um einen Enumerator zurückzugeben. Jeder Aufruf von GetEnumerator führt den oben beschriebenen vollständigen Kopiervorgang aus. Das neu erstellte Wörterbuch enthält Verweise auf die ursprünglichen internen Schlüssel- und Wertobjekte, sodass Ihre tatsächlich zwischengespeicherten Datenwerte nicht dupliziert werden.
Die Warnung in der Dokumentation ist korrekt. Vermeiden Sie GetEnumerator () - einschließlich aller obigen Antworten, die LINQ-Abfragen verwenden.
Eine bessere und flexiblere Lösung
Hier ist eine effiziente Methode zum Löschen des Caches, die einfach auf der vorhandenen Infrastruktur zur Änderungsüberwachung aufbaut. Es bietet auch die Flexibilität, entweder den gesamten Cache oder nur eine benannte Teilmenge zu löschen, und weist keines der oben diskutierten Probleme auf.
// 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;
}
}
}