c# - ¿Cómo borro un System.Runtime.Caching.MemoryCache?
asp.net .net-4.0 (6)
Uso System.Runtime.Caching.MemoryCache
para guardar elementos que nunca caducan. Sin embargo, a veces necesito la capacidad de borrar todo el caché. ¿Cómo puedo hacer eso?
Hice una pregunta similar here sobre si podría enumerar el caché, pero esa es una mala idea, ya que debe sincronizarse durante la enumeración.
Intenté usar .Trim(100)
pero eso no funciona en absoluto.
Intenté obtener una lista de todas las llaves a través de Linq, pero luego volví al punto en el que comencé porque el desalojo de elementos uno por uno puede conducir fácilmente a condiciones de carrera.
Pensé en guardar todas las claves, y luego emitir un .Remove(key)
para cada una, pero también hay una condición implícita de carrera, así que tendría que bloquear el acceso a la lista de claves, y las cosas se vuelven complicadas otra vez. .
Entonces pensé que debería poder llamar a .Dispose()
en todo el caché, pero no estoy seguro de si este es el mejor enfoque, debido a la forma en que se implementa.
Usar ChangeMonitors
no es una opción para mi diseño, y es innecesariamente complejo para un requisito tan trivial.
Entonces, ¿cómo borro completamente el caché?
Estaba luchando con esto al principio. MemoryCache.Default.Trim (100) no funciona (como se discutió). Recortar es un mejor intento, por lo que si hay 100 elementos en la memoria caché, y usted llama a Trim (100), eliminará los menos utilizados.
Trim devuelve el recuento de elementos eliminados, y la mayoría de las personas esperan que se eliminen todos los elementos.
Este código elimina todos los elementos de MemoryCache en mis pruebas xUnit con MemoryCache.Default. MemoryCache.Default es la región predeterminada.
foreach (var element in MemoryCache.Default)
{
MemoryCache.Default.Remove(element.Key);
}
Esto es lo que hice para algo en lo que estaba trabajando ...
public void Flush()
{
List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
MemoryCache.Default.Remove(cacheKey);
}
}
Los detalles en la respuesta de @stefan detallan el principio; así es como lo haría.
Se debe sincronizar el acceso a la memoria caché mientras se recrea, para evitar la condición de carrera del código del cliente al acceder a la memoria caché después de que se elimine, pero antes de que se vuelva a crear.
Para evitar esta sincronización, haga esto en su clase de adaptador (que ajusta MemoryCache):
public void clearCache() {
var oldCache = TheCache;
TheCache = new MemoryCache("NewCacheName", ...);
oldCache.Dispose();
GC.Collect();
}
De esta forma, TheCache
siempre está en un estado no dispuesto y no se necesita sincronización.
Me encontré con este problema también. .Dispose () hizo algo bastante diferente de lo que esperaba.
En cambio, agregué un campo estático a mi clase de controlador. No usé el caché predeterminado para evitar este comportamiento, pero creé uno privado (si desea llamarlo así). Entonces mi implementación se veía un poco así:
public class MyController : Controller
{
static MemoryCache s_cache = new MemoryCache("myCache");
public ActionResult Index()
{
if (conditionThatInvalidatesCache)
{
s_cache = new MemoryCache("myCache");
}
String s = s_cache["key"] as String;
if (s == null)
{
//do work
//add to s_cache["key"]
}
//do whatever next
}
}
No debe llamar a dispose en el miembro predeterminado de MemoryCache si desea poder usarlo más:
El estado de la memoria caché está configurado para indicar que la caché está dispuesta. Cualquier intento de llamar a los métodos públicos de almacenamiento en caché que cambian el estado de la memoria caché, como los métodos que agregan, eliminan o recuperan entradas de la memoria caché, puede causar un comportamiento inesperado. Por ejemplo, si llama al método Set después de que se descarta la caché, se produce un error de no operación. Si intenta recuperar elementos de la memoria caché, el método Get siempre devolverá Nothing. http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx
Acerca del Trim, se supone que funciona:
La propiedad Trim primero elimina las entradas que han superado una expiración absoluta o deslizante. Cualquier devolución de llamada que se registre para elementos que se eliminen se pasará una razón eliminada de Expirado.
Si la eliminación de entradas caducadas es insuficiente para alcanzar el porcentaje de ajuste especificado, las entradas adicionales se eliminarán de la caché en función de un algoritmo utilizado menos recientemente (LRU) hasta que se alcance el porcentaje de ajuste solicitado.
Pero otros dos usuarios informaron que no funciona en la misma página, así que supongo que estás atrapado con Remove () http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx
Actualización Sin embargo, no veo ninguna mención de que sea singleton o no sea seguro tener varias instancias, por lo que debería poder sobreescribir su referencia.
Pero si necesita liberar la memoria de la instancia Predeterminada, tendrá que borrarla manualmente o destruirla permanentemente a través de dispose (dejándolo inutilizable).
Basándote en tu pregunta, podrías hacer que tu propia clase impone una memoria caché que puedas desechar internamente a voluntad. Ser la naturaleza de un caché :-)
Vea this publicación, y específicamente, la respuesta que Thomas F. Abraham publicó. Tiene una solución que le permite borrar todo el caché o un subconjunto con nombre.
La clave aquí es:
// Cache objects are obligated to remove entry upon change notification.
base.OnChanged(null);
Lo he implementado yo mismo, y todo parece funcionar bien.