Finalmente stasera sono riuscito a trovare il tempo di dotare il mio di un feed rss. Lo potete sottoscrivere su feedburner all'indirizzo http://feeds.feedburner.com/imagic. Consentitemi di ringraziare l'autore dell' RSS Toolkit che mi ha graziato permettendomi di ottenere il risultato voluto in meno di 20 minuti...

Se a qualcuno interessa lo può trovare qui: http://www.asp.net/downloads/sandbox/

Technorati tags: , ,

Qualche tempo fa avevo accennato alle problematiche che derivano dall'uso del SqlProfileProvider. Avevo anche introdotto una possibile soluzione che consentiva di indicizzare i contenuti in una tabella appositamente creata. Oggi ho scoperto sul sito www.asp.net un provider creato dal tema di asp.net che consente di superare questo problema salvando i dati di profilazione in un campo a ciascuno.

Link:


Sono passati un po' di giorni da quando ho presentato la mia candidatura, ma da circa una settimana ho ricevuto la notizia che mi è data l'opportunità di aprire un nuovo weblog in lingua inglese. Il blog troverà spazio in una delle più grandi comunità di blogger su ASP.NET a questo indirizzo... http://weblogs.asp.net/aboschin. Si avete capito bene da oggi ho un blog nientepopodimeno che si www.asp.net!!! Spero solo che il mio rugginoso inglese sia sufficiente a scrivere qualcosa più di un paio di strafalcioni. Comunque nessuno si preoccupi... non ho alcuna intenzione di abbandonare il blog che siete abituati a leggere.

Technorati tags: ,

Fino a ieri siamo stati abituati a scrivere codice Javascript nelle pagine, cercando di ridurlo ai minimi termini nella profonda convinzione che quanto meno ce ne fosse più probabilità aveva il nostro progetto di funzionare a dovere. Non ho la certezza che voi la pensiate come me, ma so per certo che fino a pochi mesi fa questo era il mio criterio perchè ritenevo che ogni riga di codice Javascript immessa fosse un passo ulteriore verso il baratro dell'entropia nelle mie pagine. Se ben ci pensate in effetti questa sensazione ha un motivo profondo; il codice Javascript ha la caratteristica di "infestare" la pagina di riferimenti, variabili, funzioni e attributi in un modo che in breve la rende pressochè illeggibile. Poco fa ho iniziato la frase sottolineando "Fino a ieri". Infatti, come sto sperimentando in questi giorni alla fine il problema è semplicemente di metodo. E il metodo oggi porta il nome di Microsoft AJAX  Library che tante soddisfazioni mi sta regalando.

Ho trovato ad esempio molto utile l'uso della classe Sys.UI.Control. Questa classe mima le fattezze della più nota System.Web.UI.Control, e consente di creare dei controlli Javascript che vengano associati ad un elemento della pagina. Ad esempio volendo creare un controllo che simuli un Orologio potremmo procedere come segue:

   1: Type.registerNamespace('Elite');
   2:  
   3: Elite.Clock = function(element)
   4: {
   5:     Elite.Clock.initializeBase(this, [element]);
   6:     
   7:     this._timer = null;
   8: }
   9: Elite.Clock.prototype = 
  10: {
  11:     initialize : function()
  12:     {
  13:         this._timer = setInterval(Function.createDelegate(this, this._onInterval), 1000);
  14:         this._writeTime();
  15:         Elite.Clock.callBaseMethod(this, 'initialize');
  16:     },
  17:     dispose : function()
  18:     {
  19:         if (this._timer)
  20:         {
  21:             clearInterval(this._timer);
  22:             delete this._timer;
  23:         }
  24:  
  25:         Elite.Clock.callBaseMethod(this, 'dispose');
  26:     },
  27:     _writeTime : function()
  28:     {
  29:         var element = this.get_element();            
  30:         element.innerText = new Date().toLocaleTimeString();
  31:     },
  32:     _onInterval : function()
  33:     {
  34:         this._writeTime();
  35:     }
  36: }
  37:  
  38: Elite.Clock.registerClass('Elite.Clock', Sys.UI.Control);

Questo breve esempio illustra una classe Javascript che come si vede nelle ultime righe estende Sys.UI.Control. Estendere questa classe implica l'uso di un costruttore con un parametro "element" ovvero un riferimento ad un elemento del DOM ottenuto per mezzo di $get(). Questo elemento deve obbligatoramente essere passato alla classe base con initializeBase(), tenendo presente che l'uso della parentesi quadre [ ] nella chiamata altro non è che la dichiarazione semplificata di un array. initializeBase() infatti concede all'utente di passare un array di parametri per il costruttore della classe base, ma in questo caso siamo limitati sempre e solo a uno.

Il controllo prosegue implementando i metodi initialize e dispose. Il primo che viene chiamato in fase di inizializzazione del controllo è ereditato dalla classe base e si incarica di istanziare oggetti, agganciare eventi e fare sostanzialmente tutte quelle attività che allocano risorse. In particolare in questo caso si alloca un timer che notifica alla classe il trascorrere dei secondi e poi passa il controllo alla classe base.

Nel metodo dispose, ereditato dalla classe base che implementa Sys.IDisposable (non ci si fa mancare nulla... anche le interfacce!), ovviamente si provvede a disallocare tutte le risorse. Questa attività è di fondamentale importanza. nonostante ci troviamo in un ambiente "protetto", all'interno del browser, i memory leak sono quanto di più frequente possiamo immaginare. Ecco perchè disallocare tutto nel metodo dispose è l'unico modo realmente sicuro di garantire lunga vita alla nostra pagina e con lei alla finestra del browser...

Diversamente da quello che ci si può aspettare per instanziare questo controllo non è sufficiente usare "new". Per inizializzare correttamente il controllo e per fare in modo che al suo rilascio ne venga invocato il metodo dispose è opportuno usare Sys.Component.create o il suo più comodo shortcut $create():

   1: var clock = $create(Elite.Clock, {}, {}, null, $get('redClock'));
   2: clock.addCssClass('myRedClock');

In questo esempio si istanzia il controllo passando poi una classe css che darà ad esso l'aspetto voluto. addCssClass() è solo uno dei metodi utili che si possono reperire nella classe e nella gerarchia a cui fa capo.

In breve quello che si ottiene è riportato nello screenshot qui a sinistra. Una volta realizzata la classe sarà molto semplice usarla anche più volte nella medesima pagina associando ad essa gli stili e le caratteristiche grafiche che più desideriamo per renderlo consono alle nostre esigenze.

 Ricordiamo però che Sys.UI.Control richiede un elemento del DOM in ingresso e quindi non solo uno <span> o un <div> ma anche un qualsiati tag html finanche il <body> che possiamo raggiungere facilmente con la proprietà document.body. Proprio in questi giorni mi sono avvalso di questa tecnica per realizzare una classe che al pari della classe C# di Code Behind rappresenti gli elementi costitutivi della pagina. La comodità sta nel fatto che pressochè ogni chiamata può migrare dall'essere immersa nell'HTML a diventare un event handler nella classe stessa.

Se poi siete anche arditi potreste permettervi di adottare l'ereditarietà per creare una classe Javascript base per diverse pagine molto simili tra loro ma che applichino la logica in modo solo leggermente diverso. Questa tecnica che tipicamente si usa tra diverse pagine ASPX nel file .cs ora è perfettamente disponibile anche in Javascript e sostanzialmente ci consente di scrivere quello che potremmo definire come un code-behind client-side.

Inutile dire che ogni giorno che passa sono sempre più affascinato da questa libreria. In realtà, nonostante le premesse di questo post, amo particolarmente Javascript mediante il quale ho scritto delle applicazioni molto belle, ma dopo aver provato la Microsoft AJAX Library ne sono inguaribilmente affezionato... e attenzione perchè con silverlight e il Framework 3.5 le cose saranno anche meglio!

Technorati tags: , ,

