[ASP .NET] How to access Request data when HttpContext.Current.Request is unavailable

What happened if you need to access the HttpRequest object whitin the event Application_Start of an ASP .NET Web Application ?

Response: you end up with an exception.

Someone might observe that such event is not a valid place for a task like that, but sometimes things are simply different from those that appear at first sight.

So, if you want to get for example the virtual path or the physical path of a web application before the HttpRequest object is constructed  this is a valid solution:

HttpRuntime.AppDomainAppVirtualPath;
HttpRuntime.AppDomainAppPath;

Simply use HttpRuntime instead of HttpContext.Current.Request!

Technorati Tags: ,

L’evento Application_Start ed il suo corretto utilizzo

Regola importante in una applicazione ASP .NET:

durante l’evento Application_Start dovrebbero essere assegnate solo variabili statiche e mai variabili d’istanza; ciò è dovuto al fatto che questo evento viene sollevato solo 1 volta durante il ciclo di vita dell’applicazione e, insieme all’evento Application_End, non è legato alla istanza in esecuzione della classe HttpApplication ma, al contrario, entrambi sono considerati eventi speciali.

Infatti, ogni applicazione ASP .NET in esecuzione è associata ad un oggetto HttpApplication, capace di gestire una richiesta alla volta. Durante il suo ciclo di vita possono essere create più istanze della classe HttpApplication, ognuna delle quali in ottica di ottimizzazione delle performance può essere riciclata. Tuttavia, i due eventi sopra citati non vengono sollevati per ogni istanza creata di HttpApplication ma, come detto, solo 1 volta durante la vita di una applicazione.

Un membro d’istanza creato in Application_Start quindi sarebbe visibile solo per la prima istanza creata di HttpApplication e non anche per le successive.

Viceversa, l’evento sollevato per ogni istanza creata di HttpApplication è invece Application_Init.

ASP .NET 4.0 #3 Ciò che non è cambiato

ASP .NET 4.0 è ormai alle porte, con la versione beta è possibile scoprire le novità  rispetto alla versione precedente, e non sono certamente poche, ma a livello di controlli lato server ce ne sono alcuni praticamente immutati rispetto alle precedenti versioni. Mi riferisco ad esempio al controllo Http File Upload, rimasto identico nelle varie versioni di ASP .NET che si sono succedute. Questo controllo soffre di qualche problema e non è certo il massimo in ottica web 2.0, ovvero su siti dove è richiesto una elevata user experience.

A meno di non utilizzare un controllo di terze parti probabilmente a pagamento, occorre fare i conti con il look del controllo rimasto identico nel tempo, con l’assenza di funzionalità  oggi richieste quali ad esempio la barra di progressione dell’upload in corso, e soprattutto con un fastidioso comportamento “by design”, ovvero la perdita del contenuto (il nome completo di percorso del file scelto) ad ogni postback della pagina; quest’ultima caratteristica è resa necessaria da motivi di sicurezza.

Se la pagina dispone di altri controlli che generano un postback, es. una dropdownlist, l’unico escamotage è quello di rendere il controllo File Upload l’ultimo controllo che genera postback in ordine di visualizzazione, in modo da invogliare l’utente ad interagire per ultimo con esso. In caso contrario, un postback della pagina provocherà  la perdita del suo contenuto, cioè del nome del file scelto, ed ovviamente obbligherà  l’utente a scegliere nuovamente il file da inviare.

L’evento page load è eseguito 2 volte

Convertire un progetto ASP .NET dalla versione 1.1 ad una versione successiva del .NET Framework nasconde un inconveniente a cui occorre porre rimedio manualmente.

L’inconveniente è dovuto alla introduzione delle partial class a partire dalla versione 2.0 del .NET Framework, in contrapposizione al codice generato dal designer nella versione 1.1.

Questo fa si che importando il codice sorgente nella nuova versione utilizzata ci si ritrovi, ad esempio, con un event handler come questo nel metodo InitializeComponent

 private void InitializeComponent() 
 { 
     this.Load += new System.EventHandler(this.Page_Load); 
 }

Questo innocente codice derivante dalla conversione fa in modo che l’evento Page Load sia generato 2 volte, una volta dall’handler presente nella partial class ed una volta da quello presente nel metodo InitalizeComponent.

Per eliminare questo fastidioso inconveniente è sufficiente rimuovere l’handler presente nel metodo InitializeComponent

ASP .Net e i thread secondari

Interessantissimo post di Stefano Pronti del nuovo blog MSDN di Supporto Tecnico agli Sviluppatori, che spiega le disastrose conseguenze di non richiamare il metodo Dispose su risorse unmanaged, utilizzate all’interno di una web application.

Per farla breve, le risorse unmanaged utilizzavano un thread secondario rispetto a quello che prende in carico la web request, ed in questo thread secondario veniva sollevata una eccezione non gestita durante la fase di finalizzazione del garbage collector, che, come è noto, viene eseguito in un thread diverso.

In questo caso il comportamento di ASP .NET a partire dalla versione 2.0 è quello di interrompere immediatamente il processo in esecuzione, con conseguenze facilmente immaginabili.

Ho già parlato qui di questo comportamento di ASP .NET e di come sia possibile utilizzare la modalità pre-versione 2.0 di gestione delle eccezioni non gestite sollevate all’interno di thread diversi.

Anche a me è capitato di dover “impazzire” con una applicazione in produzione, abbastanza vasta, che soffriva di frequenti ed improvvise cadute della sessione corrente, con enorme disagio degli utenti.

Nel caso specifico non è stato indispensabile attaccare un debugger per ottenere il dump della memoria al momento dell’eccezione, è bastato debuggare il codice, che non conoscevo neanche bene, e scoprire che venivano creati thread aggiuntivi (!?) il cui codice, in particolari circostanze, sollevava l’eccezione fatale che provocava il riavvio del worker process.

HttpResponse.ApplyAppPathModifier

Se si usano le sessioni ASP .NET coockieless, ovvero sessioni in cui il SessionID viene inserito nell’url nella seguente forma:

/App/(S(avsbnbml2n1n5mi5rmfqnu65))/default.aspx

il metodo ApplyAppPathModifier della classe HttpResponse risulta estremamente utile, poichè, passandogli come parametro stringa un path virtuale, restituisce lo stesso path con il SessiondID inserito correttamente nell’url, sollevando lo sviluppatore dalla costruzione manuale dello stesso. Ciò risulta evidente ogni qual volta è necessario utilizzare url caricati dinamicamente.

IIS ASP .NET Tab missing

Vi è mai capitato che nella console di amministrazione di IIS per una certa web application sparisse il tab ASP .NET senza apparente motivo ?

A me sì, con tutte le conseguenze del caso, e senza che riuscissi a trovare una soluzione nei forum e user group. Ora finalmente la soluzione esiste.

E’ spiegata in modo dettagliato in questo post, ed inoltre, come afferma l’autore del post, non esiste nessuna soluzione immediata che pone riparo a questa anomalia. 

Messaggio di errore Ambiguous match found e httpParseException

Scenario: web application che utilizza la versione 1.1 di ASP .NET migrata direttamente alla versione 3.5. Dopo la migrazione su una delle pagine ASPX viene sollevato una HttpParseException durante il caricamento della stessa. L’eccezione in questione, come si evince dal nome, viene generata dal runtime di ASP .NET quando il parsing di una pagina ASPX fallisce a runtime. Il messaggio di errore recita “Ambiguous match found”, e quindi non aiuta granchè. La cosa curiosa è che l’eccezione non si verifica in ambiente di sviluppo ma solo sulla versione di deploying dell’applicazione, quindi non è “debuggabile” in Visual Studio 2008 ( a meno di non effettuare un debug in remoto, cosa quasi mai possibile in ambiente di produzione) , e quindi non è di facile risoluzione.

In questi casi la prima cosa che penso è: sicuramente altri developers sparsi per il mondo hanno già sperimentato lo stesso problema, quindi  mi metto a perlustrare blogs e forum e normalmente alla fine il problema si risolve !

E così è stato anche stavolta, anche se le cause di questa eccezione possono essere diverse e quindi non esiste una soluzione universalmente applicabile.

Alcune delle cause che possono produrre una httpParseException sono, in ordine sparso:

  1. la pagina contiene un campo hidden che ha un ID con lo stesso nome di una variabile querystring usata dalla stessa pagina;
  2. la pagina ha un controllo (non ascx) inserito nel file ASPX (e quindi inserito nella partial class non visibile), e nel code-behind della stessa è dichiarato un altro controllo protected con lo stesso nome;
  3. la pagina contiene un controllo ASCX con lo stesso nome di un controllo nativo ASP .NET;

Tutte queste situazioni generano evidentemente una ambiguità dei tipi, che porta all’eccezione.

Inoltre,  effettuando un deploy del sito con l’opzione “non aggiornabile”, ovvero deselezionando l’opzione “Allow this precompiled site to be updateable”, l’eccezione dovrebbe scomparire, salvo poi approfondire le sue cause e riabilitare nel caso l’opzione.

Partial rendering troubleshooting

Regola importante: l’update parziale di una pagina ASP .NET 2.0 (o successivi) attraverso l’UpdatePanel di Ajax non funziona in presenza di questo tag nel file di configurazione dell’applicazione (o nel machine.config):

Infatti, con questa impostazione la proprietà “SupportPartialRendering” dell’oggetto ScriptManager ritorna il valore false.

Il tag in questione imposta la modalità di rendering dei controlli, es.:  in modalità compatibile XHTML (mode=”Transitional” o “Strict”) oppure no (mode=”Legacy”).

In ASP .NET 1.1 i controlli subivano un rendering non XHTML compatibile, e questo comportamento è stato modificato in ASP .NET 2.0, che invece effettua il rendering XHTML compliant. Questo significa che se si migra una applicazione scritta con la versione 1.1 del .NET Framework ad una versione più recente, il wizard di migrazione imposta l’xhtml conformance mode in modalità Legacy, provocando di fatto il mancato funzionamento del partial rendering. Per risolvere il problema è sufficiente impostare il tag mode a “Transitional” (valore di default) oppure “Strict”, oppure rimuovere il nodo in modo tale da assegnargli il valore  di default, es.:

TextBox ReadOnly in .NET 2.0 e successivi

Il controllo TextBox di una web application dispone della proprietà ReadOnly che, ovviamente, impedisce l’interazione dell’utente con il controllo quando è impostata a True.

Ma c’è un particolare importantissimo da considerare: a partire dalla versione 2.0 del .NET Framework il contenuto di un textbox in modalità “ReadOnly” è inviato al server durante un postback della pagina, ma il server ignora questo valore; in altre parole il contenuto del textbox viene perso durante un postback. Questo comportamento mira ad impedire attacchi alla sicurezza, che potrebbero modificare un valore che dovrebbe essere mantenuto inalterato.

La perdita del valore del textbox può essere inaccettabile in certi contesti applicativi e potrebbe essere necessario applicare il comportamento in essere prima del Framework 2.0. Per far ciò è necessario impostare la proprietà ReadOnly come attributo del controllo, in questo modo:

TextBox1.Attributes.Add("readonly","readonly")

e non impostando a True la relativa proprietà.

Così facendo il valore del textbox (ovvero la sua proprietà Text) viene inviato al server durante il postback ed è altresì disponibile per l’elaborazione.

UPDATE: questo aggiornamento è, diciamo così, dovuto: il “copyright” di questa scoperta che ha portato via parecchio tempo prima di venirne a capo non è mio ma della mia collega di lavoro Ines. Io ho solo raccolto il troubleshooting per aiutare altri programmatori come un tempo altri programmatori hanno aiutato me

Eccezioni non gestite in ASP .NET 2.0

Le eccezioni non gestite generate da una applicazione ASP .NET compilata con la versione 2.0 del .NET Framework sono trattate diversamente da quanto avveniva con le applicazioni ASP .NET compilate con la versione 1.0/1.1. Queste ultime semplicemente ignoravano le eccezioni non gestite sollevate all’esterno del contesto corrente, es. un thread diverso da quello principale, mentre le eccezioni sollevate all’interno del contesto erano trattate normalmente come qualsiasi eccezione non gestita. Con il .NET Framework 2.0 questo comportamento è cambiato: le eccezioni non gestite sollevate fuori dal contesto provocano l’immediata interruzione del worker process e conseguentemente dell’applicazione. L’unica traccia è un laconico messaggio nell’event viewer  (System Log) del tipo “DefaultAppPool terminated unexpected”, seguito da un ancor più generico messaggio nell’Application Log (Event Source: .NET Runtine 2.0 Error Reporting).

Ma questo comportamento (il default) è legato ad una precisa policy di gestione delle eccezioni non gestite e può essere modificato in 2 modi:

-Aggiungendo le seguenti righe nel file Aspnet.config:

per fare in modo che le eccezioni non gestite siano trattate come nel .NET Framework 1.0/1.1, ovvero ignorate (scelta non
raccomandata da Microsoft)

-Creando un opportuno httpModule che si registra per l’evento AppDomain.CurrentDomain.Unhandledexception, attraverso il quale loggare i dettagli dell’eccezione  non gestita verificatasi.

Il tutto è documentato in questo articolo, con un esempio di httpModule.

Single Sign On & sezione <machineKey>

Girovagando tra la rete mi sono imbattuto in questo utilissimo tool utilizzabile on-line per la generazione della sezione <machineKey> del web.config, nel caso, molto concreto oggigiorno, si voglia implementare il meccanismo di SSO (Single Sign On) per le proprie applicazioni. In sostanza si tratta di effettuare il login una sola volta ed essere riconosciuto da n applicazioni che condividono in questo modo il contesto di sicurezza dell’utente, con indubbi benefici.

Delete Subdirectory & AppDomain recycle

Tra le cause che provocano lo scaricamento immediato dell’AppDomain di una applicazione ASP .NET 2.0 è presente anche la cancellazione di una sub-directory della applicaizione principale, operata attraverso Windows Explorer. Infatti, dopo tale operazione nell’Event Viewer del server appare un messaggio di tipo informativo.

Questo comportamento è differente rispetto a ad ASP.NET 1.0/1.1, dove la stessa operazione non produceva assolutamente nulla nè l’engine veniva notificato dell’evento; quindi il contenuto della directory cancellata continuava ad essere considerato valido con il potenziale rischio di errore a run-time.

Il team di sviluppo di ASP .NET 2.0 ha considerato un bug il comportamento delle versioni precedenti (a mio avviso giustamente) ed ha modificato il comportamento come fosse un bug-fixing, cioè piuttosto di rischiare di servire una pagina non più esistente viene ora scaricato l’intero AppDomain. L’unica nota stonata è che nel messaggio scritto nell’ Event Viewer l’applicazione risulta scaricata a causa di “reason unknown” che non permette di capire immediatamente la causa, invece di un bel “reason:directory xyz deleted”

Check-list per applicazioni ASP .NET in produzione

Riporto di seguito una lista, in ordine sparso, tratta dalla mia esperienza sul campo dei possibili accorgimenti e/o errori che influiscono sulla scalabilità o sulle performance e che tornano quindi utili quando si trasferisce su di un server di produzione una applicazione ASP .NET complessa con elevato numero di accessi concorrenti:

  • Di default il numero di connessioni HTTP concorrenti su uno specifico indirizzo IP e per uno specifico AppDomain è pari a 2. Questo valore è sufficiente ad esempio nel caso di web services che vengono acceduti da un unica applicazione ASP .NET e le cui pagine non effettuano chiamate multiple allo stesso web service. Nell’ipotesi in cui il servizio web riceve richieste da più applicazioni o dalla stessa applicazione ma con chiamate multiple dalla stessa pagina, questo valore potrebbe causare che il worker thread che esegue la richiesta resti in attesa che si liberi una connessione, causando chiaramente una situazione di contesa delle risorse che porta a problemi di scalabilità e, nel peggiore dei casi. al riciclo dell’applicazione con esplicito messaggio di errore nel log degli eventi. Il numero delle connessioni può essere applicato per ogni singolo IP richiesto; il valore consigliato da Microsoft nel caso sopra esposto è di 12 * N ( dove N è il numero di CPU disponibili sul server)

  • Gli assembly usati da più di una applicazione devono essere firmati con strong name e installati in GAC. In questo modo 1 sola copia dell’assembly è presente in memoria a prescindere dal numero di applicazioni che lo usano; se, al contrario, lo stesso assembly viene installato nella directory bin di ogni applicazione che lo utilizza, esso sarà presente in memoria N volte, una per applicazione, con conseguente spreco di risorse.
  • Evitare situazioni che possono causare memory-leak. Ciò si verifica quando l’occupazione di memoria cresce in modo incontrollabile, ad es. perchè esistono oggetti i cui puntatori non possono essere gestiti nel modo corretto, tipico caso applicabile all’utilizzo di risorse unmanaged, e pertanto la memoria occupata da essi non può essere liberata dal GC ma solo da un riciclo dell’applicazione o peggio ancora da un riavvio di IIS. Le risorse unmanaged devono essere liberate implementando un distruttore per l’oggetto che ne fa uso oppure esplicitamente da codice attraverso l’interfaccia IDisposable. Tra le condizioni che possono provocare un alto utilizzo della memoria e quindi il rischio di memory leak meritano una menzione particolare l’utilizzo di oggetti del Framework che creano dinamicamente assembly. Ad esempio, se ogni volta che si intende serializzare un oggetto in XML si crea una istanza della classe xmlSerializer utilizzando una particolare versione di overload del suo costruttore, e cioè quella che prevede il passaggio degli extraTypes, per ogni istanza sarà creato un assembly dinamico dietro le quinte. L’assembly dinamico non potrà essere scaricato dalla memoria se non quando viene scaricato l’Application Domain che lo contiene. Altre situazioni analoghe a quest’ultima riguardano l’uso di RegEx o di xmlTransform. In quest’ultimo caso il problema è identico in quanto durante una trasformazione XSLT se si utilizzano blocchi di script inline il .NET Framework genera un assembly dinamico che resta in memoria fino al riciclo del processo host.  Maggiori informazioni qui e qui.
  • Il riciclo di un application pool avviene di default ogni 1740 minuti, pari a 29 ore. Questo valore non è chiaramente appropriato per tutti gli scenari. Sarebbe a mio avviso preferibile impostare il riciclo ad un’ora prefissata, magari notturna.
  • Quando un application pool viene riciclato per un qualsiasi motivo non sempre viene tracciato nel log degli eventi. Per permettere il log degli eventi al verificarsi di determinati eventi occorre modificare il metabase di IIS, impostando la proprietà LogEventOnRecycle con il parametro desiderato. A seconda della politica di riciclo impostata sul server di produzione è preferibile settare questo parametro al valore più opportuno per un troubleshooting più efficiente. Maggiori informazioni in questo articolo.
  • Il riciclo di un processo ASP .NET può avvenire anche al raggiungimento di una certa soglia di memoria occupata dallo stesso (parametro memoryLimit in machine.config o nelle proprietà dell’applicazione web in IIS6). Questa soglia è impostata di default al 70% di memoria fisica disponibile nel sistema, e andrebbe tarata su misura in base alle proprie esigenze. Da notare che il calcolo percentuale della memoria a disposizione viene influenzato dal parametro webGarden e cpuMask. Quindi, se si imposta il parametro webGarden a true e cpuMask a 4 CPU, la soglia di memoria RAM oltre la quale si scatena il riciclo del processo è 1/4 della percentuale impostata nel parametro memoryLimit, quindi una soglia decisamente bassa che potrebbe far aumentare il numero di ricicli che si verificano.
  • Il valore dell’attributo “debug” dell’elemento “compilation” nel file di configurazione “web.config” deve essere impostato a “false” in ambiente di produzione. Per una trattazione dei problemi a cui si può andare incontro lasciando il valore predefinito “true” è possibile far riferimento ad un mio precedente post sull’argomento.

Chiaramente questa non vuole essere una lista esaustiva circa tutti i possibili scenari a cui si può andare incontro quando si effettua il deployment in produzione di una applicazione web, ma solo una semplice check-list pronta all’uso che provvederò magari ad aggiornare man mano che entrano in gioco altri fattori.

Data caching: considerazioni

L’utilizzo della cache in applicazioni web non è sempre intuitivo come potrebbe sembrare. Se inseriamo un oggetto in cache assegnandogli, ad es., una scadenza assoluta di 1 ora, potremmo pensare che lo stesso rimanga in cache fino a che non scade. Invece non è così. Un oggetto in cache può in qualsiasi momento antecedente la sua scadenza (cioè quando è ancora valido) essere eletto per una operazione di garbage collection; quindi può essere distrutto e, in tal caso, la memoria da esso occupata viene liberata, a prescindere se il suo periodo di validità si è esaurito oppure no. Questo comportamento potrebbe verificarsi nei casi in cui il sistema richieda memoria per compiere una qualche operazione e nello stesso tempo la memoria cache risulta occupata. Morale: non bisogna mai dare per scontato che un oggetto inserito in cache sia disponibile anche se la sua scadenza è molto ampia. Questo meccanismo può essere disabilitato impostando l’enumerazione CacheItemPriority a CacheItemPriority.NotRemovable. In questo caso un oggetto in cache entrerà a far parte di un garbage collector solo se è scaduto, altrimenti sarà sempre valido. Questa impostazione torna utile nei casi in cui non è possibile ricreare l’oggetto dopo averlo messo in cache; infatti essa viene utilizzata dalla sessione InProcess di ASP .NET.

Un altro aspetto da considerare è che la cache è specifica di un certo Application Domain, e non cross appdomain. Questo significa che è globale a livello della applicazione che ne fa uso, e quindi 2 applicazioni (ovvero 2 appdomain) utilizzano sempre cache diverse e quindi non condivise. Ma come fare allora se vogliamo condividere la cache in più di una applicazione, in modo, ad es., che se inserisco un oggetto in cache dalla applicazione A anche l’applicazione B possa vederlo ? In uno scenario complesso e, soprattutto, in una web farm, è possibile memorizzare gli oggetti in un repository differente dalla memoria cache, ad. es. in un database. Questo approccio garantisce chiaramente una altissima disponibilità del servizio di caching, anche in caso di riavvio del sistema. Un approccio più semplice consiste nel creare un web service (quindi un’altra web application) che espone metodi get/set per gli oggetti da inserire in cache usando il meccanismo nativo di ASP .NET. In questo modo qualsiasi applicazione che usa il web service potrà utilizzare la cache, che sarà condivisa perchè l’application domain è sempre lo stesso.

Una risorsa fondamentale per il mio lavoro

Di questo link ne faccio un post nel mio blog per tenerlo sempre a portata di….mouse!. Trattasi di una raccolta di link di Scott Gu, uno dei maggiori esperti mondiali di ASP .NET e di .NET in generale, riguardanti tips & tricks, approfondimenti e veri e propri tutorial per chi sviluppa applicazioni web di livello enterprise. Il livello di dettaglio e la chiarezza espositiva di Scott Gu è davvero superlativa, e attraverso il suo blog si entra in possesso di “conoscenza” che forse nemmeno i libri di livello avanzato o i workshop tecnici più spinti riescono a fornire. Ho avuto la fortuna di assistere ad un evento tecnico tenutosi a Roma un paio di anni fa, in cui Scott parlò del suo argomento preferito, ovvero ASP.NET, e posso affermare con assoluta certezza che Scott Gu è davvero un grande e che le sue conferenze meritano sempre di essere ascoltate con attenzione.

Web deployment project, la mia esperienza

Il nuovo modello di compilazione-deployment di ASP .NET 2.0 non è certamente facile da usare e nemmeno intuitivo, soprattutto per chi proviene da ASP .NET 1.0/1.1. 

Non a caso Microsoft ha rilasciato un add-in, il Web deployment Project, praticamente in concomitanza con l’uscita di Visual Studio 2005, che permette di utilizzare il modello di compilazione di ASP.NET 2.0, pur garantendo un alto grado di flessibilità; infatti è possibile compilare una applicazione web  in un singolo assembly, oppure in un assembly separato per ogni sua directory, oppure ancora in un assembly separato per ogni pagina/controllo.

In aggiunta, poco tempo dopo è stato rilasciato un’altro add-in, il Web Application Project, di cui magari parlerò in in post a parte. Per il momento mi limito a dire che ambedue questi add-in non sono supportati da Visual Studio in versione Visual Web Developer.

Se si prova ad lanciare in esecuzione una applicazione ASP .NET 2.0, ogni pagina è compilata “al volo” su richiesta (comportamento di default), e per ogni pagina richiesta viene generato un singolo assembly a cui viene attribuito un nome “non user-friendly”. Questo provoca pochissimi vantaggi, anzi, a mio avviso uno solo, e gravi svantaggi. A questa conclusione ci sono arrivato dopo parecchi mesi di utilizzo di Visual Studio 2005, praticamente dalle versioni beta, a dispetto di un iniziale entusiasmo per il nuovo modello di compilazione. Infatti, in apparenza basta far puntare VS ad una directory del file system che contiene una web application e lanciarla in esecuzione per vederla nel browser senza una sua preventiva compilazione. Non solo, è anche possibile modificare il sorgente della pagina mentre la stessa è in esecuzione, salvare la pagina per vedersi applicate immediatamente le modifiche al codice sorgente senza una ricompilazione (questo piccolo apparente “miracolo” è dovuto al fatto che ogni pagina è compilata in un assembly separato, quindi è possibile scaricare dalla memoria il singolo assembly e ricaricarlo ad ogni modifica della pagina; questo è l’unico vantaggio di cui parlavo prima) . A prima vista tutto ciò può sembrare favoloso, in realtà questo comportamento nasconde parecchie insidie. Prima di tutto affinchè questo modello funzioni è necessario copiare sulla macchina di deployment anche i sorgenti della propria applicazione, praticamente occorre replicare il contenuto dell’applicazione presente sulla propria macchina di sviluppo. Questa è, a mio avviso,una condizione inaccettabile su applicazioni pubbliche che manipolano dati sensibili o che effettuano transazioni economiche; forse è appena appena accettabile su applicazioni funzionanti in intranet che non trattano dati personali.

Inoltre, il nome dell’assemby contenente la pagina compilata è generato a run-time ed univoco, anche se viene ricompilata la stessa pagina compilata precedentemente. Questo significa che è impossibile per una pagina X mantenere un riferimento alla pagina Y, perchè la classe di quest’ultima ed il suo assembly saranno “nominati” solo a run-time con un nome non prevedibile. Questo è davvero un grande problema per coloro che usano web control caricati dinamicamente a run-time, perchè significa che non è possibile ottenere un riferimento “strongly typed” ad un web control caricato dinamicamente, ma solo un riferimento generico “UserControl”, a meno di non utilizzare il tag @Register sulla pagina (il quale forza l’assembly della pagina che contiene il tag a mantenere un riferimento all’assembly che contiene lo user control), oppure, se l’applicazione è compilata in assembly separati per ogni sua directory, occorre che la pagina che referenzia lo user control e quest’ultimo risiedano nello stesso assembly, quindi nella stessa directory fisica.

L’add-in Web Deployment Project permette di “precompilare” l’intero sito in uno o più assembly, e di assegnare a quest’ultimi nomi statici, quindi ripetibili, che permettono alla classe che contiene il codice per una data pagina di essere referenziata senza problemi da un assembly/classe esterna in quanto il suo nome non cambia ad ogni ricompilazione. Utilizzo questo add-in praticamente da sempre e sinceramente, ma è solo un parere personale su cui mi piacerebbe ascoltare esperienze di altri sviluppatori, non riesco ad immaginare una web application che non ne faccia uso, chiaramente intendo applicazioni line of business ad alta disponibilità che girano su server pubblici con un alto indice di richieste.

Virtual Path Provider

Una tra le feature più innovative di ASP .NET 2.0 è il cosiddetto Virtual Path Provider. Devo dire però che, dopo aver approfondito l’argomento grazie a questo post di Dino Esposito e da lì al link relativo all’articolo su MSDN, non riesco ancora a trovare un campo di applicazione “logico” per questa nuova funzionalità, almeno in ambienti di produzione ad alta disponibilità. Attraverso questa feature si avrà la possibilità di astrarre la risorsa fisica (file, directory) che viene inglobata nella richiesta di una pagina ASP. NET. Questa risorsa fisica in ASP .NET 1.1 deve obbligatoriamente trovarsi sul file system e da nessun’altra parte. In ASP .NET 2.0 questo non è più vero, nel senso che ogni risorsa fisica si troverà di default sul file system, a meno che non venga implementato un Virtual Path Provider personalizzato che a fronte di una richiesta di risorsa effettui un mapping della stessa su un diverso repository, es. database, file ZIP. ecc.

Insomma, potremo creare un intero sito web senza alcun file aspx presente su disco (a parte il global.asax). Sono curioso di capire quanto questa innovazione potrà davvero essere utile in scenari già di per sè complessi, quanto potrà incidere sulle prestazioni in generale e che impatto avrà su attività comuni quali il deployment e l’aggiornamento di un sito web.

Sono graditi eventuali feedback di coloro che avessero già avuto modo di implementare questa funzionalità in ambiente di produzione.

.NET Pet Shop 4.0

E’ disponibile per il download la versione 4.0 di .NET Pet Shop, una applicazione “benchmark” di esempio per confrontare le prestazioni di una applicazione ASP .NET di classe enterprise con una equivalente applicazione J2EE. La versione 4.0 è focalizzata su ASP .NET 2.0 e mostra come ottenere una applicazione robusta riducendo il numero di righe di codice sorgente necessarie.

In questa versione è possibile vedere all’opera le seguenti caratteristiche della versione 2.0 di ASP .NET e del .NET Framework:

  1. Uso del nuovo namespace System.Transaction per la gestione di applicazioni distribuite senza ricorrere a COM+, aumentando le prestazioni
  2. Uso dei generics
  3. Master Pages, Profile e Memberships
  4. Controllo Wizards
  5. Elaborazione asincrona attraverso MSMQ e System.Messaging
  6. Caching dei contenuti con dipendenza da tabelle SQL 2005