Il PathSeparator e il controllo SiteMapPath

March 22, 2008 14:09 by Admin

Il controllo SiteMapPath è assai utile per implementare un sistema di spostamento tra le pagine del sito recuperando i dati da un oggetto SiteMap, il quale a sua volta si basa su un file xml appositamente costrutito (e gestito tramite l'oggetto) che funge da mappa del sito.

Il SiteMapPath è estremamente comodo per spostarsi facilmente all'interno di un sito senza sprecare spazio e funge da punto di riferimento per individuare nel sito la posizione della pagina attualmente visualizzata.

Questo tipo di controllo visualizza un percorso gerarchico di nomi di pagine con collegamenti ipertestuali tramite il quale è possibile tornare dalla posizione corrente ai livelli superiori della gerarchia di pagine.

Il controllo SiteMapPath è costituito da nodi. Ciascun elemento del percorso viene indicato come un collegamento ipertestuale rappresentato da un oggetto SiteMapNodeItem e ogni nodo è separato dal successivo con un separatore.

Un errore che ho commesso in questi giorni, dovuto dal fatto che non avevo mai sentito la necessità di usare il controllo in questione, è stato quello di confondere il corretto separatore da utilizzare.

Il controllo SiteMapPath, infatti, espone due sistemi per inserire un separatore. La proprietà PathSeparator, di tipo stringa, utilizzabile per fornire appunto stringhe letterali come separatori, e il PathSeparatorTemplate, di tipo ITemplate, per fornire come separatore un oggetto che può essere una immagine, una label e via dicendo.

Non sò per quale ragione, ma la proprietà PathSepartor mi era sfuggita, e avevo utilizzato il template con la stringa che mi interessava utilizzare come separatore.

La cosa bella era che tutto funzionava egregiamente, ma nel momento in cui ho creato il mio Adapter personale (per evitare quelle tonnellate di codice inutile generato dal controllo base), mi ritrovavo al posto della mia stringa il simbolo dei due punti. E non riuscivo a comprendere il perchè, fintanto che aprendo un ticket - pensando ad un bug - sul sito Microsoft, dopo diversi giorni ricevo una risposta circa la mia errata implementazione.

Il bug è stato chiudo come non fixed; effettivamente parte dell'errore era mio. Ma nessuno mi ha spiegato però perchè la stringa - che non doveva essere correttamente visualizzata - senza adapter invece veniva rappresentata così gliela fornivo e per giunta perchè, invece di mostrare il carattere di default minore, venivano mostrati i due punti.

Misteri della programmazione ASP.Net!

Nota: il PathSepartorTemplate ha priorità rispetto alla proprietà PathSeparator. Questo significa che se entrambe vengono impostate, quello che verrà mostrato sarà il contenuto del Template.

 

Technorati Tags: ,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Testare un HttpHandler in ClassicMode

February 29, 2008 15:53 by Admin

Windows Vista è ormai uscito da più di un anno; la curiosità insita nei programmatori avrà quasi certamente preso il sopravvento e, molto probabilmente, avrete installato Windows Vista.

Se non la avete fatto, peggio per voi ... e potete pure tralasciare la lettura di questo post perchè non vi serve assolutamente a niente.

Se invece avete installato Vista vi ritrovate con IIS aggiornato alla versione 7, che devo dire è veramente un gran bel gioiellino.

Non mi dilungo molto sui cambiamenti del nuovo web server Microsoft, sarebbero fuorvianti. Mi limito invece a quelle che sono le caratteristiche che ruotano attorno a quanto sto per scrivere. gli HttpHandler.

Un HttpHandler è del codice che - in base all'estenzione del file richiesto - si preoccupa di catturare la richiesta dalla coda di gestione ed eseguire / parsare il file secondo quanto l'handler è stato programmato, quindi restituirne l'output verso il context richiedente.

E' necessaria una premessa, che già il titolo dovrebbe portare all'attenzione dei programmatori più attenti. Mi riferisco alla modalità di esecuzione del worker process - ClassicMode - sopra menzionato.

Con l'introduzione di IIS 7, infatti, il nuovo server web ha subito dei profondi cambiamenti architetturali che in sintesi oggi ci offrono due modalità di esecuzione per l'Application Pool: il ClassicMode e l'Integrated Mode.

Fintanto che Windows 2008 non sarà ufficialmente commercializzato e adottato dagli hosting provider o installato sulla nostra macchina di produzione, purtroppo per noi il server web disponibile è ancora IIS6, che prevede solo la modalità ClassicMode.

Ne consegue che, allorquando decidiamo di sviluppare un nostro HttpHandler, e ovviamente lo testiamo con IIS 7, laddove avessimo la necessità di verificare che effettivamente il componente appena creato possa correttamente funzionare anche su IIS 6, prima di passarlo in produzione, dobbiamo testarlo nella modalità sopracitata.

 

Come fare?

imageSe abbiamo testato il componente su un sito web che girava in un Integrated Application Pool, dobbiamo per prima cosa dire al nostro sito di usare un Application Pool configurato con il ClassicMode.

Ovviamente non è tutto qui, magari.

Nella stragrande maggioranza dei casi, dato che con l'handler sarete andati a gestire una particolare vostra estenzione (ma questo succede anche se avete utilizzato una estenzione nota), mentre con IIS 7 è sufficiente mappare l'estenzione verso il vostro handler, con IIS 6 dovrete prima mappare l'estenzione sulla quale state lavorando affinchè passi per l'aspnet_isapi.dll (diversamente sarà IIS a prendersi in carico la richiesta e non sapendoci che fare con la vostra etenzione non mappata probabilmente vi restituirà una 404).

Sarà poi compito di Asp.Net fare il check dei vari handler installati e dirottare internamente la richiesta verso l'handler più appropriato.

imageE infine apportare una modifica al nostro file web.config per dire che quella determinata estenzione dovrà essere gestita dal nostro Handler (che dovrà, per IIS6 essere necessariamente un file compilato e messo nella Bin del nostro sito).

Con IIS 7, tutto questo non è necessario, l'Handler può essere direttamente una classe messa nella nostra App_Code e compilata al volo, che con una semplice mappatura che a questo punto cambierà di posizione (non più quindi in , ma in dove si specificherà a questo punto anche il path al quale il nostro handler deve rispondere) e tanto sarà sufficiente affinchè l'evento HttpApplication.MapRequestHandler possa dirottare la richiesta verso il nostro handler.

 

Approfondimenti

Per futura memoria di chi legge, ma anche per lo scrivente, lascio alcuni link utili sugli handler e sul life cycle di IIS 6 e 7:

  1. ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
  2. ASP.NET Application Life Cycle Overview for IIS 7.0
  3. Introduction to HTTP Handlers

Se vi trovate invece in panne con la migrazione di un Handler da IIS6 a ISS7, nel blog di Rick Strahl ci sono un paio di post interessanti:

  1. Migrating an ASP.NET app to IIS 7
  2. HttpModule and HttpHandler sections in IIS 7 web.config files

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

L'errore 404 in ASP.Net e IIS

February 27, 2008 11:38 by Admin

Dopo un precedente articolo sull'errore 404 lato SEO, oggi parlo dell'errore 404 lato ASP.Net e IIS. Il perchè bisogna configurarlo l'ho spiegato nell'articolo sopra citato, come configurarlo nell'ambiente Microsoft lo faccio ora.

Esistono due modi, complementari, per configurare l'errore 404 in ASP.Net /IIS6. Complementari perchè uno non esclude l'altro.

imageData l'architettura del framweork, una volta che la richiesta giunge al server, questa a seconda dell'estenzione che il file richiesto ha, può subire un dirottamente all'interno della pipeline.

I file statici, quindi i file .htm e .html (ma anche i .css e tutte le immagin) verranno smistati automaticamente verso l'handler che si preoccupa di gestire i file statici (StaticFileModule), mentre i file con estenzione .aspx verranno gestiti dall'handler aspnet_isapi.

imageDati questi due percorsi differenti, ne consegue che qualora venga inoltrata una richiesta per una risorsa non esistente sul server di destinazione, la risposta 404 procederà per due vie completamente distinte.

Quella dei file statici verrà inoltrata direttamente ad IIS, il quale ritornerà al client la pagina associata al codice di errore 404 configurata all'interno della proprietà del sito.

Di default viene restituita una pagina brutta e asettica come quella che si vede qui sotto, ma agendo nelle proprietà di configurazione del sito si può impostare un qualsiasi tipo di risorsa, anche appartenente ad un altro sito web.

image

Nel caso di una risorsa .aspx non trovata, le cose sono leggermente diverse.

A preoccuparsi di restituire una pagina d'errore è lo stesso framwork.

Ovviamente, di default, la gestione delle pagine non trovate non è abilitata, e nel caso viene mostrata una pagina d'errore (per alcuni) più criptica e sempre antiestetica.

 

image

 

Per poter abilitare la gestione delle pagine non trovate, in questo caso bisogna agire all'interno del file Web.Config, andando a creare, laddove non esista già, una sezione CustomErrors.

 

Un esempio può essere come quello qui sotto:

<customErrors mode="On">
    <
errorstatusCode="404" redirect="404.html" />
customErrors>

In questo caso, il framework reindirizzerà la richiesta verso il file statico 404.html. Nulla vieta di mettere una pagina con estenzione .aspx che faccia del lavoro per noi, tipo mandare une e-mail all'amministratore del sito, piuttosto che registrare degli eventi in un file di log, ecc.

Medesimo discorso vale anche per la configurazione del Custom Error statico. Impostando una risorsa di tipo URL si può specificare un percorso assoluto e rimandare il tutto ad una pagina dinamica, con il vantaggio che si potrà creare una sola pagina e gestire in entrambe i casi il problema della risorsa non trovata.

 

 

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Cambio della piattaforma di blogging

February 26, 2008 19:53 by Admin

Dopo circa un anno di uso di SubText ho deciso di passare a miglior vita spostando tutti miei vecchi post sulla piattaforma DotNetBlog?

Perchè? Semplice ogni tanto bisogna cambiare, ma in primis perchè ormai SubText puzza di stantio, non vengono rilasciati aggiornamenti, è piuttosto chiusa come architettura, e non per ultimo un problema in fase di commentazione che un amico mi ha portato a far vedere sabato scorso, dove con IE non c'era verso di lasciare commenti (mi beccavo sempre un errore 500 che non sono riuscito a comprendere il perchè neppure guardando il log file).

Insomma Sabato notte ci ho fatto le 2:30 per fare la migrazione, perchè sfortunatamente la procedura di export dei post da SubText su formato BlogML non riusciva ad essere importata in DotNetBlog in maniera corretta.

Sono dovuto andare di query a manina!! Un lavoraccio pazzesco.

Nonostante avessi finito, non potevo rilasciare subito la nuova versione. C'erano alcune cose da fare ancora. Cose che in parte si sono sistemate dopo un riavvio del server. Per esempio l'errore 404 non veniva trappato e rispedito alla pagina aspx che se ne occupava, non c'era continuità con i link perchè l'autore di DotNetBlog per quanto ha detto di aver fatto un Url Rewriter in grado di catturare anche i vecchi link per SubText, in realtà io nel codice non vi ho visto traccia.

Insomma, tolta Domenica e tutto la giornata di ieri, oggi con qualche ora ho messo apposto quelle due o tre cose per farlo funzionare come mi aspettavo.

Come piattaforma, se facciamo un paragone con SubText, quella di DotNetBlog è per certi versi ancora indietro; ci sono molte molte cose che non mi piacciono, una per tutte per esempio l'impossibilità di avere più blog sotto lo stesso sito web.

Manca una link section, la gestione dei referer è veramente indecente. Insomma come tutti i prodotti c'è del buono e del cattivo.

Ma la cosa che più mi piace è l'archittetura estendibile che lo sviluppatore ha ideato, attraverso la quale si può scrivere un proprio componente, deployarlo dentro la cartella Extensions e il blog può utilizzare questa nuova caratteristica appena installata.

Apprezzo molto anche il migliore supporto della MetaWebLog API (quella che si usa con LiveWriter per capirci) che sotto SubText era veramente scarsa (niente aggiornamento della data di pubblicazione, niente categorie aggiungibili, ecc. ecc.)

Concludo con una ulteriore nota positiva per questo prodotto, al quale sembrano interessati molti bloggers che contribuiscono con idee e suggerimenti su CodePlex nel quale si trovano anche i sorgenti che il suo autore sembra mantenere con una discreta frequenza.

A parte tutto questo, mi sento però nel dovere di ringraziare l'autore di SubText, per avermi dato la possibilità tramite il suo prodotto di gestirmi il mio blog.

 

Technorati Tags: ,

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Le risorse che non ci sono più e l'URL forwarding

February 21, 2008 21:48 by Admin

Quando si lavora ad un sito web può capitare di dover spostare una risorsa. In questi casi non c'è niente di meglio che dire a tutti quello che stiamo facendo, implementando un forwarding verso la nuova risorsa, indicando quindi al client che ne ha fatto richiesta che è avvenuto questo cambiamento. In parole povere, il server non dovrà far altro che caricare un'altra risorsa invece che quella originariamente richiesta dal client.

Se infatti non si procedesse con un forwarding verso una nuova risorsa, una richiesta da parte del client verso un URL non esistente ritornerebbe al client un codice di errore che sta indicare che la risorsa non è stata trovata. Questo codice il famoso 404, pagina non trovata.

Come procedere in questi casi?

Esistono tre modi per fare l'URL Forwarding in ASP.Net

  1. Il Response.Redirect, che probabilmente i vecchi programmatori Classic ASP ricorderanno bene
  2. Il Server.Transfer
  3. Il Responde.RedirectLocation

Vediamoli nel dettaglio.

Il Response.Redirect è un metodo che il framework si trascina dietro sin da classic ASP 2.0 (a mia memoria, non ricordo l'1.0 se lo aveva, ma credo di ricordare di no). Con questo metodo, semplicemente si prende la richiesta corrente dalla pipeline e la si sposta su una nuova richiesta. La nuova richiesta (risorsa) potrà essere tanto una risorsa interna che una risorsa esterna. Quest'ultimo aspetto è da tenere in considerazione perchè ad esempio il metodo Server.Transfer non supporta reindirizzamenti esterni.
Tornano al nostro Redirect, un utilizzo intensivo di questo sistema è degradante, perchè questo discorso di aprire una nuova richiesta porta ad un overhead del server che alla lunga vedrà un calo di prestazioni. Chiaramente ci vogliono scenari distribuiti piuttosto complessi per potersene rendere contro.
Per questo motivo, con il framework 1.0 questo metodo è stato corredato di un overload che comprende un parametro boolean che serve ad indicare al metodo se persistere il thread corrente e lasciare che continui la sua esecuzione sebbene la pipeline sia stata dirottata o meno verso una nuova pagina.
In entrambe i casi, per via del fatto che all'interno del codice del framework viene fatta una chiamata al metodo Response.End, viene comunque sollevata una eccezione gestita dal framework, che non scatenerà errori nella vostra pagina, ma sarà sempre una sorta di anomalia.

Morale, evitare il Response.Redirect se proprio non se ne può fare a meno (Redirect verso un'altra risorsa). Utilizzare invece il metodo Server.Transfer, assai più performante. Anche il Server.Transfer è roba vecchia, nel senso che venne introdotto già a partire da Classic ASP 3.0, anzi fu una delle novità principali che io ricordi.
Il funzionamento è quasi simile al Redirect, per lo meno visivamente sembra uguale. La differenza sta nel fatto che mentre la Redirect richiede una nuova pagina, la Server.Transfer richiede l'esecuzione della risorsa e la restituisce sulla pipeline originale.
Attenzione al fatto dell'eccezione; se questo davvero vi preoccupa, sappiate che anche il Server.Transfer solleva anch'egli l'eccezione del Response.Redirect, perchè internamente fa una chiamata al metodo Response.End.

L'utilizzo di Response.End, Response.Redirect e Server.Transfer sollevano una ThreadAbortException. E' un prolema noto documentato dalla KB312629

Se davvero non volete avere di questi problemi dovete usare il metodo Server.Execute, che è lo stesso poi richiamato dal Server.Transfer, con la differenza che il Server.Execute non chiama alcun Response.End e vi restituisce il risultato dell'esecuzione della pagina chiamata, ovvero l'esatto output che avreste chiamando direttamente quella determinata pagina dal primo fino all'ultimo (ammesso che vi siano).

Sembrerebbe il metodo migliore, tuttavia vi sono casi specifici in cui il Server.Transfer non può essere utilizzato. Non sono in grado di elencarli tutti, ma per esempio uno lo posso documentare.

Oggi stato scrivendo un HTTPHandler per gestire un sito web che deve subire un processo di transizione da statico a dinamico, e facendo alcuno prove, mi sono reso conto che il Server.Transfer in questo processo di trasferimento parte dall'assunto che l'oggetto Page, con tutto quello che ne consegue, sia stato già correttamente caricato dal context corrente. In realtà, nel mio caso, agendo ancor prima che l'oggetto Page fosse stato creato, l'oggetto Session nel mio caso non esisteva, e ne risultava che la pagina a cui trasferivo la chiamata, facendo uso della Session mi generava un errore.

Non entro del merito del perchè. Magari un errore mio che comunque esula da questo post che si limita all'URL fowarding.

Come risolvere? Semplice, con un Response.RedirectLocation che non c'entrerebbe niente di per se con un trasferimento automatico verso una nuova risorsa, e infatti il suo compito è solamente quello di impostare l'HTTP Location header della pagina su di un nuovo percorso.

Senza fare null'altro questo sistema non serve a niente (non in questo caso di URL Forwarding). Se però aggiungiamo immediatamente dopo una impostazione dello StatusCode, cioè alteriamo il codice restituito al client, che normalmente sarebbe 200 - Ok, e lo impostiamo su un 302 - Risorsa mossa temporaneamente o un 301 - Risorsa mossa definitivamente, quando questo codice torna al client, il fatto che sia specificato un nuovo path, porterà il client ad effettuare una nuova richiesta verso la risorsa indicata.

Il tutto con una grande differenza, il thread precedente sarà stato correttamente chiuso, nessuna eccezione sarà stata generata.

Un esempio per essere più concreti:

context.Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
context.Response.RedirectLocation = "default.aspx";

A questo punto scegliere il sistema migliore è solo questione di analisi e capire quale sia veramente il sistema più adatto alla specifica esigenza sulla quale si sta lavorando.

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Errore durante il debug di componenti server a design time

February 12, 2008 11:29 by Admin

Per il debug di componenti lato server, sia in VS 2005 che in VS 2008, ci si deve attaccare con il debug all'istanza corrente del progetto sul quale si sta lavorando.

Tralasciando il come fare, che non è oggetto di questo post, può succedere talvolta che si incappi nel seguente error:

Managed Debugging Assistant 'LoaderLock' has detected a problem in 'filename.exe'. Additional Information: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.

Per risolverlo, per VS 2005 occorre installare la SP1, mentre per VS 2008 la fix dovrebbe essere già integrata. Parlo al condizionale perchè non avendo più una versione di 2005 sulla quale verificare prima e dopo la SP1, non sò se la seguente soluzione (testata per VS 2008) valga prima della SP1 di VS 2005.

Comunque occorre entrare nel menù delle eccezioni del debug (CTRL+ALT+E per fare prima) andare sotto la voce Managed Debugging Assistants e deselezionare il thrown dell'eccezione per il LoaderLock.

Technorati Tags: ,,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Valori di default per un ASP.Net WebControl

February 11, 2008 15:17 by Admin

Nella scorsa settimana mi sono trovato di fronte alla necessità di dover creare un WebControl per ASP.Net al fine di incapsulare una porzione di codice che avrei sicuramente potuto condividere tra più progetti, e la scelta di utilizzare un WebControl era quanto mai sensata.
Al di là dello scopo ultimo del codice, un'aspetto che mi ha tenuto bloccato per alcuni giorni è stato quello di trovare un sistema per dare un valore di default ad alcune proprietà del controllo stesso.
La soluzione in fondo è semplice, il problema è stato l'arrivarci perchè come al solito ci si perde di fronte al gran numero di classi e di override che il .Net mette a disposizione.

Si potrebbe dire semplicemente, come in effetti lo è, che basta aggiungere qualche riga di codice nel Page_Load della pagina che contiene il controllo, con un paio di if controllare se la pagina è appena caricata, oppure se è un PostBack e si agisce di conseguenza. Vero.
Per un attimo però mi sono immedesimato nei panni di un altro sviluppatore che avrebbe potuto usare il mio controllo. Magari uno sviluppatore alle prime armi, che non conoscendo il mio controllo, semplicemente lo trascina nella web page e imposta qualche proprietà, magari dimenticandone qualcuna necessaria al corretto funzionamento del controllo stesso, con l'inevitabile conseguenza che il controllo non funziona a dovere.

Chi conosce gli attributi potrebbe in un primo momento pensare di utilizzare l'attributo DefaultValue. Tuttavia questo attributo è solo una parte di questa soluzione.

   Gli attributi forniscono un potente sistema per associare in maniera dichiarativa delle informazioni al codice .Net.

Infatti il DefaultValueAttribute si preoccupa esclusivamente di associare un valore di default alla proprietà. Sembrerebbe quello che fa al caso nostro, ma sfortunatamente quello che non è scritto nell'MSDN è che il comportamento di questo attributo è limitato all'uso con il solo designer. Meglio ancora il DefaultValueAttribute si preoccupa di:

  • Rendere disponibile una funzione di reset in caso in cui il valore della PropertyGrid sia diverso da quello contenuto in questo attributo;
  • Evitare a Visual Studio di serializzare il valore nel markup code quando la proprietà contiene la stessa impostazione dell'attributo;
  • Indica al controllo propertygrid del designer di scrivere in carattere standard (non grassetto) il valore della proprietà quando questa è identica al valore dell'attributo;

Quindi per capirci, l'attributo DefaultValue non ha nulla a che vedere con il senso più generale della parola Default, che lascia immaginare che impostato quell'attributo, la proprietà del controllo alla quale l'attributo è associato in fase di inizializzazione sappia di dover assumere tale valore (Per una soluzione tipo questa, si potrebbe ricorrere ad un condice come quello che ho mostrato in questo post sul DefaultValue di un controllo).
Il controllo, infatti, non sà assolutamente nulla di quel DefaultValue attribute, e quando viene inizializzato, le sue proprietà assumeranno - fatto salvo diverso codice - il valore di default del tipo esposto. Significa che una stringa conterrà uno string.empty, un integer avrà valore 0 e così via.

Si tratta quindi di un problema di inizializzazione?

La cosa potrebbe a questo punto essere risolvibile mettendo questo nostro valore di default all'interno del costruttore della classe che costituisce il nostro web control, ovviando così ad ogni genere di problema. Vero anche questo.

Ma la domanda che voglio porre a questo punto è: e se io volessi rendere libero lo sviluppatore di poter cambiare questi valori di default?

Anche qui la risposta sarebbe "Il tuo sviluppatore semplicemente usando la Page_Load ..."; come dare torto a questa affermazione?

Veniamo però al lato pratico: fare un hard-coding dei valori delle proprietà necessarie all'interno del costruttore (Punto 3), significa un millesimale degrado di performance laddove il programmatore a sua volta, con il Page_Load imposta nuovamente i suoi valori di default per quel dato controllo (Punto 4). imageQuesto succede perchè al momento del caricamento della pagina l'ASP.Net Page life cycle funziona così (tralasciando le parti che non ci interessano):

  1. La pagina viene richiesta
  2. La pagina viene inizializzata
  3. I controlli interni vengono istanziati
  4. I controlli vengono inizializzati 
  5. La pagina viene caricata

Fosse tutto qui il problema, uno ci potrebbe anche sorvolare sopra. Io volevo comunque dare la possibilità al mio controllo di persistere visivamente questo mio valore di default, cioè fare in modo che usando il designer e la property grid, qualora io imposto una certa proprietà questa mi venga in automatico scritto nel markup code del controllo contenuto nella pagina web.

In effetti questo già avviene, ma con una eccezione riferita all'attributo DefaulValue. Nel caso in cui il nostro web control sia stato appena creato, quindi non abbiamo ancora utilizzato l'attributo, ad ogni modifica delle proprietà tramite la property grid corrisponde la serializzazione del valore nel markup code.

Laddove però si sia utilizzato l'attributo, qualora la proprietà abbia il DefaultValue con lo stesso identico valore impostato nella property grid, il designer ritiene opportuno non serailizzare più il valore e anzi di cancellarlo completamente.
Tradotto questo cosa vuol dire? Che al momento in cui la nostra pagina verrà eseguita, non essendoci del markupcode, questo non verrà tradotto nel corrispettivo codice di inizializzazione e la nostra proprietà varrà niente (stringa vuote, 0, ecc.). Ecco allora il nuovo dilemma.

Come persistere allora il valore di default?

Guardando sempre tra gli attributi, con un pò di pazienza, se ne scorge uno che permette di influenzare il modo in cui il designer tratta il nostro controllo. Si tratta dell'attributo Designer del namespace System.ComponentModel (Per una lista descrittiva di tutti gli attributi potete guardare qui). Sembra quello che fa al caso nostro. Tant'è che in precedenti controlli WinForm ho avuto modo di utilizzarlo per alterare la lista delle proprietà in fare di PrePosting, quindi la classe non mi era affatto nuova.

Dentro ai metodi di cui era possibile fare l'override ne scorgo uno che fa al caso mio: InitializeNewComponent(System.Collections.IDictionary defaultValues), il cui abstract dell'MSDN recitando "Inizializza un componente appena creato" lasciano proprio intendere che questo è quello che serve.

Sulle prime non riesco a trovare alcun esempio per ASP.Net, ma solo per WinForm, ma l'analogia delle classi che ereditano dallo stesso namespace mi invita a provare il medesimo codice, che ovviamente non funziona. Provo dalle cose più semplici a quelle più astruse. Fino a quando apro un incident MSDN per scoprire - sottoponendo tutto questo caso - con l'excalation engineer (quindi un livello sopra il tecnico che ti segue con la prima fase) che questo metodo non funziona.
Anzi, secondo filosofia Microsoft - dice l'ingegnere - tutto questo discorso del DefaultValue, almeno per come l'ho inteso io, non si doveva neanche fare, perchè questa filosofica recita che i controlli devono essere più Smart e più semplici possibili, ovvero dall'esempio che mi ha fatto questo ingegnere già un è troppo.
Continua poi l'ingegnere dicendo che non essendo necessaria alcuna forma di inizializzazione via markup perchè non tutti i programmatori potrebbero utilizzare Visual Studio come designer, sarebbe del tutto inutile far funzionare un metodo del genere.
Può o meno essere condivisibile questa affermazione. Anzi, aggiungo, se davvero è inutile, perchè non mettere tale classe come sealed? Lasciarla è un puro invito a perdere tempo.

Finalmente la soluzione

Giusto per riassumere, visto che è assai facile che leggendo si sia creata un pò di confusione. Quello che volevo ottenere era:

  1. Al trascinamento del controllo nella web page volevo che automaticamente nel markup code venissero scritte quelle che io ritenevo le mie proprietà di default con un valore x;
  2. Utilizzando l'attributo DefaultValue, volevo che in caso di match tra valore attributo e valore proprietà, il markup code rimanesse li dove era;

In merito alla soluzione per il punto, visto e considerato l'insuccesso della classe Designer, lo scopo è perfettamente raggiungibile tramite l'attributo ToolBoxData.
Questo attributo indica al designer di interpretare il suo contenuto ogni qual volta che un controllo viene trascinato sulla web form e di utilizzare parte del suo contenuto per comporre il markup code dentro la web page. Dentro a questo attributo semplicemente si scrivono le proprietà che si vuole facciano parte del markup code, quindi quel valore di default visibile che stavo cercando.

Sembrerebbe tutto risolto, ma non è così. Questo giochino infatti funziona bene solamente se decoriamo le nostre proprietà con un ulteriore altro attributo che l'ingegnere ha portato alla mia attenzione.
Si tratta dell'attributo DesignerSerializationVisibility, che una volta impostato con l'enum su DesignerSerializationVisibility.Visible, indica al designer di persistere il valore della proprietà sempre e comunque, anche quando il valore di default dell'attributo corrisponde a quello della proprietà.
Questo risolve un secondo problema, quello del DefaultValue Attribute che cancella il markupcode in caso di corrispondenza tra valore attributo e valore proprietà.

Con questa soluzione si ottiene finalmente un vero Default Value per le proprietà dei vostri web control.

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

La persistenza delle proprietà di un WebControl senza utilizzare il Postback

February 5, 2008 21:16 by Admin

Chi programma in ASP.Net sin dalle prime versioni ha iniziato da subito ad apprezzare e contemporaneamente ad odiare lo StateBag delle pagine, altrimenti chiamato ViewState. Senza scendere troppo nei particolari, altrimenti andrei fuori tema, il ViewState non fa altro che serializzare il contenuto delle variabili di una pagina all'interno di una stringa che si passa attraverso le successive richieste mediante postback.

Il dramma del ViewState è per l'appunto che magari pur non necessario per tutti i controlli (anche annidati) è necessario mantenerlo attivo affinchè questo processo di persistenza possa funzionare. Questo significa il più delle volte trasmettere una enorme quantità di dati da e verso il server, che non tutti gli scenari possono supportare.

Nasce così, con il framework e ASP.Net 2.0 un nuovo metodo di persistenza definitico ControlState, che funziona sullo stesso principio del ViewState, ma a differenza di quest'ultimo è indipendente, non è disattivabile da parte dell'utente e memorizza solo lo stretto necessario (in virtù chiaramente degli override che compiete nel vostro codice), ma soprattutto non vi obbliga ad avere un ControlState attivo per ogni controllo. Se ne deduce che si può addirittura disabilitare completamente il ViewState e poter contare sempre sulla persistenza delle variabili di un dato controllo.

Tutto questo è vero, ma solo in parte!

Allora vi starete chiedendo, ma se non è vero, perchè diavolo lo scrivi. Ho detto che non è vero solo in parte. Per poter funzionare, tanto ViewState che ControlState, devono abbracciare la tecnica del PostBack, cioè tramite una funzione Javascript iniettata dal framework nella pagina, i valori verranno ritrasmessi alla pagina stessa e riacquisiti per successive elaborazioni.

Supponiamo ora per un momento che - come nel mio caso - nella costruzione di un controllo avessi come limite il fatto di non dover usare funzioni Javascript (tralasciamo il perchè). Cosa succede? Niente Javascript = Niente PostBack. E allora?

Per ovviare a questo tipo di problema ho dovuto far ricorso ad un sistema alternativo di persistenza, basato sulla serializzazione dei miei dati direttamente in memoria tramite l'oggetto Session della pagina ASP.Net

La serializzazione e` il processo di conversione dello stato di un oggetto in un form che puo` essere mantenuto o trasportato.

Così facendo, con un paio di if all'interno del mio codice, e ricorrendo allo stesso flusso di esecuzione generato quando si usa il ControlState sono riuscito a persistere i valori del mio controllo senza far ricorso al Postback.

Di seguito mostro alcuni stralci di codice.

Per prima cosa occorre creare una classe che sia serializzabile, che serve tanto che vogliate usare il ControlState che questo sistema alternativo, quindi:

[Serializable]
internal struct PersistedData
{
 
public int test;
}

Occorre poi fare il controllo se si vuole usare il ControlState, e questo lo faremo nel metodo OnInit

 if (PagingMode == PagingModeEnum.Postback)

  Page.RegisterRequiresControlState(this);
else
 
LoadControlStateInSession();

Aggiungere la persistenza del metodo alternativo nel metodo PreRender. Non è necessaria quella del ControlState perchè li siete obbligati a fare - per far si che funzioni - un ovverride del metodo SaveControlState.

if (PagingMode != PagingModeEnum.Postback)
 
this.SaveControlStateInSession();

Infine incollare il codice relativo alle due funzioni di persistenza sopra utilizzate.

///
/// Saves state the specified storage mechanism by
/// first serializing to a string with the LosFormatter
///

private void SaveControlStateInSession()
{
 
LosFormatter output = new LosFormatter();
 
using (StringWriter writer = new StringWriter())
  {
   
output.Serialize(writer, this.SaveControlState());
   
HttpContext.Current.Session["__" + this.UniqueID] = writer.ToString();
  }
}


///
/// Retrieves the serialized data from the Storage medium
/// as string using LosFormatter formatting.
///

private void LoadControlStateInSession()
{
 
string RawBuffer = HttpContext.Current.Session["__" + this.UniqueID] as string;
  if (RawBuffer == null)
   
return;

  LosFormatter input = new LosFormatter();

  this.LoadControlState(input.Deserialize(RawBuffer));
}

Le due funzioni si appoggiano alla classe LosFormatter, una classe nascosta nei meandri del System.Web.UI, utilizzata dal framework per serializzare il contenuto del ViewState nel famoso campo hidden __VIESTATE.

Nota del 20/01/2008: If you want to read the article in english, please follow the How to persist a control property without using PostBack link.

 

 


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5