I meccanismi di caching in ASP.NET 2.0

di Stefano Mostarda, in ASP.NET 2.0,

Con ASP.NET è stato introdotto in nativo il meccanismo di caching che, se opportunamente utilizzato, permette un notevole aumento delle prestazioni. Grazie a questa feature si possono mantenere dati, o anche intere pagine, direttamente nella memoria del server eliminando così gli accessi al disco o al database, più in generale ad una risorsa, aumentando così la velocità con cui le richieste vengono processate.

Nella versione 1.x del framework il caching è già in una fase molto matura del suo sviluppo, ma si può dire che con l'avvento della versione 2.0 abbia quasi raggiunto il suo stato dell'arte. Infatti sono state eliminate molte delle limitazioni, introducendo principalmente due feature: la possibilità di estendere la classe CacheDependency e la post-cache substitution. Inoltre grazie alla prima delle due novità menzionate è ora possibile impostare la dipendenza dei dati in cache dal database. A queste vanno aggiunte anche altre feature come la configurazione da web.config dell'OutputCaching.

Estendere la classe CacheDependency

Situazione

Una delle feature fondamentali del caching è la possibilità di invalidare un elemento al verificarsi di determinate condizioni: in questo caso si parla di dipendenza. Questa può essere impostata su un file, una data, un lasso di tempo o un altro oggetto in cache e questo copre gran parte delle nostre esigenze. Per creare una dipendenza si deve creare un'istanza della classe CacheDependency e poi passarla come argomento quando si utilizzano le API Cache.Add. e Cache.Insert per aggiungere l'oggetto in cache.

Problema

La classe CacheDependency non è ereditabile (sealed in C#, NotInheritable in VB.NET). Questo significa che non possiamo sfruttarla come base per creare nuovi tipi di dipendenze. Lo scenario più comune è la necessità di far spirare dei dati in cache in base ad una tabella o una query sul DB (problema risolto in modo nativo nella nuova versione). Ma ci sono ancora molte altre situazioni: per un CMS può essere utile creare una dipendenza in base ad un blog, per un'applicazione crearne una in base ad un Web Service, ecc. Attualmente tutto ciò non è possibile, per cui la soluzione migliore, nella maggior parte dei casi, è quella di mettere in cache gli oggetti con una durata molto breve.

Soluzione

Per risolvere questo problema, è stata aperta la classe CacheDependency così da poterla utilizzare come base per nuovi tipi di dipendenza. Il meccanismo su cui si basa la creazione di una nuova dipendenza è il polling. Viene definito un lasso di tempo, parametrizzabile dall'utente o cablato nella classe, allo scadere del quale viene effettuata una richiesta alla sorgente dati. Se la risposta risulta differente dalla precedente, viene invocato il metodo che invalida tutti gli elementi che dipendono dall'oggetto in cache.

I metodi da utilizzare quando si eredita da CacheDependency sono due:

  • DependencyDispose: da implementare per liberare le risorse eventualmente utilizzate nella classe (ad esempio, il timer per eseguire ciclicamente le richieste alla sorgente dati);

  • NotifyDependencyChanged: da invocare per far scattare l'invalidazione degli elementi che dipendono dall'oggetto.

Vediamo ora un esempio su come implementare una classe che genera una dipendenza da un metodo di un Web Service.

Innanzitutto generiamo il metodo del Web Service che funge da sorgente dati. Tale metodo non fa altro che restituire il minuto dell'ora corrente.

public int GetData()
{
  return DateTime.Now.Minute;
}

Successivamente generiamo il codice per la classe che genera la dipendenza. Innanzitutto vediamo che viene definito un oggetto Timer statico e allocato la prima volta che si crea un'istanza della classe. Nel costruttore del timer viene passato l'oggetto TimerCallBack che contiene le informazioni sul metodo da invocare per reperire i dati e sul periodo di polling. In seguito viene effettuata la prima chiamata al metodo che recupera i dati e il risultato viene memorizzato in una variabile. Il meccanismo di Singleton sul timer viene utilizzato per evitare il rischio di creare un nuovo thread ogni volta che viene allocato l'oggetto. Nel metodo invocato dal timer si recuperano i dati che vengono messi a confronto con quelli memorizzati. Nel caso in cui i dati siano diversi, viene invocato il metodo NotifyDependencyChanged. Infine nel metodo DependencyDispose viene liberata l'unica risorsa allocata dall'oggetto.

public class WSDependency : System.Web.Caching.CacheDependency
{
  static Timer timer;
  public int res;

  public WSDependency()
  {
    if (timer == null)
      timer = new Timer(new TimerCallback(CallBack), this, 20, 20000);
    res = GetData();
  }

  protected void CallBack(object sender)
  {
    int app = GetData();
    if (app != res)
    {
      ((WSDependency)sender).NotifyDependencyChanged(sender, EventArgs.Empty);
      res = app;
    }
  }

  public int GetData()
  {
    WebService ws = new WebService();
    return ws.GetData();
  }

  protected override void DependencyDispose()
  {
    timer.Dispose();
    base.DependencyDispose();
  }
}

Ovviamente la classe può essere migliorata inserendo le proprietà per settare l'indirizzo e il metodo del Web Service da invocare e il tempo di polling.

SqlDependency

Come accennato in precedenza, il problema della dipendenza da database è stato uno dei più sentiti dagli sviluppatori ASP.NET e, finalmente, è stato risolto nella nuova versione. Come accennato in precedenza, la SqlDependency è figlia dell'apertura della classe CacheDependency.

Il meccanismo si basa su due diverse tecnologie a seconda della versione di SQL Server che si usa.

  • La prima fa uso del polling dall'applicazione verso il database per verificare se qualcosa è stato modificato. Il vantaggio di questa tecnica è che, facendo uso di feature supportate da qualsiasi database, si può facilmente trasportare. Va utilizzata con SQL Server 7/2000.

  • La seconda fa uso dei Notification Services di SQL Server. L'applicazione, invece di pollare sul DB, aspetta che sia il DB a notificare quando cambia il risultato prodotto da una determinata query. Questo comporta un notevole vantaggio in termini di risorse, in quanto si eliminano tutte le query di polling, risparmiando così il carico di rete e quello applicativo e impattando al minimo sul DB. Va utilizzata con SQL Server 2005.

Vediamo ora come mettere in pratica quanto detto.

Invalidazione basata su polling

Configurazione del database

La nuova versione del framework .NET ci mette a disposizione una classe per configurare il database: SqlCacheDependencyAdmin. Possiamo utilizzarla in 2 modi: utilizzando l'eseguibile aspnet_regsql, che internamente ne sfrutta i metodi (il file è presente nella directory in cui è installato il framework), o creare una utility ad-hoc.

Qualunque sia la scelta, il meccanismo è il medesimo; prima si abilita il database e poi si abilita la tabella. Innanzitutto viene generata sul database la tabella AspNet_SqlCacheTablesForChangeNotification che contiene la lista di tutte le tabelle che sono state abilitate al controllo per l'invalidazione e vengono aggiunte le stored procedure necessarie a gestire questa tabella (aggiornamento della riga relativa ad una tabella abilitata quando questa viene modificata, interrogazione per verificare eventuali modifiche, ecc). Di seguito viene aggiunta una riga alla tabella per le notifiche, con il riferimento alla tabella abilitata, ed i trigger sulla tabella abilitata per invocare le stored procedure che aggiornano la tabella per le notifiche.

Se si utilizza l'eseguibile aspnet_regsql, i comandi da utilizzare sono i seguenti:

aspnet_regsql -E -d database /ed
aspnet_regsql -E -d database -t tabella -et

Il primo abilita il database, mentre il secondo abilita la tabella.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

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.

Approfondimenti