Inside ModelVirtualCasting #3: la cache

di Daniele Bochicchio, in ASP.NET,

Uno dei punti in cui io e MarcoDes (che abbiamo curato maggiormente la parte web) abbiamo investito gli ultimi scampoli di giorni prima dell'evento è per finalizzare la parte di gestione della cache. Prima di continuare, però:

  1. Introduzione ai repository
  2. Architettura interna dei repository

Tradizionalmente, le applicazioni web basate su ASP.NET soffrono del fatto che la cache è stata pensata per essere solo in process ed esclusivamente per ASP.NET. Fino ad ASP.NET 3.5, infatti, si trova in System.Web.Caching. Non c'è niente di male ad utilizzare una reference a System.Web, finchè, ovviamente, non decidete di fare un'applicazione non-web e sfruttare il Client Profile del .NET Framework.

Inoltre, se la vostra applicazione ha un forte carico o, semplicemente, vi interessano scenari apocalittici, come quelli in cui una web app ed un windows service lavorano sugli stessi oggetti in cache, in ASP.NET 3.5 non c'è niente di pronto e vi tocca costruirvi tutto quello che vi serve per gestire la cache out-of-process.

.NET Framework 4.0, per questo motivo, introduce un nuovo assembly (System.Runtime.Caching), che si può referenziare in tutti i tipi di applicazioni. Trovate approfondimeti in questo script.

ASP.NET 4.0, essendo parte del framework, ha quindi la possibilità di sfruttare questo nuovo meccanismo a provider, dove ObjectCache fa da base per le possibili implementazioni custom. Avere una classe base, da cui poter ereditare e su cui implementare le proprie personalizzazioni è molto comodo, perchè con le versioni precedenti era necessario farsi il sistema completo (interfaccia base per i provider, IoC container, cache factory) e perdere un bel po' di tempo. In più, il nuovo modello della cache supporta una granularità migliore (ad esempio, si vedano i ChangeMonitor e più in generale la CacheItemPolicy). Purtroppo ad ASP.NET 4 manca l'ultimo miglio, cioè un vero provider model implementato al 100%, per capirci come quello di Membership API, dove avete tutti i pezzi già pronti e la configurazione guida il provider utilizzato.

In realtà, se da un punto di vista pratico questa è una limitazione (vi obbliga ad inventarvi la vostra strada), dall'altro lascia aperte possibilità molto interessanti, nell'ambito dell'estrema personalizzazione che consente.

Tutto ciò premesso, l'idea di aggiungere la cache a ModelVC, la nostra applicazione "cavia", è partita da 3 presupposti fondamentali:

  • cache a provider, con IoC: deve bastare cambiare la configurazione perchè cambi il provider utilizzato;
  • poco intrusiva: devo poter decidere al volo se un dato elemento va in cache, oppure no;
  • intuitiva, facile da usare e configurabile: deve essere aggiunta ai repository (di cui Marco ha già parlato) in maniera rapida.

L'idea iniziale per avere i punti 2 e 3 è stata quella di sfruttare un attributo, per decorare un metodo del repository. Ad esempio, così:

namespace ASPItalia.ModelVirtualCasting.Common 
{  
  public interface INewsRepository : IRepository<INewsRepository, News> 
  {  
    [CacheThis("last-news", 60)]  
    List<NewsDTO> GetLastNewsDTO(int number);  
  } 
}

Attraverso l'attributo CacheThis, in automatico, iniettiamo (con AOP) la cache sul metodo che restituisce la lista delle ultime news. L'attributo (guardate pure il codice) è pensato per variare la chiave che associata alla cache in base al valore dei parametri che vengono passati, rendendo trasparente l'uso in un'applicazione reale, dove magari in home page tirate fuori 10 elementi, ma nella pagina dedicata la visualizzate semplicemente tutti.

A questo punto, il grosso del lavoro è svolto da Unity, che si occupa di caricare il tipo effettivamente mappato nel web.config.

Per quanto riguarda i provider, per poter gestire il fatto che MemoryCache, cioè il provider di default che lavora in memoria, non ha un costruttore ma una proprietà Default attraverso il quale si accede alla sua istanza, abbiamo implementato una semplice interfaccia, con un metodo GetInstance(), che restituisce un tipo ObjectCache.

Per questo motivo, il provider con cache in memoria è molto semplificato:

namespace ASPITALIA.ModelVirtualCasting.Cache.Providers.InMemory 
{ 
  public class MemoryCacheBuilder : ICacheBuilder  
  {  
    public MemoryCacheBuilder() { } 

    public ObjectCache GetInstance()  
    {  
      return MemoryCache.Default;  
    } 
   } 
}

Al contrario, i due provider per la cache out-of-process sono stati implmentati derivando da ObjectCache, oltre che implementando l'interfaccia ICacheBuilder. Ne trovate uno per Scaleout (un prodotto commerciale) e per Windows Server AppFabric Caching (gratuito, in RC, conosciuto come Velocity). Non c'è niente di interessante in questi due, nel senso che sono semplicemente un mapping verso le rispettive API di quelle previste da ObjectCache. E' interessante, però, dare un'occhiata a come i provider, attraverso la proprietà DefaultCacheCapabilities, si descrivano, dicendo quali feature supportano.

Chiudo con una piccola nota: per supportare scenari del genere, con cache out-of-process, le entità devono essere serializzabili. Dato che abbiamo utilizzato dei proxy per Entity Framework, abbiamo preferito affidarci ad un DTO. Da un punto di vista delle performance, poi, questa è comunque la scelta migliore, in quanto consente di tenere in cache solo i dati effettivamente utilizzati in quel particolare caso d'uso.

Nel prossimo futuro contiamo di aggiungere il supporto all'invalidazione della cache in fase di modifica, per supportare scenari come l'aggiornamento o l'inserimento di un nuovo elemento, che attualmente richiedono che venga scritto del codice ad hoc.

Direi che per quanto riguarda la cache, è tutto. Come detto, ModelVC la trovate qui.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese