Voici l'article de Jon Davis. Pour préserver la lisibilité, j'ai supprimé la section EntLib, désormais obsolète, ainsi que l'introduction et la conclusion.
ASP.NET Cache
ASP.NET, ou l'assemblage 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 susmentionnés dans une sorte de table de hachage.
Après avoir parcouru Google, il apparaît que de nombreuses personnes qui ont discuté de la fonctionnalité de mise en cache intégrée dans .NET ont eu recours au 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 dispose d'un ObjectCache que j'aborderai 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 de nombreuses personnes sont encore bloquées dans .NET 2.0 et .NET 3.5, et ont besoin de quelque chose pour travailler, et il se trouve que cela fonctionne pour de nombreuses personnes, même si MSDN le dit clairement :
Remarque : la classe Cache n'est pas destinée à être utilisée en dehors des applications ASP.NET. Elle a été conçue et testée pour être utilisée dans ASP.NET afin d'assurer la mise en cache des 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 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 aquí .
Pour :
- C'est intégré .
- Malgré la syntaxe .NET 1.0, il est assez facile à utiliser. simple à utiliser.
- Lorsqu'il est utilisé dans un contexte web, c'est bien testé . En dehors des contextes web, selon les recherches Google, il n'est pas communément connu pour causer des problèmes, malgré les recommandations de Microsoft, tant que vous utilisez .NET 2.0 ou une version plus récente.
- Vous pouvez être notifié via un délégué lorsqu'un élément est supprimé, ce qui est nécessaire si vous devez le garder en vie et que vous n'avez pas pu définir la priorité de l'élément à l'avance.
- Les articles individuels ont la flexibilité de l'une des méthodes d'expiration et de radiation a), b) ou c) dans la liste des méthodes de radiation figurant en tête de cet article. Vous pouvez également associer le comportement d'expiration à la présence d'un fichier physique.
Cons :
- Non seulement il est statique, mais il n'y a qu'un seul . Vous ne pouvez pas créer votre propre type avec sa propre instance statique de cache. Vous ne pouvez avoir qu'un seul seau pour l'ensemble de votre application, un point c'est tout. 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 très gênant si, par exemple, vous avez un service qui a besoin de 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 les 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 suffira généralement pas.
- Les objets peuvent disparaître, bon gré mal gré . Beaucoup de gens l'ignorent, ce qui n'était pas mon cas jusqu'à ce que je rafraîchisse mes connaissances sur l'implémentation de ce cache. Par défaut, le cache ASP.NET est conçu pour détruire les éléments lorsqu'il en a "envie". Plus précisément, voir (c) dans ma définition d'une table de cache au début de cet article. Si un autre thread dans le même processus travaille sur quelque chose de complètement différent, et qu'il déverse des éléments de haute priorité dans le cache, alors dès que .NET décidera qu'il a besoin de mémoire, il commencera à détruire certains éléments du cache en fonction de leurs priorités, les priorités les plus faibles 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 empêche la suppression de l'élément à des fins de nettoyage de la mémoire, mais qui le supprimera tout de même conformément à la politique d'expiration. L'utilisation de CacheItemPriority.NotRemovable dans les invocations de cache peut s'avérer fastidieuse, c'est pourquoi il est nécessaire d'utiliser un wrapper.
- La clé doit être une chaîne de caractères. Si, par exemple, vous mettez en cache des enregistrements de données dont la clé est un long ou un entier, vous devez d'abord convertir la clé en chaîne de caractères.
- La syntaxe est périmée . Il s'agit de la syntaxe .NET 1.0, encore plus laide 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(), renvoyant null s'il n'y a pas de correspondance, plus Add(), Insert() (redondant ?), Remove(), et GetEnumerator().
- Ignore le principe DRY de mettre en place vos comportements d'expiration/suppression par défaut afin que vous puissiez les oublier. Vous devez explicitement indiquer au cache comment vous souhaitez que l'élément que vous ajoutez expire ou soit supprimé chaque fois que vous ajoutez un élément.
- Aucun moyen d'accéder aux détails de la mise en cache d'un élément mis en cache, par exemple l'heure à laquelle il a été ajouté. L'encapsulation a été un peu trop poussée ici, ce qui rend 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.-à-d. la collection de sessions) ou non.
- Événements liés à l'éloignement ne sont pas exposées en tant qu'événements et doivent être suivies au moment de l'ajout.
- Et si je ne l'ai pas assez dit, Microsoft le déconseille explicitement en dehors du web. Et si vous avez le malheur d'avoir NET 1.1, vous n'êtes pas censé l'utiliser en toute confiance. en dehors du web, alors ne vous en préoccupez pas.
ObjectCache / MemoryCache de .NET 4.0
Microsoft a finalement mis en œuvre une classe abstraite ObjectCache dans la dernière version de .NET Framework, ainsi qu'une implémentation MemoryCache qui hérite et implémente ObjectCache à des fins de mémoire dans un environnement autre que le 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 que l'on trouve dans le cache ASP.NET. System.Runtime.Caching.MemoryCache
est l'implémentation en mémoire d'ObjectCache et est très similaire au cache ASP.NET, avec quelques changements.
Pour ajouter un article avec une date d'expiration glissante, votre code devrait ressembler à 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)
});
Pour :
-
Il est intégré et désormais pris en charge et recommandé par Microsoft en dehors du web.
-
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 il devrait l'être. Recommandation de Microsoft (voir jaune Attention) .
-
Quelques légères améliorations ont été apportées par rapport à l'interface du cache ASP.NET, comme la possibilité de s'abonner à des événements de suppression sans nécessairement être présent lorsque les éléments ont été ajoutés, la fonction redondante Insert() a été supprimée, les éléments peuvent être ajoutés avec un objet CacheItem doté d'un initialisateur qui définit la stratégie de mise en cache, et la fonction Contains() a été ajoutée.
Cons :
- Il ne renforce pas encore complètement DRY. D'après ma petite expérience, il n'est toujours pas possible de définir l'intervalle de temps d'expiration une seule fois et de l'oublier. Et franchement, bien que la politique de l'exemple d'ajout d'élément ci-dessus soit plus lisible, elle nécessite une verbosité horrible.
- Il ne s'agit toujours pas d'une clé générique ; la clé doit être une chaîne de caractères. Vous ne pouvez donc pas stocker en tant que long ou int si vous mettez en cache des enregistrements de données, à moins que vous ne convertissiez en chaîne.
BRICOLAGE : Construisez-en un 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 automatiquement supprimés pour nettoyer la mémoire). Voici tout ce que vous avez à faire :
- Créez une classe de conteneur de valeurs appelée Expiring ou Expirable qui contiendrait une valeur de type T, une propriété TimeStamp de type DateTime pour stocker le moment où la valeur a été ajoutée au cache, et un TimeSpan qui indiquerait à quelle distance de l'horodatage l'élément devrait expirer. Pour une expiration explicite, il suffit d'exposer une propriété setter qui définit le TimeSpan en fonction d'une date soustraite de l'horodatage.
- Créez une classe, appelons-la ExpirableItemsDictionary, qui implémente IDictionary. Je préfère en faire une classe générique définie par le consommateur.
- Dans la classe créée au point 2, ajoutez un Dictionary> comme propriété et appelez-le InnerDictionary.
- L'implémentation de l'IDictionary dans la classe créée au point 2 doit utiliser l'InnerDictionary pour stocker les éléments mis en cache. L'encapsulation cacherait les détails de la méthode de mise en cache par le biais d'instances du type créé au point 1 ci-dessus.
- Assurez-vous que l'indexeur (this[]), ContainsKey(), etc., veille à éliminer les éléments périmés et à supprimer les éléments périmés avant de renvoyer une valeur. Retourner null dans les getters si l'élément a été supprimé.
- Utilisez des verrous de threads sur tous les getters, setters, ContainsKey(), et en particulier lors de l'effacement des éléments expirés.
- Lève un événement lorsqu'un article est retiré pour cause d'expiration.
- Ajoutez une instance de System.Threading.Timer et fixez-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.
- Vous pouvez ajouter une routine AddOrUpdate() qui repousse l'expiration glissante en remplaçant l'horodatage sur le conteneur de l'élément (instance Expiring) s'il existe déjà.
Microsoft doit soutenir ses modèles originaux parce que sa base d'utilisateurs en est devenue dépendante, mais cela ne signifie pas qu'il s'agit de bons modèles.
Pour :
- Vous avez contrôle complet sur la mise en œuvre.
- Peut renforcer le sec en mettant en place des comportements de mise en cache par défaut et en déposant simplement des paires clé/valeur sans déclarer les détails de la mise en cache à chaque fois que vous ajoutez un élément.
- Peut mettre en œuvre 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 le rend plus accessible aux aides et aux méthodes d'extension qui fonctionnent avec IDictionary<>.
- Les détails de la mise en cache peuvent être désencapsulés Vous pouvez ainsi écrire des tests unitaires explicites sur votre stratégie de mise en cache et étendre votre implémentation de base de la mise en cache avec d'autres stratégies de mise en cache qui s'appuient sur elle.
- Bien qu'il ne s'agisse pas nécessairement d'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, il est possible d'utiliser la fonction de mise en cache de l'ASP.NET. peut définir l'interface pour qu'il ressemble à ce que vous voulez qu'il soit.
- On peut utiliser n'importe quel type de clé. C'est l'une des raisons pour lesquelles les génériques ont été créés. Tout ne doit pas être associé à une chaîne de caractères.
Cons :
- n'est ni inventé ni approuvé par Microsoft Il ne bénéficiera donc pas de la même assurance qualité.
- En supposant que seules les instructions que j'ai décrites ci-dessus soient mises en œuvre, la mémoire cache n'efface pas "bon gré mal gré" les éléments à effacer en priorité (ce qui est de toute façon une fonction utilitaire d'un cache dans un cas particulier BUY RAM là où vous utiliseriez le cache, la RAM n'est pas chère).
Parmi ces quatre options, c'est celle que je préfère. J'ai mis en œuvre cette solution de base de mise en cache. Jusqu'à présent, elle semble fonctionner parfaitement, il n'y a pas de bugs connus (merci de me contacter en commentaire 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 d'une mise en cache de base. Le voici :
Lien Github : https://github.com/kroimon/ExpirableItemDictionary
Ancien lien : ExpirableItemDictionary.zip
Digne de mention : AppFabric, NoSQL et autres
Remarquez que le titre de cet article de blog indique "Simple Caching", et non "Heavy-Duty Caching". Si vous souhaitez vous lancer dans des tâches plus lourdes, vous devez vous tourner vers des solutions dédiées et évolutives.