web-dev-qa-db-fra.com

Comment effacer un System.Runtime.Caching.MemoryCache

J'utilise un System.Runtime.Caching.MemoryCache pour contenir des éléments qui n'expirent jamais. Cependant, j'ai parfois besoin de pouvoir effacer tout le cache. Comment je fais ça?

J'ai posé une question similaire ici concernant le point de savoir si je pouvais énumérer le cache, mais c'est une mauvaise idée car il doit être synchronisé pendant l'énumération.

J'ai essayé d'utiliser .Trim(100) mais cela ne fonctionne pas du tout.

J'ai essayé d'obtenir une liste de toutes les clés via Linq, mais je suis revenu à mon point de départ, car expulser des objets un par un peut facilement conduire à des conditions de course.

J'ai pensé stocker toutes les clés, puis émettre une .Remove(key) pour chacune d'entre elles, mais il y a également une condition de concurrence implicite. Je devrais donc verrouiller l'accès à la liste des clés et tout redeviendra compliqué.

J'ai alors pensé que je devrais pouvoir appeler .Dispose() sur l'intégralité du cache, mais je ne suis pas sûr que ce soit la meilleure approche, en raison de la façon dont elle est mise en œuvre.

Utiliser ChangeMonitors n'est pas une option pour ma conception, et est excessivement complexe pour une exigence aussi triviale.

Alors, comment puis-je vider complètement le cache?

31
Peter Marks

Vous ne devez pas appeler dispose sur le membre Default du MemoryCache si vous voulez pouvoir l'utiliser plus tard:

L'état du cache est défini pour indiquer que le cache est supprimé . Toute tentative d'appeler des méthodes de mise en cache publiques qui modifient l'état de le cache, telles que les méthodes qui ajoutent, suppriment ou récupèrent le cache entrées, peut provoquer un comportement inattendu. Par exemple, si vous appelez le Définir la méthode après la suppression du cache, une erreur No-op se produit. Si vous Pour tenter de récupérer des éléments du cache, la méthode Get utilisera toujours retourne rien . http://msdn.Microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx

À propos de la garniture, elle est censée fonctionner:

La propriété Trim supprime d'abord les entrées qui ont dépassé une expiration absolue ou glissante. Tous les rappels enregistrés pour les éléments supprimés, le motif Expiré a été supprimé.

Si la suppression des entrées expirées est insuffisante pour atteindre le pourcentage de trim spécifié, des entrées supplémentaires seront supprimées du cache basé sur un algorithme le moins récemment utilisé (LRU) jusqu'à la demande le pourcentage de finition est atteint.

Mais deux autres utilisateurs ont signalé que cela ne fonctionnait pas sur la même page, donc je suppose que vous êtes coincé avec Remove () http://msdn.Microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx

Update Cependant, je ne vois aucune mention de ce qui est singleton ou autrement dangereux d'avoir plusieurs instances, vous devriez donc pouvoir écraser votre référence.

Mais si vous devez libérer la mémoire de l'instance par défaut, vous devrez l'effacer manuellement ou la détruire définitivement via dispose (la rendant inutilisable).

Sur la base de votre question, vous pouvez créer votre propre classe imposante pour singleton, renvoyant un Memorycache que vous pouvez en disposer en interne à votre guise. Etant la nature d'une cache :-)

14
stefan

J'avais du mal avec ça au début. MemoryCache.Default.Trim (100) ne fonctionne pas (comme indiqué). Le rognage est la meilleure tentative. Par conséquent, s'il y a 100 éléments dans le cache et que vous appelez le rognage (100), les éléments les moins utilisés seront supprimés. 

Couper renvoie le nombre d'éléments supprimés et la plupart des gens s'attendent à ce que tous les éléments soient supprimés.

Ce code supprime tous les éléments de MemoryCache pour moi dans mes tests xUnit avec MemoryCache.Default. MemoryCache.Default est la région par défaut.

foreach (var element in MemoryCache.Default)
{
    MemoryCache.Default.Remove(element.Key);
}
29

Voici ce que j'avais fait pour quelque chose sur lequel je travaillais ...

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

Les détails de la réponse de @ stefan détaillent le principe; voici comment je le ferais.

Il convient de synchroniser l’accès au cache lors de sa reconstitution, afin d’éviter que le code client accède au cache après sa suppression, mais avant sa reconstitution.

Pour éviter cette synchronisation, procédez comme suit dans votre classe d'adaptateur (qui enveloppe le MemoryCache):

public void clearCache() {
  var oldCache = TheCache;
  TheCache = new MemoryCache("NewCacheName", ...);
  oldCache.Dispose();
  GC.Collect();
}

De cette façon, TheCache est toujours dans un état non disposé et aucune synchronisation n'est nécessaire.

2
Peter Marks

Découvrez this post, et plus précisément, la réponse que Thomas F. Abraham posted. Il a une solution qui vous permet d’effacer tout le cache ou un sous-ensemble nommé. 

L'essentiel ici est:

// Cache objects are obligated to remove entry upon change notification.
base.OnChanged(null);

J'ai implémenté cela moi-même et tout semble bien fonctionner.

0
Jowen

J'ai rencontré ce problème aussi. .Dispose () a fait quelque chose de très différent de ce à quoi je m'attendais.

Au lieu de cela, j'ai ajouté un champ statique à ma classe de contrôleur. Je n'ai pas utilisé le cache par défaut pour contourner ce problème, mais j'ai créé un cache privé (si vous voulez l'appeler ainsi). Donc, mon implémentation ressemblait un peu à ceci:

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
    }
}
0
Richard Lander