System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - y a-t-il des différences?

84

Je me demande s'il existe des différences entre MemoryCacheet HttpRuntime.Cache, laquelle est préférée dans les projets ASP.NET MVC?

Pour autant que je sache, les deux sont thread-safe, l'API est à première vue plus ou moins la même, alors y a-t-il une différence quand utiliser laquelle?

Giedrius
la source

Réponses:

80

HttpRuntime.Cacheobtient le Cachepour l'application actuelle.

La MemoryCacheclasse est similaire à la Cacheclasse ASP.NET .

La MemoryCacheclasse possède de nombreuses propriétés et méthodes pour accéder au cache qui vous seront familières si vous avez utilisé la Cacheclasse ASP.NET .

La principale différence entre HttpRuntime.Cacheet MemoryCacheest que ce dernier a été modifié pour le rendre utilisable par les applications .NET Framework qui ne sont pas des applications ASP.NET.

Pour une lecture supplémentaire:

Mise à jour :

Selon les commentaires des utilisateurs, il arrive que le blog de Jon davis ne fonctionne pas, c'est pourquoi j'ai mis l'article en entier sous forme d'image.

Remarque: si ce n'est pas clair, cliquez simplement sur l'image, après quoi elle s'ouvrira sur un navigateur, puis cliquez à nouveau dessus pour zoomer :)

entrez la description de l'image ici

Sampath
la source
Cet article de John Davis est vraiment bien lu - des avantages et des inconvénients clairs en un seul endroit.
Giedrius
absolument tout est au même endroit. En outre, Davis a également mentionné 4 méthodes de mise en cache différentes.
Sampath
2
@Spikeh Le chargement est parfait pour moi.
user247702
1
@Stijn Merci, ne chargeait pas l'autre jour, mais il est de retour maintenant :)
Spikeh
1
@sampath ça marche maintenant. Hier, il semblait que le site avait été piraté. Merci de votre aide!
Baga
24

Voici l'article de Jon Davis. Pour préserver la lisibilité, je supprime la section EntLib désormais obsolète, l'intro ainsi que la conclusion.


Cache ASP.NET

ASP.NET ou l'assembly System.Web.dll dispose d'un mécanisme de mise en cache. Il n'a jamais été conçu pour être utilisé en dehors d'un contexte Web, mais il peut être utilisé en dehors du Web et il exécute tous les comportements d'expiration ci-dessus dans une sorte de table de hachage.

Après avoir parcouru Google, il semble que de nombreuses personnes qui ont discuté de la fonctionnalité de mise en cache intégrée dans .NET ont eu recours à l'utilisation du cache ASP.NET dans leurs projets non Web. Ce n'est plus le système de mise en cache intégré le plus disponible et le plus pris en charge dans .NET; .NET 4 a un ObjectCache que je reviendrai plus tard. Microsoft a toujours insisté sur le fait que le cache ASP.NET n'est pas destiné à être utilisé en dehors du Web. Mais beaucoup de gens sont toujours bloqués dans .NET 2.0 et .NET 3.5 et ont besoin de quelque chose avec lequel travailler, et cela fonctionne pour beaucoup de gens, même si MSDN dit clairement:

Remarque: la classe Cache n'est pas destinée à être utilisée en dehors des applications ASP.NET. Il a été conçu et testé pour une utilisation dans ASP.NET pour fournir la mise en cache pour les applications Web. Dans d'autres types d'applications, telles que les applications console ou les applications Windows Forms, la mise en cache ASP.NET peut ne pas fonctionner correctement.

La classe du cache ASP.NET est System.Web.Caching.Cache dans System.Web.dll. Cependant, vous ne pouvez pas simplement créer un nouvel objet Cache. Vous devez l'acquérir à partir de System.Web.HttpRuntime.Cache.

Cache cache = System.Web.HttpRuntime.Cache;

L'utilisation du cache ASP.NET est documentée sur MSDN ici .

Avantages:

  1. C'est intégré .
  2. Malgré la syntaxe .NET 1.0, son utilisation est assez simple .
  3. Lorsqu'il est utilisé dans un contexte Web, il est bien testé . En dehors des contextes Web, selon les recherches Google, il n'est généralement pas connu pour causer des problèmes, malgré la recommandation de Microsoft, tant que vous utilisez .NET 2.0 ou version ultérieure.
  4. Vous pouvez être averti via un délégué lorsqu'un élément est supprimé, ce qui est nécessaire si vous devez le maintenir actif et que vous ne pouvez pas définir la priorité de l'élément à l'avance.
  5. Les éléments individuels ont la flexibilité de l'une des méthodes (a), (b) ou (c) d'expiration et de suppression dans la liste des méthodes de suppression en haut de cet article. Vous pouvez également associer le comportement d'expiration à la présence d'un fichier physique.

Les inconvénients:

  1. Non seulement c'est statique, il n'y en a qu'un . Vous ne pouvez pas créer votre propre type avec sa propre instance statique d'un cache. Vous ne pouvez avoir qu'un seul compartiment pour l'ensemble de votre application, point final. Vous pouvez envelopper le seau avec vos propres wrappers qui font des choses comme pré-injecter des préfixes dans les clés et supprimer ces préfixes lorsque vous retirez les paires clé / valeur. Mais il n'y a toujours qu'un seul seau. Tout est regroupé. Cela peut être une véritable nuisance si, par exemple, vous disposez d'un service qui doit mettre en cache trois ou quatre types de données différents séparément. Cela ne devrait pas être un gros problème pour des projets pathétiquement simples. Mais si un projet présente un degré de complexité significatif en raison de ses exigences, le cache ASP.NET ne suffit généralement pas.
  2. Les objets peuvent disparaître, bon gré mal gré. Beaucoup de gens ne sont pas au courant de cela - je ne l'étais pas, jusqu'à ce que j'aie actualisé mes connaissances sur cette implémentation de cache. Par défaut, le cache ASP.NET est conçu pour détruire les éléments quand il en a envie. Plus précisément, voir (c) dans ma définition d'une table de cache en haut de cet article. Si un autre thread du même processus travaille sur quelque chose de complètement différent et qu'il vide les éléments de haute priorité dans le cache, dès que .NET décide qu'il a besoin de mémoire, il commencera à détruire certains éléments du cache selon leurs priorités, les priorités inférieures en premier. Tous les exemples documentés ici pour l'ajout d'éléments de cache utilisent la priorité par défaut, plutôt que la valeur de priorité NotRemovable qui l'empêche d'être supprimée à des fins d'effacement de la mémoire mais la supprimera toujours conformément à la politique d'expiration.
  3. La clé doit être une chaîne. Si, par exemple, vous mettez en cache des enregistrements de données où les enregistrements sont saisis sur un long ou un entier, vous devez d'abord convertir la clé en chaîne.
  4. La syntaxe est périmée . C'est la syntaxe .NET 1.0, encore plus moche que ArrayList ou Hashtable. Il n'y a pas de génériques ici, pas d'interface IDictionary <>. Il n'a pas de méthode Contains (), pas de collection Keys, pas d'événements standard; il n'a qu'une méthode Get () plus un indexeur qui fait la même chose que Get (), retournant null s'il n'y a pas de correspondance, plus Add (), Insert () (redundant?), Remove () et GetEnumerator () .
  5. Ignore le principe DRY de configuration de vos comportements d'expiration / suppression par défaut afin que vous puissiez les oublier. Vous devez indiquer explicitement au cache comment vous souhaitez que l'élément que vous ajoutez expire ou soit supprimé chaque fois que vous ajoutez un élément.
  6. Aucun moyen d'accéder aux détails de mise en cache d'un élément mis en cache, tels que l'horodatage du moment où il a été ajouté. L'encapsulation a été un peu exagérée ici, rendant difficile l'utilisation du cache lorsque, dans le code, vous essayez de déterminer si un élément mis en cache doit être invalidé par rapport à un autre mécanisme de mise en cache (c'est-à-dire la collection de sessions) ou non.
  7. Les événements de suppression ne sont pas exposés en tant qu'événements et doivent être suivis au moment de l'ajout.
  8. Et si je ne l'ai pas assez dit, Microsoft le recommande explicitement en dehors du Web. Et si vous êtes maudit avec .NET 1.1, vous n'êtes pas censé l'utiliser avec une certaine stabilité en dehors du Web, alors ne vous inquiétez pas.

ObjectCache / MemoryCache de .NET 4.0

Microsoft a finalement implémenté une classe ObjectCache abstraite dans la dernière version du .NET Framework et une implémentation de MemoryCache qui hérite et implémente ObjectCache à des fins en mémoire dans un paramètre non Web.

