in

DotNetMarche

.NET Framework User Group delle Marche

Articoli

Alcune considerazioni sul cache di pagina

Di: Ricci Gian Maria

Un adeguato caching permette di aumentare le prestazioni di un sito Asp.NET in maniera considerevole, in particolare la cache di pagina è la tecnica che dà i maggiori risultati, ma se non la si conosce a fondo si potrebbe rimanere sorpresi da alcuni comportamenti.

Cache di pagina

La cache di pagina viene impostata con la direttiva di pagina OutputCache ed è particolarmente efficiente, perché permette al motore Asp.NET di riutilizzare l’html generato da una richiesta precedente senza eseguire nemmeno una riga di codice. Questa tecnica non è però priva di effetti collaterali, che debbono essere accuratamente esaminati per capire se effettivamente si può applicare alle proprie pagine e/o controlli. Si prenda ad esempio la pagina page1.aspx contenuta nel codice accluso che contiene una semplice gridView collegata alla tabella customer del database northwind. L’unica particolarità è la presenza di una colonna template che contiene un bottone il cui scopo è cancellare un cliente dal database.

Nell’esempio accluso la routine non effettua veramente la cancellazione ma indica semplicemente in una label l’id cliente che sarebbe stato cancellato, in questo modo non si alterano i dati nel database e l’esempio non perde di validità.

La pagina ha quindi un funzionamento molto semplice, vediamo ora cosa succede se si abilita la cache.

Abilitare la cache di pagina

Sempre nell’esempio accluso, la pagina page2.aspx contiene lo stesso codice visto in precedenza, con la semplice aggiunta della direttiva di abilitazione della cache

Se si esegue la pagina si nota però che alla prima pressione del bottone il comportamento è quello corretto e viene indicata la richiesta di cancellazione, ma alle pressioni successive non accade nulla e se si mette un breakpoint in modalità debug si può verificare che la routine GridView1_RowCommand viene chiamata solamente la prima volta.

Chi conosce bene la cache di pagina non rimarrà sorpreso da questo comportamento, ma analizziamo comunque in dettaglio cosa succede per avere un quadro della situazione.

Alla prima richiesta il server non ha la pagina in cache, viene quindi generata la pagina in modo normale e l’html risultante viene salvato nella cache. Alla prima pressione di un bottone la richiesta arriva al server, anche questa volta viene controllata la cache di pagina e nuovamente si ha un cache miss perché questa volta la richiesta è in POST mentre precedentemente era in get. Alle successive pressioni del tasto la pagina viene trovata in cache, viene quindi restituito al chiamante l’html contenuto nella cache e nessun evento viene generato perché non viene nemmeno creata un’istanza della classe che gestisce la pagina

Questo tipo di cache può quindi aumentare di molto le prestazioni di un’applicazione Asp.NET perché evita completamente l’esecuzione di codice. Si consideri inoltre che la cache è abilitabile anche per i singoli controlli utente e quindi è possibile effettuare cache parziale del contenuto di una pagina.

Purtroppo nel caso in cui si richieda un comportamento dinamico, come nell’esempio appena mostrato, questa tecnica non è apparentemente utilizzabile perché purtroppo elimina la chiamata dei metodi lato server che eseguono logica di business.

Forzare l’esecuzione della pagina indipendentemente dalla cache

Supponiamo che la pagina precedente venga utilizzata per il 99.99% delle volte in modalità sola lettura e solo in rarissimi casi venga richiesta una cancellazione. In questo scenario il cache di pagina è legittimo, bisogna solamente trovare il modo di forzare un cache miss alla pressione di un bottone. Naturalmente per pagine fortemente interattive questa tecnica non è assolutamente efficiente, perché si genererebbero troppe versioni nella cache e sarebbe quindi consigliabile adottare altre strategie di caching.

La direttiva OutputCache permette di indicare come viene indicizzata la cache tramite vari attributi come varyByParam, varyByControl etc, ed è proprio su questi che si deve agire per forzare un cache miss in maniera programmatica. In particolare il varyByParam permette di specificare come chiavi di indicizzazione nomi di parametri in GET o in POST. Supponiamo quindi di aggiungere la linea

Alla direttiva OutputCache. In questo caso se la pagina viene chiamata con page2.aspx?param=1 il motore di asp.net indicizzerà l’output nella cache con il valore param=1. Se una successiva richiesta viene fatta ad esempio con page2.aspx?param=2 il risultato è un cache miss e l’output viene inserito a fianco del precedente, indicizzato con il valore param=2. In questo scenario è importante capire che per ogni differente combinazione dei parametri indicati viene mantenuta nel server una versione della pagina in cache.

