Ecco l'articolo di Jon Davis. Per preservare la leggibilità, sto tagliando la sezione EntLib ormai obsoleta, l'introduzione e la conclusione.
Cache ASP.NET
ASP.NET o l'assembly System.Web.dll dispone di un meccanismo di memorizzazione nella cache. Non è mai stato concepito per essere utilizzato al di fuori di un contesto Web, ma può essere utilizzato al di fuori del Web e esegue tutti i comportamenti di scadenza di cui sopra in una sorta di tabella hash.
Dopo aver setacciato Google, sembra che alcune persone che hanno discusso della funzionalità di memorizzazione nella cache incorporata in .NET abbiano fatto ricorso all'uso della cache ASP.NET nei loro progetti non Web. Questo non è più il sistema di memorizzazione nella cache integrato più disponibile e più supportato in .NET; .NET 4 ha un ObjectCache di cui parlerò più avanti. Microsoft è sempre stata irremovibile sul fatto che la cache ASP.NET non è destinata all'uso al di fuori del Web. Ma molte persone sono ancora bloccate in .NET 2.0 e .NET 3.5 e hanno bisogno di qualcosa con cui lavorare, e questo sembra funzionare per molte persone, anche se MSDN dice chiaramente:
Nota: la classe Cache non è destinata all'uso al di fuori delle applicazioni ASP.NET. È stato progettato e testato per l'uso in ASP.NET per fornire la memorizzazione nella cache per le applicazioni Web. In altri tipi di applicazioni, come le applicazioni console o le applicazioni Windows Form, la memorizzazione nella cache ASP.NET potrebbe non funzionare correttamente.
La classe per la cache ASP.NET è System.Web.Caching.Cache in System.Web.dll. Tuttavia, non puoi semplicemente eseguire il new-up di un oggetto Cache. È necessario acquisirlo da System.Web.HttpRuntime.Cache.
Cache cache = System.Web.HttpRuntime.Cache;
Lavorare con la cache ASP.NET è documentato su MSDN qui .
Professionisti:
- È integrato .
- Nonostante la sintassi .NET 1.0, è abbastanza semplice da usare.
- Se utilizzato in un contesto web, è ben testato . Al di fuori dei contesti web, secondo le ricerche di Google non è comunemente noto per causare problemi, nonostante Microsoft lo sconsigli, purché si utilizzi .NET 2.0 o versioni successive.
- È possibile ricevere una notifica tramite un delegato quando un elemento viene rimosso, operazione necessaria se è necessario mantenerlo attivo e non è possibile impostare la priorità dell'elemento in anticipo.
- I singoli articoli hanno la flessibilità di uno qualsiasi dei metodi di scadenza e rimozione (a), (b) o (c) nell'elenco dei metodi di rimozione all'inizio di questo articolo. È inoltre possibile associare il comportamento di scadenza alla presenza di un file fisico.
Contro:
- Non solo è statico, ce n'è solo uno . Non è possibile creare il proprio tipo con la propria istanza statica di una cache. Puoi avere un solo bucket per l'intera app, punto. Puoi avvolgere il bucket con i tuoi wrapper che fanno cose come pre-iniettare prefissi nelle chiavi e rimuovere questi prefissi quando estrai le coppie chiave / valore. Ma c'è ancora un solo secchio. Tutto è raggruppato insieme. Questo può essere un vero fastidio se, ad esempio, si dispone di un servizio che necessita di memorizzare nella cache tre o quattro diversi tipi di dati separatamente. Questo non dovrebbe essere un grosso problema per progetti pateticamente semplici. Ma se un progetto ha un grado significativo di complessità a causa dei suoi requisiti, la cache ASP.NET in genere non sarà sufficiente.
- Gli oggetti possono scomparire, volenti o nolenti. Molte persone non ne sono consapevoli, non lo ero finché non ho aggiornato le mie conoscenze sull'implementazione della cache. Per impostazione predefinita, la cache ASP.NET è progettata per distruggere gli elementi quando "si sente". Più specificamente, vedere (c) nella mia definizione di tabella della cache all'inizio di questo articolo. Se un altro thread nello stesso processo sta lavorando su qualcosa di completamente diverso e scarica gli elementi ad alta priorità nella cache, non appena .NET decide che ha bisogno di richiedere un po 'di memoria inizierà a distruggere alcuni elementi nella cache secondo le loro priorità, prima quelle più basse. Tutti gli esempi documentati qui per l'aggiunta di elementi della cache utilizzano la priorità predefinita, piuttosto che il valore di priorità NotRemovable che impedisce che venga rimosso per scopi di svuotamento della memoria ma lo rimuoverà comunque in base alla politica di scadenza.
- La chiave deve essere una stringa. Se, ad esempio, si stanno memorizzando nella cache record di dati in cui i record sono codificati su un valore lungo o intero, è necessario prima convertire la chiave in una stringa.
- La sintassi è obsoleta . È la sintassi .NET 1.0, ancora più brutta di ArrayList o Hashtable. Non ci sono generici qui, nessuna interfaccia IDictionary <>. Non ha il metodo Contains (), nessuna raccolta di chiavi, nessun evento standard; ha solo un metodo Get () più un indicizzatore che fa la stessa cosa di Get (), restituendo null se non c'è corrispondenza, più Add (), Insert () (ridondante?), Remove () e GetEnumerator () .
- Ignora il principio DRY di impostare i comportamenti di scadenza / rimozione predefiniti in modo da poterli dimenticare. Devi indicare esplicitamente alla cache come desideri che l'elemento che stai aggiungendo scada o venga rimosso ogni volta che aggiungi un elemento.
- Non è possibile accedere ai dettagli di memorizzazione nella cache di un elemento memorizzato nella cache, ad esempio il timestamp di quando è stato aggiunto. L'incapsulamento è andato un po 'fuori bordo qui, rendendo difficile utilizzare la cache quando nel codice si sta tentando di determinare se un elemento memorizzato nella cache deve essere invalidato rispetto a un altro meccanismo di memorizzazione nella cache (ad esempio raccolta di sessioni) o meno.
- Gli eventi di rimozione non vengono esposti come eventi e devono essere monitorati al momento dell'aggiunta.
- E se non l'ho detto abbastanza, Microsoft lo sconsiglia esplicitamente al di fuori del web. E se sei maledetto con .NET 1.1, non dovresti usarlo con alcuna sicurezza di stabilità al di fuori del web, quindi non preoccuparti.
ObjectCache / MemoryCache di .NET 4.0
Microsoft ha finalmente implementato una classe ObjectCache astratta nell'ultima versione di .NET Framework e un'implementazione MemoryCache che eredita e implementa ObjectCache per scopi in memoria in un'impostazione non Web.
System.Runtime.Caching.ObjectCache si trova nell'assembly System.Runtime.Caching.dll. È una classe astratta che dichiara sostanzialmente le stesse interfacce in stile .NET 1.0 che si trovano nella cache ASP.NET.System.Runtime.Caching.MemoryCache
è l'implementazione in memoria di ObjectCache ed è molto simile alla cache ASP.NET, con alcune modifiche.
Per aggiungere un elemento con una scadenza scorrevole, il tuo codice sarebbe simile a questo:
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)
});
Professionisti:
- È integrato e ora supportato e consigliato da Microsoft al di fuori del Web.
A differenza della cache ASP.NET, è possibile creare un'istanza di un oggetto MemoryCache.
Nota: non deve essere statico, ma dovrebbe esserlo: questa è la raccomandazione di Microsoft (vedere Attenzione gialla) .
Sono stati apportati alcuni piccoli miglioramenti rispetto all'interfaccia della cache ASP.NET, come la possibilità di iscriversi agli eventi di rimozione senza essere necessariamente presenti quando gli elementi sono stati aggiunti, il ridondante Insert () è stato rimosso, gli elementi possono essere aggiunti con un CacheItem oggetto con un inizializzatore che definisce la strategia di memorizzazione nella cache e Contains () è stato aggiunto.
Contro:
- Ancora non rinforza completamente DRY. Dalla mia piccola esperienza, non è ancora possibile impostare una volta la scadenza scorrevole TimeSpan e dimenticarsene. E francamente, sebbene la politica nell'esempio di aggiunta di elementi sopra sia più leggibile, richiede un'orribile verbosità.
- Non è ancora codificato genericamente; richiede una stringa come chiave. Quindi non puoi memorizzare as long o int se stai memorizzando nella cache i record di dati, a meno che non converti in stringa.
Fai da te: creane uno da solo
In realtà è piuttosto semplice creare un dizionario di memorizzazione nella cache che esegua una scadenza esplicita o scorrevole. (Diventa molto più difficile se vuoi che gli elementi vengano rimossi automaticamente per scopi di cancellazione della memoria.) Ecco tutto ciò che devi fare:
- Creare una classe contenitore di valori chiamata qualcosa come Expiring o Expirable che conterrebbe un valore di tipo T, una proprietà TimeStamp di tipo DateTime da memorizzare quando il valore è stato aggiunto alla cache e un TimeSpan che indicherebbe quanto lontano dal timestamp che l'articolo dovrebbe scadere. Per una scadenza esplicita puoi semplicemente esporre un setter di proprietà che imposta il TimeSpan data una data sottratta dal timestamp.
- Crea una classe, chiamiamola ExpirableItemsDictionary, che implementa IDictionary. Preferisco renderlo una classe generica con definita dal consumatore.
- Nella classe creata in # 2, aggiungi un Dictionary> come proprietà e chiamalo InnerDictionary.
- L'implementazione se IDictionary nella classe creata in # 2 dovrebbe utilizzare InnerDictionary per archiviare gli elementi memorizzati nella cache. L'incapsulamento nasconderebbe i dettagli del metodo di memorizzazione nella cache tramite istanze del tipo creato in # 1 sopra.
- Assicurati che l'indicizzatore (this []), ContainsKey (), ecc., Stia attento a cancellare gli elementi scaduti e rimuovere gli elementi scaduti prima di restituire un valore. Restituisce null in getter se l'elemento è stato rimosso.
- Utilizzare i thread lock su tutti i getter, setter, ContainsKey () e in particolare quando si cancellano gli elementi scaduti.
- Genera un evento ogni volta che un elemento viene rimosso a causa della scadenza.
- Aggiungi un'istanza System.Threading.Timer e modificala durante l'inizializzazione per rimuovere automaticamente gli elementi scaduti ogni 15 secondi. Questo è lo stesso comportamento della cache ASP.NET.
- Potresti voler aggiungere una routine AddOrUpdate () che spinge fuori la scadenza scorrevole sostituendo il timestamp sul contenitore dell'elemento (istanza in scadenza) se esiste già.
Microsoft deve supportare i suoi progetti originali perché la sua base di utenti ha costruito una dipendenza da essi, ma ciò non significa che siano buoni progetti.
Professionisti:
- Hai il controllo completo sull'implementazione.
- Può rinforzare ASCIUTTO impostando comportamenti di memorizzazione nella cache predefiniti e quindi semplicemente inserendo le coppie chiave / valore senza dichiarare i dettagli di memorizzazione nella cache ogni volta che aggiungi un elemento.
- Può implementare interfacce moderne , vale a dire
IDictionary<K,T>
. Ciò lo rende molto più facile da utilizzare poiché la sua interfaccia è più prevedibile come interfaccia di dizionario, inoltre la rende più accessibile agli helper e ai metodi di estensione che funzionano con IDictionary <>.
- I dettagli della memorizzazione nella cache possono essere non incapsulati , ad esempio esponendo il tuo InnerDictionary tramite una proprietà di sola lettura pubblica, consentendoti di scrivere test unitari espliciti sulla tua strategia di memorizzazione nella cache, nonché estendere l'implementazione della memorizzazione nella cache di base con strategie di memorizzazione nella cache aggiuntive che si basano su di esso.
- Sebbene non sia necessariamente un'interfaccia familiare per coloro che si sono già sentiti a proprio agio con la sintassi in stile .NET 1.0 della cache ASP.NET o del Caching Application Block, è possibile definire l'interfaccia in modo che appaia come si desidera.
- Può usare qualsiasi tipo per le chiavi. Questo è uno dei motivi per cui sono stati creati i generici. Non tutto dovrebbe essere codificato con una stringa.
Contro:
- Non è stato inventato da, né approvato da, Microsoft , quindi non avrà la stessa garanzia di qualità.
- Supponendo che siano implementate solo le istruzioni che ho descritto sopra, non cancella "volenti o nolenti" gli elementi per svuotare la memoria su base prioritaria (che è comunque una funzione di utilità di una cache all'angolo .. ACQUISTA RAM dove useresti la cache , La RAM costa poco).
Tra tutte e quattro queste opzioni, questa è la mia preferenza. Ho implementato questa soluzione di caching di base. Finora, sembra funzionare perfettamente, non ci sono bug noti (contattatemi con i commenti qui sotto oa jon-at-jondavis se ce ne sono !!), e ho intenzione di usarlo in tutti i miei progetti secondari più piccoli che richiedono memorizzazione nella cache di base. Ecco qui:
Collegamento Github: https://github.com/kroimon/ExpirableItemDictionary
Vecchio collegamento: ExpirableItemDictionary.zip
Degno di menzione: AppFabric, NoSQL, Et Al
Si noti che il titolo di questo articolo del blog indica "Simple Caching", non "Heavy-Duty Caching". Se vuoi entrare nelle cose pesanti, dovresti guardare soluzioni dedicate e scalabili.