System.Runtime.Caching.ObjectCache se trouve dans l'assembly System.Runtime.Caching.dll. Il s'agit d'une classe abstraite qui déclare essentiellement les mêmes interfaces de style .NET 1.0 que celles trouvées dans le cache ASP.NET. System.Runtime.Caching.MemoryCacheest l'implémentation en mémoire d'ObjectCache et est très similaire au cache ASP.NET, avec quelques modifications.

Pour ajouter un élément avec une expiration glissante, votre code ressemblerait à ceci:

var config = new NameValueCollection();  
var cache = new MemoryCache("myMemCache", config);  
cache.Add(new CacheItem("a", "b"),  
    new CacheItemPolicy  
    {  
        Priority = CacheItemPriority.NotRemovable,  
        SlidingExpiration=TimeSpan.FromMinutes(30)  
    }); 

Avantages:

  1. Il est intégré et désormais pris en charge et recommandé par Microsoft en dehors du Web.
  2. Contrairement au cache ASP.NET, vous pouvez instancier une instance d'objet MemoryCache.

    Remarque: il n'est pas nécessaire qu'il soit statique, mais cela devrait l'être, c'est la recommandation de Microsoft (voir Attention jaune) .

  3. Quelques légères améliorations ont été apportées par rapport à l'interface du cache ASP.NET, telles que la possibilité de s'abonner à des événements de suppression sans nécessairement être là lorsque les éléments ont été ajoutés, l'insertion redondante () a été supprimée, les éléments peuvent être ajoutés avec un CacheItem objet avec un initialiseur qui définit la stratégie de mise en cache, et Contains () a été ajouté.

Les inconvénients:

  1. Ne renforce toujours pas complètement DRY. D'après ma petite expérience, vous ne pouvez toujours pas définir le TimeSpan d'expiration glissante une fois et l'oublier. Et franchement, bien que la politique de l'exemple d'ajout d'éléments ci-dessus soit plus lisible, elle nécessite une verbosité horrible.
  2. Il n'est toujours pas générique; il nécessite une chaîne comme clé. Vous ne pouvez donc pas stocker aussi longtemps ou int si vous mettez en cache des enregistrements de données, à moins que vous ne les convertissiez en chaîne.

Bricolage: Construisez-vous vous-même

Il est en fait assez simple de créer un dictionnaire de mise en cache qui effectue une expiration explicite ou glissante. (Cela devient beaucoup plus difficile si vous voulez que les éléments soient supprimés automatiquement à des fins d'effacement de la mémoire.) Voici tout ce que vous avez à faire:

  1. Créez une classe de conteneur de valeur appelée quelque chose comme Expiring ou Expirable qui contiendrait une valeur de type T, une propriété TimeStamp de type DateTime à stocker lorsque la valeur a été ajoutée au cache, et un TimeSpan qui indiquerait à quelle distance de l'horodatage l'article doit expirer. Pour une expiration explicite, vous pouvez simplement exposer un setter de propriété qui définit le TimeSpan en fonction d'une date soustraite de l'horodatage.
  2. Créez une classe, appelons-la ExpirableItemsDictionary, qui implémente IDictionary. Je préfère en faire une classe générique avec définie par le consommateur.
  3. Dans la classe créée au n ° 2, ajoutez un Dictionary> en tant que propriété et appelez-le InnerDictionary.
  4. L'implémentation if IDictionary dans la classe créée en # 2 doit utiliser InnerDictionary pour stocker les éléments mis en cache. L'encapsulation masquerait les détails de la méthode de mise en cache via des instances du type créé au n ° 1 ci-dessus.
  5. Assurez-vous que l'indexeur (this []), ContainsKey (), etc. veillent à effacer les éléments expirés et à supprimer les éléments expirés avant de renvoyer une valeur. Renvoie null dans les getters si l'élément a été supprimé.
  6. Utilisez des verrous de thread sur tous les getters, setters, ContainsKey (), et en particulier lors de la suppression des éléments expirés.
  7. Déclenchez un événement chaque fois qu'un élément est supprimé en raison de son expiration.
  8. Ajoutez une instance System.Threading.Timer et installez-la lors de l'initialisation pour supprimer automatiquement les éléments expirés toutes les 15 secondes. Il s'agit du même comportement que le cache ASP.NET.
  9. Vous souhaiterez peut-être ajouter une routine AddOrUpdate () qui repousse l'expiration glissante en remplaçant l'horodatage sur le conteneur de l'élément (instance expirante) s'il existe déjà.

Microsoft doit prendre en charge ses conceptions originales car sa base d'utilisateurs a développé une dépendance à leur égard, mais cela ne signifie pas que ce sont de bonnes conceptions.

Avantages:

  1. Vous avez un contrôle total sur la mise en œuvre.
  2. Peut renforcer DRY en configurant des comportements de mise en cache par défaut, puis en supprimant simplement les paires clé / valeur sans déclarer les détails de mise en cache chaque fois que vous ajoutez un élément.
  3. Peut implémenter des interfaces modernes , à savoir IDictionary<K,T>. Cela le rend beaucoup plus facile à utiliser car son interface est plus prévisible en tant qu'interface de dictionnaire, et il le rend plus accessible aux assistants et aux méthodes d'extension qui fonctionnent avec IDictionary <>.
  4. Les détails de la mise en cache peuvent être non encapsulés , par exemple en exposant votre InnerDictionary via une propriété publique en lecture seule, ce qui vous permet d'écrire des tests unitaires explicites par rapport à votre stratégie de mise en cache et d'étendre votre implémentation de mise en cache de base avec des stratégies de mise en cache supplémentaires qui s'appuient sur elle.
  5. Bien que ce ne soit pas nécessairement une interface familière pour ceux qui se sont déjà familiarisés avec la syntaxe de style .NET 1.0 du cache ASP.NET ou du bloc d'application de mise en cache, vous pouvez définir l'interface pour qu'elle ressemble à ce que vous voulez.
  6. Peut utiliser n'importe quel type pour les clés. C'est l'une des raisons pour lesquelles les génériques ont été créés. Tout ne doit pas être saisi avec une chaîne.

Les inconvénients:

  1. N'est pas inventé ni approuvé par Microsoft , il n'aura donc pas la même assurance qualité.
  2. En supposant que seules les instructions que j'ai décrites ci-dessus sont implémentées, ne supprime pas «bon gré mal gré» les éléments pour effacer la mémoire de manière prioritaire (ce qui est de toute façon une fonction utilitaire du coin d'un cache. , La RAM est bon marché).

Parmi ces quatre options, c'est ma préférence. J'ai implémenté cette solution de mise en cache de base. Jusqu'à présent, cela semble fonctionner parfaitement, il n'y a pas de bogues connus (veuillez me contacter avec des commentaires ci-dessous ou à jon-at-jondavis s'il y en a !!), et j'ai l'intention de l'utiliser dans tous mes petits projets secondaires qui ont besoin mise en cache de base. C'est ici:

Lien Github: https://github.com/kroimon/ExpirableItemDictionary

Ancien lien: ExpirableItemDictionary.zip

Digne de mention: AppFabric, NoSQL, Et Al

Notez que le titre de cet article de blog indique «Simple Caching» et non «Heavy-Duty Caching». Si vous souhaitez vous lancer dans les activités lourdes, vous devriez envisager des solutions dédiées et évolutives.

DeepSpace101
la source
3

MemoryCache est ce qu'il dit, un cache stocké en mémoire

HttpRuntime.Cache (voir http://msdn.microsoft.com/en-us/library/system.web.httpruntime.cache(v=vs.100).aspx et http://msdn.microsoft.com/en- us / library / system.web.caching.cache.aspx ) persiste dans tout ce que vous configurez dans votre application.

voir par exemple «ASP.NET 4.0: Écriture de fournisseurs de cache de sortie personnalisés» http://weblogs.asp.net/gunnarpeipman/archive/2009/11/19/asp-net-4-0-writing-custom-output-cache -providers.aspx

Christian Westman
la source
1
Hm, je ne sais pas si le deuxième lien n'est pas trompeur, car il est question de OutputCache et d'implémentation de OutputCacheProvider.
Giedrius
Hm, je ne peux pas trouver où il dirait, que vous pouvez persister System.Web.Caching.Cache en utilisant une configuration différente
Giedrius
2

MemoryCache.Default peut également servir de "pont" si vous migrez une application ASP.NET MVC classique vers ASP.NET Core, car il n'y a pas de "System.Web.Caching" et "HttpRuntime" dans Core.

J'ai également écrit un petit benchmark pour stocker un boolélément 20000 fois (et un autre benchmark pour le récupérer) et MemoryCache semble être deux fois plus lent (27ms vs 13ms - c'est le total pour toutes les 20k itérations) mais ils sont tous les deux ultra-rapides et ceci peut probablement être ignoré.

Alex
la source