Il nostro problema è comunque differente, quello che si desidera è abilitare la cache, ma far si che alla pressione di determinati bottoni si abbia sempre un cache miss, perché siamo interessati all’esecuzione del codice lato server associato al bottone stesso. Almeno apparentemente non esiste però una direttiva varyByXxx che permetta di indicare ad asp.NET uno o più bottoni che invalidano la cache, ma il problema può essere aggirato in maniera molto semplice.

La soluzione è contenuta nella page3.aspx, se la si manda in esecuzione si può vedere come sia stata aggiunta una label che riporta l’ora di generazione della pagina, necessaria per verificare che effettivamente le pagine siano nuovamente rigenerate e non prese dalla cache. Aprendo un secondo browser e navigando allo stesso indirizzo si può notare come l’ora restituita sia quella del primo caricamento, questo banale esperimento ci mostra che la cache è effettivamente funzionante. La differenza è che questa volta i bottoni funzionano correttamente ed il codice dell’handler viene eseguito ad ogni click.

Il trucco è veramente banale, prima di tutto è stato inserito un HiddenField di nome forcePostBack il cui valore iniziale è un guid, e poi la cache è stata impostata in modo da variare sulla base del contenuto di questo controllo. Se si vuole che alla pressione di un bottone sia sempre eseguito il codice del corrispettivo handler è sufficiente cambiare il contenuto dell’hiddenField con un altro valore prima di effettuare il postback, assicurandosi chiaramente che questo valore sia univoco. Per ottenere un nuovo guid ad ogni pressione di un bottone è indubbiamente vantaggioso generare direttamente lato server il codice javascript associato all’evento OnClientClick di ogni bottone. Ecco ad esempio come vengono creati i bottoni della gridView.

Nello snippet è stato evidenziato come l’unica aggiunta sia l’attributo OnClientClick il cui contenuto è impostato da una funzione lato server chiamata ForcePostbackJs().

Come si può vedere ad ogni evento load la pagina genera un nuovo guid e alla pressione del bottone si associa un javascript che imposta il contenuto dell’hiddenfield al nuovo guid generato. In questo modo si ha la sicurezza che ad ogni pressione di un bottone il nuovo valore dell’hidden field sia univoco e generi sempre un cache miss.

Il quadro non è però ancora completo, se si inserisce la vera logica di cancellazione, dopo la pressione del bottone alle successive richieste della pagina verrebbe comunque restituita la versione contenuta nella in cache, in cui è ancora presente l’elemento cancellato. In generale infatti se la pressione di un bottone provoca l’esecuzione di logica di business che modifica i dati sulla base dei quali è generata la pagina, tutte le precedenti versioni in cache non sono più valide. La soluzione è quindi invalidare la cache di pagina in maniera programmatica, inserendo l’istruzione seguente nell’handler del bottone

In questo modo ogni volta che viene eseguita logica di business tutta la cache della pagina Page3.aspx viene invalidata e le successive richieste visualizzeranno dati corretti.

Conclusioni

La cache di pagina è una tecnica che permette di ottenere grandi incrementi di prestazioni per pagine statiche o che contengono piccole porzioni dinamiche, purtroppo è necessario ricorrere a piccoli “trucchi” se si vuole che la logica lato server risponda sempre agli eventi utente. In generale comunque la possibilità di abilitare questa forma di caching anche per i singoli controlli la rende indubbiamente una tecnica di grande efficacia per le proprie applicazioni Asp.NET.


Published Nov 14 2006, 08:24 AM by Alkampfer
Filed under:
Attachment: Example.zip

Comments

No Comments

About Alkampfer

Chi sono

Mi chiamo Gian Maria Ricci, sono nato il 18/08/1974 e il fatto che mi troviate qui in dotnetmarche significa che per passione e per lavoro mi occupo di .NET.

Sono laureato in Ingegneria Elettronica con specializzazione in Microelettronica, ma la mia passione è sempre stata la programmazione, che coltivo da quando mi fu regalato il mitico Vic20 un paio di decadi fa. Passato per il Basic, alle superiori ho programmato in Pascal e Prolog con qualche deviazione in Fortran e Cobol, fino ad approdare al C/C++. Per lavoro ho poi conosciuto il VB6 ed ho iniziato ad apprezzare il .NET fin dalle beta, tanto da utilizzare .NET come ambiente di sviluppo per la mia tesi.

Oltre che di dotnetmarche faccio parte del gruppo nablasoft, formato da miei ex colleghi di università con cui ho condiviso e condivido ancora la passione per la computer graphics. Tra i lavori prodotti per nablasoft, sicuramente quello di cui vado più orgoglioso è un tutorial in lingua inglese sulla programmazione della Playstation2 tramite il linux kit che ancora oggi ci da una visibilità al primo posto in alcune ricerche di google. Nel sito trovate inoltre altri lavori miei e dei miei colleghi che spaziano dalla matematica, all’elettronica e naturalmente anche al .NET.

