2010-11-15 12 views
75

He creado un caché usando la clase MemoryCache. Le agrego algunos elementos, pero cuando necesito volver a cargar el caché, quiero borrarlo primero. ¿Cuál es la forma más rápida de hacer esto? ¿Debería recorrer todos los elementos y eliminarlos uno a la vez o hay una forma mejor?¿Cómo borrar MemoryCache?

Respuesta

49

Dispose MemoryCache existente y crea un nuevo objeto MemoryCache.

+2

Inicialmente usé MemoryCache.Default, haciendo que Dispose me causara un poco de dolor. Aún así, Dispose terminó siendo la mejor solución que pude encontrar. Gracias. – LaustN

+8

@LaustN ¿Puede explicar el "dolor" causado por MemoryCache.Default? Actualmente uso MemoryCache.Default ... La documentación de MemoryCache de MSDN me hace preguntarme si se recomienda deshacer y recrear: "No cree instancias de MemoryCache a menos que sea necesario. Si crea instancias de caché en aplicaciones cliente y Web, las instancias de MemoryCache deberían crearse temprano en el ciclo de vida de la aplicación ". ¿Esto se aplica a .Default? No digo que utilizar Dispose sea incorrecto, sinceramente solo estoy buscando una aclaración sobre todo esto. –

+4

Pensé que valía la pena mencionar que 'Dispose' _does_ invoca cualquier' CacheEntryRemovedCallback' adjunta a los elementos actuales almacenados en caché. –

3

También podría hacer algo como esto:


Dim _Qry = (From n In CacheObject.AsParallel() 
      Select n).ToList() 
For Each i In _Qry 
    CacheObject.Remove(i.Key) 
Next 
18
var cacheItems = cache.ToList(); 

foreach (KeyValuePair<String, Object> a in cacheItems) 
{ 
    cache.Remove(a.Key); 
} 
+1

Esto tiene el mismo riesgo que la respuesta de @ Tony; por favor mira mi comentario debajo de eso. – TrueWill

+0

@TrueWill ¿Quién es o era @Tony? –

+2

@AlexAngas - Puede haber cambiado su nombre a magritte. Ver también http://stackoverflow.com/questions/4183270/how-to-clear-the-net-4-memorycache/22388943#22388943 – TrueWill

26

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

La solución es:

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

De la [documentación] (http://msdn.microsoft.com/en- us/library/system.runtime.caching.memorycache.getenumerator.aspx): _Recuperar un enumerador para una instancia de MemoryCache es una operación de uso intensivo y de bloqueo de recursos. Por lo tanto, el enumerador no se debe usar en aplicaciones de producción. – TrueWill

+0

No creo que esto sea lo mismo que recuperar un enumerador. Esto es bastante rápido y no enumera nada. Aún así ... me gusta deshacerme del caché. – hal9000

+1

@emberdude Es exactamente lo mismo que recuperar un enumerador. ¿Qué le parece la implementación de 'Select()'? – RobSiklos

0

un poco de mejora de la respuesta Magritte.

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

Si el rendimiento no es un problema, este bonito de una sola línea hará el truco:

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

El problema con la enumeración

El MemoryCache.GetEnumerator() Remarks section advierte: "Recuperación de un enumerador para una La instancia de MemoryCache es una operación de bloqueo y de uso intensivo de recursos. Por lo tanto, el enumerador no se debe usar en aplicaciones de producción ".

He aquí por qué, se explica en pseudocódigo de la GetEnumerator() aplicación:

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 

Desde la implementación divide la memoria caché en varios objetos de diccionario, que debe reunir todo en una sola colección con el fin de la mano volver un enumerador. Cada llamada a GetEnumerator ejecuta el proceso de copia completo detallado anteriormente. El diccionario creado recientemente contiene referencias a la clave interna original y los objetos de valor, por lo que sus valores de datos en caché no se duplican.

La advertencia en la documentación es correcta. Evite GetEnumerator(), incluidas todas las respuestas anteriores que utilizan consultas LINQ.

Una solución mejor y más flexible

Aquí es una manera eficiente de borrar la memoria caché que simplemente se basa en la infraestructura existente de seguimiento del cambio. También proporciona la flexibilidad para borrar todo el caché o solo un subconjunto con nombre y no tiene ninguno de los problemas mencionados anteriormente.

// 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; 
     } 
    } 
} 
+4

Parece una implementación de la funcionalidad regional faltante. – Jowen

+0

Muy agradable. He estado tratando de implementar algo utilizando monitores y guids de caché de memoria encadenada, pero estaba empezando a ponerse un poco feo cuando traté de ajustar la funcionalidad. – Chao

+6

No recomendaría este patrón para uso general. 1. Es lento, no es culpa de la implementación, pero el método de eliminación es extremadamente lento. 2. Si expulsa elementos de la memoria caché con un vencimiento, aún se llama a Change Monitor. 3. Mi máquina estaba tragando toda la CPU, y demorando mucho tiempo en borrar 30k elementos de la caché cuando estaba ejecutando pruebas de rendimiento. Algunas veces después de esperar más de 5 minutos acabo de matar las pruebas. –

2

Funcionó a través de esto, y en función de ello , escribió un método claro paralelo ligeramente más efectivo:

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