La versione Alpha di ASP.NET 2.0 aveva una magnifica Site Counter API: bastava abilitare la proprietà countclicks di un controllo "cliccabile" (Button, LinkButton, ImageButton, Phone Link, ImageMap) ed in uno store in Access (all'epoca predefinito) o SQL Server venivano immagazzinati: il nome dell'applicazione, l'URL della pagina che cliccava il controllo, identificativo di gruppo e nome del controllo, evento, URL di destinazione, data di inizio e fine della registrazione dei clic, numero di clic. Persino un controllo client-side come Hyperlink poteva essere tracciato: la Alpha generava una particolare coppia nome-valore sulla querystring, che permetteva di registrare i click effettuati. La reportistica doveva essere creata "a mano" utilizzando l'API, ma nel Web Site Administration Tool c'era già un segnaposto vuoto per configurare i report da generare, e si attendeva solo la Beta per poter tagliare dai budget i vari "site counters". Invece, la comunità dei beta tester si levò contro quella che veniva vista come una funzionalità non strettamente necessaria e che appesantiva inutilmente il framework di base (vedi questo post di ASPItalia), per cui non solo il sistema di reportistica non è mai stato implementato, ma la stessa API e le comode proprietà di tracking dei controlli sono scomparse con la Beta 1.
Per non tornare alle consuete variabili Session ed Application, abbiamo pensato di sfruttare il web.config. Volendo ad esempio contare quante volte viene invocato un sottosistema di reportistica, aggiungiamo un valore personalizzato:
<appSettings >
<addkey"ReportButtonClick" value="0"/>
</appSettings>
Ricordiamo che la sezione appSettings va subito dopo <configuration>. Se non volete dare a chiunque l'accesso al web.config, potete fargli aggiungere proprietà personalizzate tramite il Web Site Administration Tool (voce di menu Website/ASP.NET Configuration).
A questo punto, aggiungiamo il seguente codice, per esempio nell'handler dell'evento "clic sul pulsante che genera il report":
'inizia il blocco di conteggio degli accessi
counter = System.Configuration.ConfigurationManager.AppSettings.Get("ReportButtonClick")
counter = counter + 1
Me.lblClickCounter.Text = "Il sistema di reportistica è stato interrogato " & counter & " volte"
System.Configuration.ConfigurationManager.AppSettings.Set("ReportButtonClick", counter)
Va da sè che potete implementare un "usuale" contatore di accessi aggiungendo il codice di cui sopra nel Page_Load(), così come potete sfruttare la variabile contatore per qualcosa di diverso della visualizzazione in una label. Abbiamo naturalmente provato tutte le possibili cause di fault: concorrenza, riavvio dell'applicazione, e così via. Funziona tutto benissimo.
In effetti, il metodo Set di ConfigurationManager.AppSettings immagazzina il valore nella cache di ASP.NET, implementata come uno spazio di indirizzi nella RAM del Web server, per cui la soluzione proposta non solo è scalabile (all'aumentare della memoria del server, ovviamente) ma addirittura consente di persistere il valore del contatore a seguito di un riavvio di IIS. L'analisi con FileMon rivela che il web.config viene letto solo all'avvio dell'applicazione. L'unico, ovvio problema è quello che nella terminologia del caching di ASP.NET è noto come scavenging: quando la RAM disponibile diminuisce, ASP.NET rimuove automaticamente dalla cache in memoria i dati più vecchi. Per ovviare a questa situazione, il programmatore non può più delegare al framework la gestione della cache: è necessario gestirla direttamente da codice con i metodi dell'apposita classe System.Web.Caching.Cache, per la quale potete anche, naturalmente, consultare la MSDN. Più precisamente, aggiungendo un valore alla cache con i metodi Add o Insert, se ne può specificare la priorità specificando come parametro uno dei valori della enumeration CacheItemPriority; in particolare, il valore NotRemovable fa sì che un oggetto non venga rimosso dalla cache se non allo scadere del tempo di vita.
Potrete trovare maggiori informazioni sulla proprietà AppSettings della classe ConfigurationManager a questo indirizzo.