Tra le mie altre partecipazioni a gruppi web posso citare GPI (game programming italia) per il quale ricopro il ruolo di responsabile della sezione playstation2, anche se la mia attività è sicuramente stata molto breve ed è molto che non contribuisco più in maniera attiva.

Le mie pubblicazioni

Nel corso degli anni oltre a pubblicare materiale nel sito nablasoft, ho collaborato con altre realtà come www.programmazione.it, il gruppo playstation2linux ed il gruppo infomedia di cui sono articolista principalmente per la rivista dev.

  • "Funzionali ed adattatori nella STL"- Computer Programming (aprile 2004).
  • "Esportare il contenuto di una DataGrid in Microsoft Excel" –Computer Programming (maggio 2004)
  • "Garbage Collection in ambiente .NET" – Computer Programming (luglio 2004)
  • "Serializzazione e persistenza in database" –DEV (Luglio 2004)
  • "Programmare la PS2 tramite il Linux kit. Introduzione" - DEV (settembre 2004)
  • "Utilizzare la libreria zlib in C# o VB.NET" - DEV (agosto 2004)
  • "Programmare la PS2 tramite il Linux kit. Renderizzare un cubo" – DEV (ottobre 2004)
  • "Utilizzo di puntatori per elaborare immagini in C#" - DEV (novembre 2004)
  • "Programmare la PS2 tramite il Linux kit. Utilizzare il joypad" - DEV (novembre 2004)
  • "Personalizzare l'aspetto dei menu con GDI+" - DEV (dicembre 2004)
  • "Utilizzare MSDE in applicazioni desktop. Installazione e gestione" – DEV (dicembre 2004)
  • "Utilizzare MSDE in applicazioni desktop. Deployment" – DEV (gennaio 2005)
  • "PC o console, tecniche di sviluppo a confronto" – DEV (gennaio 2004)
  • "CLR Profiler per .NET - Stringhe e finalizzazione" – Computer Programming (gennaio 2005)
  • "Utilizzare MSDE in applicazioni desktop. Distribuire la struttura di database" – DEV (febbraio 2005)
  • "Comunicare in rete tramite C#" – DEV (febbraio 2004)
  • "CLR Profiler per .NET – Pool di oggetti" – Computer Programming (febbraio 2005)
  • "Amministrare le politiche in Active Directory" – Login (marzo/aprile 2005)
  • "Spedire mail in C#" – DEV (aprile 2005)
  • "Utilizzare API di shell in C#" – DEV (maggio 2005)
  • ".NET e remoting" – DEV (Maggio 2005)
  • "Utilizzare Dll C++ in Visual Basic 6" – DEV (Maggio 2005)
  • "Sql Server 2005 - Scrivere un aggregatore custom in Visual Basic" – DEV (Giugno 2005)
  • "Utilizzare le API di shell in C# - 2a parte" – DEV (Giugno 2005)
  • "Utilizzare le API di shell in C# - 3a parte" – DEV (Settembre 2005)
  • "Trasferire una sessione da ASP ad ASP.NET " – DEV (Settembre 2005)
  • "Unit testing in .NET 2.0" – DEV (Ottobre 2005)
  • "Visual Studio 2005 Class diagram" – DEV (Ottobre 2005)
  • "Nullable Types in .NET 2.0", DEV (Novembre 2005)
  • "Creare Una trial in C#", DEV (Dicembre 2005)
  • "Linguaggi ed evoluzione di sintassi", DEV (Gennaio 2006)
  • "C# 2.0 Evoluzioni sintattiche”, DEV (Gennaio 2006)
  • "Office Web Components - Realizzare un report in Excel", DEV (Gennaio 2006)
  • "VB.NET e Sql Server 2005 - Scrivere stored procedures in VB.NET", DEV (Febbraio 2006)
  • "Persistence Frameworks in C#”, DEV (Maggio 2006)
  • "Persistenza con serializzazione XML in .NET 2.0", DEV (Maggio 2006)
  • ".NET 2.0 – Enterprise Library 2.0, logging", DEV (Maggio 2006)
  • "Database - XML e SQL Server 2005", DEV(Giugno 2006)
  • "Enterprise Library 2.0 Logging - Seconda parte", DEV (Giugno 2006)
  • "ASP.NET 2.0 e AJAX", DE (Luglio 2006)

  • PS2 3D programming Tutorial - Tutorial sulla programmazione 3D per la console Playstation 2 Pubblicato nel sito ufficiale Sony per il kit PS2 linux.

<script src="http://feeds.feedburner.com/AlkampferEng?format=sigpro" type="text/javascript" ></script><noscript>

Subscribe to RSS headline updates from:
Powered by FeedBurner

</noscript>
Powered by Community Server (Commercial Edition), by Telligent Systems