Giusto ieri ho scritto due righe a proposito di programmazione object-oriented in Javascript e oggi voglio tornare sull'argomento per fare un ulteriore passo verso un lavoro corretto e produttivo. Noi tutti quando scriviamo in C# o in un qualunque linguaggio realmente object oriented siamo abituati a gestire eventi asincroni nel contesto stesso in cui questi sono generati in modo tale che la loro invocazione e la loro gestione rimanga confinata all'interno di una sola istanza di una classe. In Javascript invece, questo non è immediatamente possibile; Spesso e volentieri ci si trova a dover gestire chiamate asincrone, o eventi generati dall'interfaccia e utilizzando un paradigma object oriented ci si trova a gestire eventi fuori del contesto dal quale essi sono generati. Mi rendo conto che spiegare a parole questo problema è difficile perciò vi propongo un esempio che chiarisca. Poniamo ad esempio di voler scrivere una classe che sia in grado di trasformare un elemento dell'interfaccia in un pulsante. Ad essa sarà passato in ingresso un elemento del DOM ed essa sarà in grado di agganciare l'evento onclick e gestirlo quando necessario:

   1: // HTML
   2:  
   3: <div id="myButton">Click Me!!!</div>
   4:  
   5: // Classe Javascript
   6:  
   7: ClickButton = function(message, element)
   8: {
   9:     this._message = message;
  10:     element.onclick = this._onclick;
  11: }
  12: ClickButton.prototype = 
  13: {
  14:     _onclick : function()
  15:     {
  16:         alert(this._message);
  17:     }   
  18: }
  19:  
  20: // USO
  21:  
  22: var bt = new ClickButton("Ciao!!!", $get("myButton"));

Guardando questo pezzetto di codice può sembrare tutto in perfetto ordine ma in realtà se provate a farlo girare in un qualsiasi browser il risultato sarà una message box con scritto "undefined" anzichè come ci si aspetterebbe un caloroso "Ciao!!!". Come dicevo pocanzi il problema è che Javascript non ha la nozione di contesto di una istanza, pur se noi passiamo ad essa un riferimento al metodo _onclick() in realtà stiamo semplicemente copiando in una variabile il corpo della funzione stessa che sarà completamente slegata dal contesto dell'istanza in cui si trova. Per ovviare a questo problema occorre passare all'evento non solo la funzione _onclick() ma anche un riferimento a "this" così che il runtime sia in grado di eseguirla correttamente. Per fare questo nella Microsoft AJAX Library esiste un metodo della classe statica "Function" che serve proprio a generare correttamente questa chiamata. Ecco come modificare la classe di cui sopra per farla funzionare:

   1: ClickButton = function(message, element)
   2: {
   3:     this._message = message;
   4:     element.onclick = Function.createDelegate(this, this._onclick);
   5: }
   6: ClickButton.prototype = 
   7: {
   8:     _onclick : function()
   9:     {
  10:         alert(this._message);
  11:     }   
  12: }

Con createDelegate() verrà generata una funzione che ha appunto lacapacità di invocare il metodo richiesto nel contesto della classe di appartenenza. La definizione del metodo createDelegate() pur con questo nome un po' altisonante altro non è che uno stratagemma per memorizzare il riferimento a this:

   1: Function.createDelegate = function(instance, method)
   2: {
   3:     return function()
   4:     {
   5:         return method.apply(instance, arguments);
   6:     }
   7: }

Chi ha provato ad usare Silverlight si sarà già imbattuto in qualcosa di analogo. In vari siti nella rete si trova la definizione di un metodo che è del tutto analogo a Function.createDelegate(). Ecco quindi un buon motivo per referenziare la Microsoft AJAX Library anche quando si lavora con Silverlight. Attenzione però che esiste almeno un evento che in Silverlight non potrete gestire con questo metodo. Si tratta dell'evento onResize dell'oggetto Host che tipicamente viene usato per riposizionare il layout al resize del controllo host.


Javascript è un linguaggio ad oggetti, anche se per la massima parte l'uso che se ne fa è quello di scrivere piccole e semplici funzioni con un paradigma procedurale. In effetti scrivere ad oggetti per mezzo di javascript è molto diverso dallo scrivere in un comune linguaggio object-oriented. Molti dei principi di questo tioo di programmazione trovano applicazione solo per mezzo di stratagemmi, come ad esempio nel caso dell'ereditarietà.

La nuova libreria Microsoft AJAX Library, introduce una serie di utility che semplificano questo lavoro e consentono un uso più produttivo del paradigma object-oriented anche in ambito scripting. Per creare una classe in Javascript si può procedere in svariati modi, tuttavia facendo uso della libreria AJAX il metodo consigliato è quello dell'uso del prototype pattern come segue:

   1: MyClass = function()
   2: {
   3:     // costruttore della classe
   4: }
   5:  
   6: MyClass.prototype =
   7: {
   8:     // corpo della classe
   9: }

Le poche righe qui riportate mostrano lo scheletro di classe che si può realizzare facilmente anche senza l'uso della Microsoft AJAX Library. Possiamo facilmente aggiungere un metodo facendo uso della sintassi propria di JSON. Proviamo ad esempio a verificare che esista un effettivo incapsulamento delle informazioni dichiarando un campo "privato" e un metodo che dimostra il valore ad esso assegnato:

   1: MyClass = function(message)
   2: {
   3:     this._message = message;
   4: }
   5:  
   6: MyClass.prototype =
   7: {
   8:     display : function()
   9:     {
  10:         alert(this._message);
  11:     }
  12: }
  13:  
  14: // USO : --------------
  15:  
  16: var msg = new MyClass("Hallo World!");
  17: msg.display();

Il piccolo esempio riportato oltre a dimostrare le potenzialità di questa tecnica, ne dimostra anche i limiti. Ad esempio è piuttosto evidente che il campo "_message" che pur nella nostra immaginazione è evidentemente un campo privato, è facilmente accessibile dall'esterno. Non vi è naturalmente alcun modo di impedirlo, semplicemente perchè in javascript non esistono i modificatori di accesso, ma per convenzione nella libreria Microsoft l'uso di un trattino di sottolineatura sottintende che esso non debba mai essere usato esternamente.

Un'altro rilievo da notare è che l'uso della keyword "this" è obbligatorio. La sua omissione induce l'interprete a cercare una variabile globale con lo stesso nome e tutti noi sappiamo che in programmazione object-oriented di variabili globali non ve n'è traccia alcuna. Infine è da tenere in considerazione che la classe non può specificare una base e che di conseguenza non esiste override.

L'adozione della Microsoft AJAX Library ci da una serie di strumenti che ci permettono di simulare facilmente alcune delle "feature" della programmazione ad oggetti. Tanto per cominciare potremmo organizzare le nostre classi in namespace, dando ad esse una migliore leggibilità:

   1: Type.registerNamespace('Elite');
   2:  
   3: Elite.ButtonBehavior = function(element)
   4: {
   5:     this._element = element;
   6: }
   7:  
   8: Elite.ButtonBehavior.prototype = 
   9: {
  10:     ...
  11: }

La classe "Type" consente di creare un namespace fittizio che poi in seguito dovremmo riportare diligentemente in tutte le ricorrenze del nome. A questo punto ci dovremmo chiedere come fare ad estendere una nostra classe. Facciamo l'ipotesi di aver creato una classe che contenga delle funzioni base per il trattamento di elementi di interfaccia. Tale classe si chiamerà Elite.BaseElement. Mediante i metodi statici alle classi potremmo registrare la nuova classe creata ed assegnare ad essa una base:

   1: Elite.ButtonBehavior.registerClass('Elite.ButtonBehavior', Elite.BaseElement)

Per poter dire di aver completato la nostra classe non ci resta che adoperarci per fare in modo che avvenga l'invocazione dei metodi base. Non dimentichiamo infatti che si tratta pur sempre di una simulazione e quindi qualcosa da fare c'è, soprattutto per quanto riguarda il costruttore:

   1: Type.registerNamespace('Elite');
   2:  
   3: Elite.ButtonBehavior = function()
   4: {
   5:     Elite.ButtonBehavior.initializeBase(this);
   6: }
   7:  
   8: Elite.ButtonBehavior.prototype = 
   9: {
  10:     initialize : function()
  11:     {        
  12:         Elite.ButtonBehavior.callBaseMethod(this, 'initialize');
  13:     }
  14: }
  15:  
  16: Elite.ButtonBehavior.registerClass('Elite.ButtonBehavior', Elite.BaseElement);

Lo scheletro della classe è pronto. Non resta che aggiungere i metodi necessari ricordando che in Javascript le property non esistono ma possono essere emulate con una coppia di metodi get_ e set_. La Microsoft AJAX Library infatti istituzionalizza anche questo consentendo ad esempio in fase di inizializzazione di assegnare i valori come vere e proprie proprietà.


Mentre navigavo alla ricerca di informazioni per preparare delle slide ho scovato un servizio offerto dal sito del . Si tratta di un WebService ASMX esposto pubblicamente che consente di consumare servizi relativi il calendario. Così finalmente potro sapere con certezza di che giorno cade pasqua o magari quanti giorni lavorativi ci sono in un mese.

Link: http://www.ilpalio.siena.it/drs_Calendario.asmx

Ah, per inciso l'anno prossimo pasqua viene il 23 Marzo... provare per credere


Stasera ho provato a smontare l'updatepanel per cercare di capirne il funzionamento. Dopo un po' che mettevo il naso qui e li nel codice con FireBug, sono arrivato al cuore ovvero dove vengono effettuate le chiamate. Ne è uscito questo pezzettino di codice, tratto adattando gli internals che utilizza Sys.Net.WebClient per effettuare una chiamata in POST sul server di provenienza della pagina. In realtà il codice è volutamente semplificato per renderlo maggiormente comprensibile a chiunque:

   1: Downloader = function Elite$Downloader()
   2: { }
   3:  
   4: Downloader.prototype =
   5: {
   6:     _asyncPostBackCompleted:function(sender, eventArgs)
   7:     {
   8:         var tm = $get("time");
   9:         tm.innerHTML = sender.get_responseData();
  10:     },
  11:     download:function()
  12:     {
  13:         var request = new Sys.Net.WebRequest();
  14:         request.set_url("time.axd");
  15:         request.get_headers()['X-AndreaBoschinAjax'] = 'Delta=true';
  16:         request.get_headers()['Cache-Control'] = 'no-cache';
  17:         request.set_timeout(10000);
  18:         request.set_httpVerb("GET");
  19:         request.add_completed(Function.createDelegate(this, this._asyncPostBackCompleted));
  20:         request.set_body("");
  21:         request.invoke();
  22:     }      
  23: }

Nel codice che ho scritto dichiaro una classe "Downloader" creata semplicemente per pura utilità nell'assegnare i delegate. Al click di un pulsante (non incluso nel riquadro) viene invocato il metodo download() che effettua la chiamata al server e restituisce la risposta in modo asincrono.

Non so dirvi l'utilità pratica di questo codice, ma dicerto ha una sua utilità "accedemica". Chissà poi che qualcuno non trovi un caso reale in cui applicarlo.


Stamane nel newsgroup di asp.net è apparsa una domanda interessante che mi ha fatto pensare un po' a proposito delle potenzialità di Membership e di ProfileProvider. Ho colto così l'occasione per scrivere due righe di codice per risolvere un problema che effettivamente potrebbe essere limitante nell'uso di questi strumenti. Il problema in questione è che così come le informazioni del profilo vengono persistite nella relativa tabella è praticamente impossibile se non molto difficile accedere ad esse per filtrare gli utenti in base ad esse.

Le informazioni di profilo vengono memorizzate tutte in due campi nella tabella aspnet_Profile con un formato particolare che consente al runtime di recuperarle velocemente ma che d'altro canto impedisce di accedervi per mezzo di una semplice query. Ecco come:

   1: PropertyNames: Age:S:0:2:Province:S:2:2:Town:S:4:7:
   2: PropertyValues: 38TVTreviso

In sostanza il campo PropertyNames oltre al nome delle proprietà contiene il tipo, il punto di inizio e la lunghezza della frazione del campo PropertyValues. Nel riquadro è riportato il formato di tre proprieta: Age, Province e Town.

Ora è del tutto evidente che sperare di scrivere una query SQL che consenta di raggiungere ad esempio tutti gli utenti che abitano in provincia di Treviso è pura utopia. Inoltre se andiamo a vedere i metodi del ProfileProvider in breve ci renderemo conto che estrarre questa informazione è piuttosto complicato e rischia di costringerci a leggere i campi uno per uno decodificandoli.

Oggi qualcuno naturalmente aveva bisogno di ottenere proprio questo risultato e quindi mi sono dovuto inventare un workaround. Di per se non sarebbe complesso trovare gli utenti di Treviso se i dati fossero spalmati correttamente in una tabella, perciò ho pensato di salvare una copia dei dati di interesse in una tabella di appoggio da usare per questi scopi.

L'idea è quella di inserirsi al punto giusto ed operare inserimenti e update all'unisono con il provider stesso in modo da tenere allineata questa tabella senza però perdere le funzionalità comode del provider che deve rimanere l'unico gestore in lettura e scrittura. Quindi ho deciso di estendere il provider e fare l'override del metodo SetPropertyValues().

   1: public override void SetPropertyValues(
   2:     SettingsContext sc, 
   3:     SettingsPropertyValueCollection properties)
   4: {
   5:     base.SetPropertyValues(sc, properties);
   6:  
   7:     using (SqlConnection conn = new SqlConnection(this.ConnectionString))
   8:     {
   9:         conn.Open();
  10:  
  11:         using (SqlCommand command = new SqlCommand("spx_UpdateProfileIndex", conn))
  12:         {
  13:             command.CommandType = CommandType.StoredProcedure;
  14:             command.Parameters.AddWithValue("@username", sc["UserName"]);
  15:             command.Parameters.AddWithValue("@province", properties["Province"].PropertyValue);
  16:             command.ExecuteNonQuery();
  17:         }
  18:     }
  19: }

Nel metodo dapprima si chiama la base che provvede ad inserire i dati al loro posto come consueto. Poi il controllo passa alla stored procedure nella quale dati che vengono salvati nella tabella di appoggio. La tabella in questione ha un campo per ogni proprietà. La stored procedure è abbastanza furba da sapere se deve fare una insert o un update.

Con questo sistema si riesce a creare quindi una tabellina semplice che ci aiuti a fare delle query per trovare il riferimento agli utenti che hanno un dato valore in una proprietà. L'importante è non abusarne. Non si deve nemmeno per un momento pensare che modificando un valore in essa si modificheranno in accordo anche il valore della proprietà di profile, altrimenti ci troveremmo nella condizione in cui rifare il provider è più conveniente. Ovviamente poi sarà nostro onere tenere il numero di colonne appropriato per proprietà che vengano aggiunte in futuro.

Tuttavia lasciatemi fare una considerazione: la palese inadeguateza del ProfileProvider in casi come questo mi suggerisce che sia preferibile adottare un sistema custom per la persistenza di proprietà che poi servano per catalogare gli utenti, mentre il ProfileProvider dovrebbe essere usato per dati meno sensibili come ad esempio le personalizzazione dell'interfaccia o comunque dei dati che devono solamente essere persistiti tra diverse sessioni.

Codice di esempio completo: (6,8 KB)


Quest'oggi nei newsgroup qualcuno mi ha chiesto come fare a spostare la ViewState (che ha la spiacevole tendenza a diventare colossale) in Session. Ho mostrato questa tecnica durante un recente webcast che trovate a questo indirizzo il codice sorgente da cui attigere http://www.xedotnet.org/40/section.aspx/376.

Comunque ripeto qui l'esempio, che peraltro è di una semplicità disarmante per completezza:

   1: protected override PageStatePersister PageStatePersister
   2: {
   3:     get
   4:     {
   5:         return new SessionPageStatePersister(this);
   6:     }
   7: } 

Il PageStatePersister è un componente di recente acquisizione per ASP.NET che ha l'onere appunto di persistere la viewstate. Tipicamente in questo campo si trova un HiddenFieldPageStatePersister, ma possiamo semplicemente cambiarlo con un'altro fornito dal framework stesso che si chiama SessionPageStatePersister e il gioco è fatto.

Comunque nel caso in cui la viewstate assumesse dimensioni bibliche vi consiglio di controllare quello che state facendo perchè il più delle volte basta impostare EnableViewState="false" su alcuni controlli chiave e si ottengono dei benefici notevoli. Non dimenticate infatti che occupare la Session può portare comunque ad un degrado delle prestazioni che avrà un impatto su tutti gli utenti collegati. Quindi occhio...


Recentemente mi è capitato di incorrere in acuni problemi relativi l'accesso alla Cache di ASP.NET in situazioni di scarsità di risorse e quindi in quei momenti particolari in cui entra in gioco la scadenza dei contenuti per i quali si usa la cache. Il codice che tempo fa avevo scritto pur se a prima vista dovrebbe essere funzionante, ad una analisi più meditata si è rivelato errato. Il codice, ridotto all'osso per renderlo meglio comprensibile è il seguente:

   1: public string GetCachedValue()
   2: {
   3:     if (Cache["cachedValue"] == null)
   4:         Cache["cachedValue"] = GetData();
   5:  
   6:     return Cache["cachedValue"] as string;
   7: }

Come dicevo il codice a prima vista non ha motivo di non funzionare, ma analizzando bene quello che può accadere durante il ciclo di vita dell'applicazione ci si rende conto che tra l'esecuzione della riga 3 e l'esecuzione della riga 6, può incorrere uno switch di contesto del thread in esecuzione e di conseguenza la cache potrebbe scadere ed essere rilasciato. Perciò se alla riga 3 il risultato dell'if determina che nella cache c'è qualcosa potremmo arrivare che alla riga 6 invece la cache è vuota. Vi assicuro che anche se ciò possa sembrare una eventualità remota, in un contesto in cui le risorse scarseggiano e in cui le chiamate sono molte essa non mancherà di verificarsi. Ecco quindi il corretto approccio alla cache per evitare questo tipo di problematiche:

   1: public string GetCachedValue()
   2: {
   3:     object cachedValue = Cache["cachedValue"];
   4:  
   5:     if (cachedValue == null)
   6:     {
   7:         cachedValue = GetData();
   8:         Cache["cachedValue"] = cachedValue;
   9:     }
  10:  
  11:     return cachedValue as string;
  12: }

Cosa cambia in questo approccio? Questo sistema, del tutto analogo ad esempio a quello che si usa per i singleton è thread-safe. Semplicemente il thread corrente prende un riferimento all'oggetto in cache nella variabile cachedValue. Questo comporta che anche se la cache dovesse essere liberata, il riferimento all'oggetto rimane valido e di conseguenza può essere ritornato in tutta tranquillità senza il rischio che i chiamanti incorrano in NullReferenceException. Per capire questa soluzione bisogna tenere bene a mente il fatto che nella cache sono presenti istanze di "object" e quindi o dei ReferenceType o dei ValueType boxed. Prenderne un riferimento eviterà innanzitutto che valutando la cache due volte si trovi una situazione differente ma eviterà anche che l'oggetto liberato sia mandato in coda per la collection da parte del Garbage Collector.

Technorati tags: , ,

E' passato un po' di tempo dall'uscita del mio ultimo articolo sul sito di XeDotNet. Questo weekend ho deciso di investire un po' di tempo per scrivere un piccolo ma interessante esempio di come funziona a il VirtualPathProvider, un componente vitale di ASP.NET che nei mesi scorsi ho avuto modo di approfondire grazie ad un lavoro particolare che ho svolto in ufficio. L'articolo che ne è uscito illustra le potenzialità di questo particolare provider e descrive l'esempio nel quale un intero sito web composto da pagine, immagini, masterpage e css viene racchiuso all'interno di un assembly come Embedded Resource. Si tratta ovviamente di un esercizio che però oltre a dimostrare ampiamente le magie che si possono ottenere con il VirtualPathProvider, è anche in grado di rivelarne abbastanza in profondità limiti, problematiche e caratteristiche.

Link: