gennaio 2006 Blog Posts
Dopo alcune serate di lavoro, e svariati test è finalmente
pubblico il sito della community del triveneto XE.NET. Un
ringraziamento anche ad Andrea Dottor e a Marco ".mark" Trova
per avermi supportato nelle lunghe nottate passate a lottare contro
CommunityServer.
Il sito espone un numero limitato di servizi. Abbiamo un paio
di feeds che useremo per informare gli iscritti a proposito delle notizie sulla
community e sulla tecnologia .NET e un utile Roller presso il quale abbiamo
aggregato alcuni feed degni di nota. Al momento non sono previsti altri servizi,
ma contiamo di potenziare quanto prima il sito aggiungendo una sezione tips, non
appena se ne manifesterà la necessità.
Da ieri sera infine, il sito accetta iscritti; chiunque volesse
può registrarsi.
powered by IMHO 1.3
Stamane ho fatto una scoperta che definirei
sconcertante. La semplice lettura della proprietà Controls, in alcuni WebControl
di ASP.NET causa notevoli malfunzionamenti al ciclo di vita della pagina. In
particolare stavo utilizzando il metodo di cui ho parlato in un post precedente, per
cercare dei controlli all'interno della gerarchia della pagina. Questa ricerca,
se eseguita all'interno dell'OnInit, fa si che alcuni controlli non manifestino
più gli eventi tipici. Ad esempio, un LinkButton non notificava più il "Click",
e una FormView non era più in grado di gestire il postback
correttamente.
Dopo una lunga estenuante ricerca, sono riuscito
ad individuare la riga che era origine del problema, e con mio stupore mi sono
reso contro che in quel punto non facevo altro che leggere la proprietà Controls
per enumerare i controlli figlio. C'è voluta una breve indagine on il Reflector
per arrivare ad una possibile spiegazione. La proprietà Controls non legge
semplicemente il valore di un field, ma si occupa anche di inizializzare la
collection nel caso in cui essa non sia già popolata; ecco il codice estratto da
reflector:
public virtual ControlCollection Controls
{
get
{
if ((this._occasionalFields == null) ||
(this._occasionalFields.Controls == null))
{
this.EnsureOccasionalFields();
this._occasionalFields.Controls =
this.CreateControlCollection();
}
return this._occasionalFields.Controls;
}
}
Come appare chiaro questo codice si occupa di istanziare una
ControlCollection di default qualora essa non sia già presente. A quanto pare
questa inizializzazione causa problemi se effettuata esternamente. Suppongo
che da qualche parte, all'interno del ciclo di vita di un chiamata, esista una
porzione di codice che verifica se tale proprietà è già inizializzata
e si comporta di conseguenza in modi del tutto diversi. In effetti, a ben
guardare la classe Control espone un metodo che compie questa verifica e che
torna utile per aggirare questo malfunzionamento. Si tratta di HasControls(), un
metodo che restituisce false se la proprietà non è inizializzata. Evidentemente
in Microsoft il problema ho avevano previsto, e quindi hanno fornito un metodo
per aggirarlo. Ecco quindi come dovrebbe essere modificato il codice
dell'esempio precedente:
public static T FindControlRecursive<T>(Control root, string id)
where T : Control
{
if (root.ID == id && root is T)
return root as T;
if (root.HasControls())
{
foreach (Control child in root.Controls)
{
T foundControl = FindControlRecursive<T>(child, id);
if (foundControl != null)
return foundControl;
}
}
return default(T);
}
In questo modo non si modificherà lo stato del controllo. Rimane
semplicemente da rilevare che si tratta di un comportamento decisamente subdolo,
che andrebbe affrontato e corretto. Probabilmente sollevare un'eccezione sarebbe
stato preferibile. A me è costato 3 ore di lavoro. Spero che a voi vada
meglio.
powered by IMHO 1.3
Un esperienza pratica di lavoro con WebServices, applicazioni
legacy, e Framework .NET mi ha suggerito l'argomento dell'articolo che questa
settimana è apparso sulle "colonne" di UgiDotNet. L'articolo dimostra come,
sfruttando gli strumenti del framework 2.0 - mi riferisco a reflection e
generics - si possa abbattere notevolmente il tempo di sviluppo di una
applicazione. Non solo, l'implementazione è anche un bell'esempio di
design, che grazie ad alcuni accorgimenti ha reso flessibile ed adattabile
a future esigenze il codice.
Tenete presente che questo lavoro ci ha consentito di evitare
la realizzazione di una cinquantina di convertitori ad-hoc che avrebbero
richiesto un tempo esponenzialmente più elevato. Un ringraziamento al mio
collega Roberto che è stato il primo a sentire l'esigenza di rendere parametrico
in qualche modo il lavoro che stava facendo, stimolandomi nel trovare la
soluzione che ora potete leggere.
Link: http://www.ugidotnet.org/articles/articles_read.aspx?ID=109
powered by IMHO 1.3
Chi usa ASP.NET conosce il metodo FindControl() che consente di
trovare un controllo all'interno del proprio genitore. Sa sicuramente anche che
questo metodo non è ricorsivo, ma si limita al primo livello e di solito si usa
scrivere un metodo apposito che implementa questa ricorsività. In rete se ne
trovano parecchi esempi ma voglio proporvene uno che ho scritto stamane per
eseguire ricerche mirate ad uno specifico tipo di controlli:
public static T FindControlRecursive<T>(Control root, string id)
where T : Control
{
if (root.ID == id && root is T)
return root as T;
foreach (Control child in root.Controls)
{
T foundControl = FindControlRecursive<T>(child, id);
if (foundControl != null)
return foundControl;
}
return default(T);
}
L'uso di un generics in questo caso consente di specificare il tipo
di controllo da ricercare nella gerarchia, semplificando di molto la stesura del
codice. Se poi avete l'ardire di togliere il constraint allora scoprirete che è
possibile anche cercare un controllo che implementa una determinata
interfaccia...
Lo confesso,
mi sto innamorando. Un amore un po' generics, ma sempre di amore
si tratta...
powered by IMHO
1.3
Nel framework 2.0 è presente una nuova interfaccia ITextControl, il cui compito è molto semplice, almeno
quanto utile per chi sviluppa webcontrols. ITextControl identifica i controlli che espongono una
proprietà Text e che quindi sono utilizzabili per "mostrare un
output all'utente".
Implementano quindi ITextControl tutti quei controlli che
avendo una proprietà Text possono consentire di mostrare qualcosa all'utente.
Literal, Label, TextBox sono tutti controlli ITextControl. Tuttavia vorrei che
qualcuno mi spiegasse perchè LinkButton e Button non implementano questa
interfaccia. Questi ultimi implementano IButtonControl, che a sua volta espone
la proprietà Text, ma era cos' difficile fare in modo che IButtonControl
derivasse da ITextControl?
Pare una stupidaggine, ma forse non avete idea della quantità e
qualità di codice che questo avrebbe consentito:
Control textControl =
FindTextControl(container, "HeaderText");
if (textControl is ITextControl)
((ITextControl)textControl).Text = this.Columns[i].HeaderText;
else if (textControl is IButtonControl)
((IButtonControl)textControl).Text = this.Columns[i].HeaderText;
else
throw new InvalidOperationException("...");
sarebbe
diventato:
ITextControl textControl =
FindTextControl(container, "HeaderText");
if (textControl != null)
textControl).Text = this.Columns[i].HeaderText;
else
throw new InvalidOperationException("...");
A volte basta talmente poco.
powered by IMHO 1.3
L'azienda presso cui lavoro ha
la necessità di assumere una nuova risorsa senior, con il seguente
profilo:
- Programmazione ad oggetti
- ASP.NET
1.1
- In
particolare
- Pagina
aspx
- Web
User Control
- Web
Custom Control
- Autenticazione/Autorizzazione
- Localizzazione di applicazioni Web
- C#
1.1
- .NET Web
Services
- Microsoft
SQL Server 2000
- T-SQL
Rappresenta titolo
preferenziale la conoscenza di:
- ASP.NET
2.0
- C#
2.0
- Microsoft SQL
Server 2005
e le seguenti
certificazioni Microsoft:
- MCSD -
Microsoft Certified Solution Developer
- MCAD - Microsoft
Certified Application Developer
Chi fosse
interessato non ha che da contattarmi all'indirizzo riportato nel mio
blog.
powered by IMHO 1.3
Se si prova a dare alle webparts un aspetto un po' più
accattivante rispetto a quello che hanno di default ci si scontra con un piccolo
ma fastidioso bug, riconosciuto da Microsoft come tale. Le webpart,
volenti o nolenti hanno sempre un padding di 5 pixel attorno al contenuto e di 2
pixel attorno al titolo. Tale padding non è impostabile in alcun modo, ma per
stessa ammissione del team di ASP.NET 2.0 è hard-coded all'interno del
framework.
Esistono un paio di workaround suggeriti da Microsoft in ladybug, ma la loro applicazione è del tutto limitata
e difficoltosa. L'unico modo veramente valido che ho trovato - suggerito da un
utente - per superare questo problema è lo sfruttare la natura dei css.
Chiunque usi i css sa che in cascata vengono applicati prima i fogli di style
.css, poi il contenuto di <style> e infine il valore dell'attributo style
di ogni tag con il risultato che quest'ultimo ha la precedenza sugli altri e
risulta insuperabile. Detto questo è evidente che l'attributo
style="padding:5px" che si trova sul <td> che incapsula il contenuto
della webpart è pressochè impossibile da aggirare.
Esiste però una clausola nei fogli di stile che consente di
sovrascrivere questo comportamento di default. Si tratta dello switch
!important che se fatto seguire ad uno stile fa si che esso diventi
preponderante rispetto a quelli di livello superiore.
.webPart
{
padding: 0px !important;
}
Questo breve pezzetto di stile risolverà definitivamente il
problema. Speriamo però che Microsoft ci metta una pezza al più
presto.
powered by IMHO 1.3
Le nuove capacità di Profiling di ASP.NET 2.0 sono una feature
molto apprezzabile anche in considerazione del fatto che sono in grado di
gestire anche i profili anonimi, cioè consentono di persistere le impostazioni
di profilazione anche per gli utenti che ancora non sono autenticati. Questo ad
esempio torna utile nel momento in cui è necessario consentire la selezione
della lingua anche ad un navigatore che non si è ancora registrato. Esiste però
un piccolo problema per capire il quale vi farò un semplice esempio: immaginate
di utilizzare il profilo anonimo appunto per persistere la lingua selezionata in
un portale. Ora è evidente che qualora un utente anonimo imposti la lingua e in
seguito si autentichi al portale, l'impostazione selezionata dovrà essere
trasportata verso il profile autenticato. Per compiere questa migrazione esiste
un evento apposito, gestibile nel global.asax in questo modo:
public void Profile_OnMigrateAnonymous(
object sender,
ProfileMigrateEventArgs args)
{
ProfileCommon anonymousProfile =
Profile.GetProfile(args.AnonymousID);
Profile.Culture = anonymousProfile.Culture;
ProfileManager.DeleteProfile(args.AnonymousID);
AnonymousIdentificationModule.ClearAnonymousIdentifier();
}
L'esempio è reperibile anche nella documentazione MSDN ma
non è del tutto evidente.
powered by IMHO 1.3
Sono davvero lieto di potervi comunicare la bella novella. Stasera, alla cena indetta per fondare lo user group .NET del triveneto sono convenute ben 12 persone, molto più di quanto mi aspettassi per un incontro tutto sommato tra perfetti sconosciuti. Invece sono stato favorevolmente sorpreso della serietà dimostrata nell'affrontare gli argomenti via via proposti, nell'esporre cosa ognuno di noi si aspetti da questo user group, dagli obbiettivi che ci uniscono fortemente.
Il primo meeting dello user group del triveneto, ha deciso. Ha deciso innanzitutto di provare a costruire qualcosa gradualmente, stabilendo quelli che sono gli obbiettivi primari da raggiungere perchè si possa dire che la sua attività è iniziata.
1) mettere online un sito web, basato su community server, che serva da centro di aggregazione per distribuire le notizie che ci riguardano. Nulla di eccessivo, un feed rss dove chi vorrà potrà leggere le news e registrarsi per farci conoscere la propria presenza. Con il tempo poi magari sfrutteremo i forum per creare dei gruppi di lavoro.
2) organizzare il primo meeting ufficiale, da tenersi entro la fine del mese di febbraio. Il lavoro da fare è parecchio; stabilire dove tenerlo, organizzare una o due sessioni, deciderne l'argomento.
Il gruppo si è anche dato un nome davvero splendido: xedotnet giusto per non tradire le nostre origini. La decisione non è stata così difficile come pensavo. Una battuta ha suggerito questo nome il cui significato naturalmente è Xpert Enthusiast .NET
Infine, i convenuti hanno incaricato me, Alejandro Gonzalez e Andrea Dottor di portare il nuovo gruppo verso i primi due obbiettivi. Il gruppo quindi è nato e ha emesso il suo primo vagito. Sta a noi ora concretizzare queste decisioni e aiutarlo a sbocciare.
Fateci gli auguri.
powered by IMHO 1.3
Stamane ho ricevuto un inatteso ma graditissimo dono per
cui ora mi sento di ringraziare pubblicamente. Si tratta nientemeno che
della licenza Visual Studio Team Suite con MSDN premium
Subscription che Davide
Vernole mi ha consegnato incartata in una velina con impresso il logo degli
MVP (che il mio feticismo mi porterà a conservare gelosamente) sotto forma
di una placca metallica del formato di una carta di credito, con inciso il
classico codice alfanumerico che apre le porte del paradiso.
Credo che non esistano parole sufficienti per esprimere la mia
gioia e gratitudine, perciò posso solo prendere l'impegno di sfruttare il suo
generoso gesto nel modo migliore, per supportare lo sviluppo del mio
progetto open source IMHO 2.0 e per migliorare la mia conoscenza del Framework
.NET da condividere con la community degli sviluppatori .NET.
Un grazie di dimensioni astronomiche, all'amico Davide che con
questo gesto mi ha dimostrato una stima e considerazione che mi onora, ancor più
dell'opportunità già importantissima di lavorare nel team guidato da
lui.
powered by IMHO 1.3
Per tutti coloro abbiano ancora dubbi su dove sia il punto di
incontro, ecco una foto della famosa "I" a fianco della quale ci troveremo
domani sera alle 20:20. Dato che questa letterona è nel bel mezzo di una mini
rotatoria vi sconsiglio di attendere la sotto. Sulla sinistra, giusto fuori
dell'inquadratura troverete un edificio con un bancomat. Ci troviamo proprio li
davanti.
A presto!
powered by IMHO 1.3
Invito chi volesse partecipare alla riunione di fondazione
dello User Group Triveneto di inviarmi al più presto la sua adesione. Nel
frattempo ho prenotato il tavolo presso la Pizzeria da Gennaro a Treville di
Castelfranco Veneto. L'appuntamento è per le 20:20 presso il piazzale del centro commerciale I Giardini del Sole,
nei paraggi della proverbiale "I" che ne fa da insegna. I convenuti poi
muoveranno verso Treville dove il tavolo è prenotato per le 20:45.
powered by IMHO 1.3
Quest'oggi mi sono scontrato con un problema che pareva di
banale soluzione ma che si è rivelato essere decisamente subdolo. L'obbiettivo
da raggiungere era di caricare le webparts di una pagina dinamicamente da un
database invece che inserirle nel markup come di consueto. A prima vista può
sembrare che il framework offra quanto necessario per compiere questo compito.
Il WebPartManager ad esempio espone il metodo AddWebPart() che sembra fatto
a posta per quello, ma dopo un po' di tentativi si intuisce che c'è qualcosa che
non va.
Il problema non è che il metodo in questione non funzioni, ma
anzi, che funziona troppo. Infatti il difetto che si evidenzia quasi subito è
che ad ogni postback le webpart vengono nuovamente aggiunte causando una
crescita smisurata e inarrestabile. Qualunque tentativo di gestire questo
comportamento infausto non ha i risultati sperati. A partire dal consueto
"IsPostBack", fino alla verifica controllo per controllo che non si stiano
inserendo duplicati tutti i tentativi falliscono meramente.
Il fatto è che, quando si aggiungono le webpart queste vengono
persistite nel database di personalizzazioni. Perciò la volta successiva che la
pagina viene costruita le personalizzazioni salvate causano la duplicazione
delle webpart. Naturalmente a questopunto verrà persistita anche la
duplicazione, con l'ovvio risultato che la volta successiva ci si ritrova con
una triplicazione... e così via.
L'unica soluzione a questo problema l'ho trovata spulciando tra
le sessioni della PDC'05. Il trucco sta nel creare un
WebPartManager derivato da quello consueto e gestire al suo interno l'aggiunta
delle webparts dinamiche nell'OnInit della pagina. In questo modo sarà possibile
sfruttare la classe WebPartManagerInternals esposta per mezzo della proprietà
Internals per settare le proprietà delle webpart aggiunte e farle per così dire
"rientrare" nel normale ciclo di vita delle webpart.
Ecco un breve spezzone di codice:
// metodo chiamato nel Page.OnInit()
private void AddExternalWebParts()
{
// legge le webpart dallo storage
List<WebPartData> parts =
WebPartProvider.GetWebParts(this.Page);
// cicla le webpart
foreach (WebPartData data in parts)
{
// istanzia lo usercontrol
Control uc =
this.Page.LoadControl(data.AscxVirtualPath);
// crea la webPart che incapsula lo usercontrol
GenericWebPart webPart =
WebPartManager.CreateWebPart(uc);
// setta le proprietà
Internals.SetZoneID(webPart, data.ZoneID);
Internals.SetZoneIndex(webPart, data.ZoneIndex);
Internals.SetIsShared(webPart, true);
// Aggiunge una WebPart statica
Internals.AddWebPart(webPart);
}
}
Nell'esempio che ho linkato, nel folder ExternalWebPart è presente
anche il codice per creare le connessioni tra webpart automaticamente. Ma questa
è un'altra storia.
powered by IMHO 1.3
Consentitemi un momento di solennita, per onorare gli eventi
che mi stanno conducendo, con la collaborazione di alcune altre persone della
mia regione a tentare di unire i nostri sforzi per un'impresa comune. L'impresa
in questione è uno UserGroup focalizzato nel veneto, ma che vorrebbe catalizzare
l'attenzione degli appassionati e dei curiosi di tutto il nord-est,
possibilmente da Verona a Gorizia e da Bolzano a Rovigo, come mi sono spesso
trovato a ripetere in questi giorni.
L'annuncio di oggi è che Venerdì 20 Gennaio,
nella zona di Castelfranco Veneto - ancora non è stata decisa con precisione la
località - si terrà un incontro esplorativo aperto a tutti coloro che vogliano
approfondire la propria conoscenza del Framework .NET e che intendano apportare
il proprio contributo alla nascita di una nuova community.
Tale community, occorre essere chiari, non ha l'intento di
sottrarre attenzione e visibilità all'UgiDotNet, la cui fondamentale importanza
come punto di aggregazione rimane invariata, ma ha l'intento di smuovere, se
possibile, le persone che per motivi di tempo e di distanza tipicamente non se
la sentono di partecipare attivamente a questo UserGroup (o ad altri...) e
creare l'occasione un giorno, se saremo abbastanza bravi di gettare le basi di
una collaborazione.
Venerdì perciò tutti coloro che si trovassero nei paraggi
saranno i benvenuti a quello che negli intenti dovrebbe essere l'appuntamento in
cui tale UserGroup verrà fondato, ne verrà deciso il nome e la direzione di
lavoro verso la quale nei primi mesi di attività ci dovremmo dirigere. Prego
chiunque intenda partecipare di contattarmi al più presto all'indirizzo per darmi modo di riservare un tavolo di
dimensioni adeguate alla partecipazione.
powered by IMHO 1.3
L'esperienza con le WebParts di questi giorni è stata
l'occasione per scrivere un breve articolo per UgiDotNet che unitamente ad un
utile Tip per aggirare un comportamento che potrebbe sembrare anomalo, descrive
il meccanismo per cui ogni WebControl o UserControl può diventare una
WebPart.
Buona lettura a tutti.
Link: http://www.ugidotnet.org/articles/articles_read.aspx?ID=108
powered by IMHO 1.3
Il web.config di ASP.NET 2.0 si sta rivelando una
vera e propria miniera. Stamane, per puro caso ho scoperto un'altra chicca che
mi accingo a proporvi. Celata, nei meandri del file di configurazione si trova
una nuova sezione tagMapping che consente di sostituire tag all'interno
di un Templated Control quale può essere una pagina, uno usercontrol o un
qualunque controllo che esponga un template.
Per chiarire il concetto provate a pensare di aver
utilizzato un WebControl all'interno di una vostra applicazione. Dopo
che avete effettuato il deploy dell'applicazione in produzione vi
rendete conto che una versione successiva dello stesso controllo utilizzata
nell'applicazione gli darebbe una serie di feature importanti. Senza dover
modificare il codice sarà sufficiente andare nel web.config e scrivere la
seguente linea:
<pages>
<tagMapping>
<add
mappedTagType="MyApp.Controls.NewControl"
tagType="MyApp.Controls.OldControl"/>
</tagMapping>
</pages>
Come risultato otterrete che l'applicazione istanzierà
il nuovo webcontrol al posto di quello vecchio. La sostituzione del controllo
avviene durante la fase di parsing del template per cui non introduce alcun
overhead al momento dell'esecuzione. Unico limite di questa tecnica è che il controllo da
sostituire deve derivare da quello di partenza. In effetti più che
comprensibile.
powered by IMHO 1.3
Se create un WebControl e poi decidete di applicare ad esso
l'utilizzo dei temi è necessario avere un particolare accorgimento per le
proprietà che questo espone se debbono contenere un url relativo alla root del
tema.
Ad esempio, se create una proprietà del WebControl, che espone
l'url di una immagine che utilizzate per il rendering (ButtonImageUrl) e poi
impostate questa proprietà nel file .skin vi renderete immediatamente conto che
l'url che viene passato a runtime al WebControl è relativo la root
dell'applicazione e non a quella del tema. Per ottenere la trasformazione
relativa al tema occorre applicare alla property un attributo di tipo
UrlProperty:
[UrlProperty]
public string ButtonImageUrl
{
get { return this.buttonImageUrl; }
set { this.buttonImageUrl = value; }
}
Interessante notare che tale attributo dispone anche di una
proprietà Filter, che consente di discriminare il tipo di file che devono essere
soggetti alla trasformazione. Ad esempio se scrivere l'attributo come segue,
solo le immagini gif saranno ricalcolate sulla base della root del
tema.
[UrlProperty(Filter="*.gif")]
powered by IMHO 1.3
Presso XYZ Computing è possibile vedere qualche foto del più piccolo
alimentatore del mond.
Eroga ben 120 Watt. Da non crederci!
Fonte: http://hardware.slashdot.org/article.pl?sid=06/01/12/1727210&from=rss
powered by IMHO 1.3
per leggere il post originale o inviare un commento visita il seguente indirizzo:
PicoPSU...
Netcraft annuncia che Alex Tew, è
riuscito a vendere gli ultimi 1000 pixel della sua Million Dollar Homepage per
la ragguardevole cifra di 38100$. La sua homepage però è stata afflitta da un
lungo downtime.
The Million
Dollar Homepage was unavailable for an
extended period early today, as huge publicity accompanied the completion of
Alex Tew's novel online advertising service. Tew, a 21-year-old UK college
student, sold the final 1,000 pixels for $38,100 in an eBay auction that
closed Wednesday, netting Tew a total of $1,037,100 in total ad sales. The
winner of the auction has not yet been announced. Tew launched the site in
September to pay his college expenses, offering 1 million pixels of ad space
at $1 a pixel.
Stavo pensando di vendere spazi nel
mio blog. 1€ a lettera. Ce qualcuno che sgancia?
Source: Netcraft: Million Dollar Homepage Hit By
Downtime
powered by IMHO 1.3
Se provare a creare uno UserControl con ASP.NET 2.0 e poi
tentate di registrarlo nel file di skin e applicando ad esso uno SkinId di
settare alcune proprietà, vi troverete di fronte al seguente errore che ieri mi
ha fatto perdere ben mezza giornata di lavoro:
Error 1 The control type
'ASP.MyControl_ascx' cannot be
themed. C:\DEV\Test\Portal\App_Themes\my_theme\my_theme.skin 42
Per risolvere questo problema occorre applicare un attributo
alla classe che rappresenta lo UserControl in questo modo:
[Themeable(true)]
public partial class MyControl : UserControl
{
}
Chissa poi perchè di default gli UserControl non dovrebbero essere
skinnabili? Ma questo è un altro paio di
maniche...
powered by IMHO 1.3
Curiosamente stamane Simone mi ha preceduto di un soffio nel parlarvi di
Url Rewriting. In questi giorni infatti mi sono trovato nella necessità di
soddisfare una particolare esigenza di rewriting che mi ha fatto approfondire
l'argomento e avevo in mente di scrivere questo post da un po'. Preso atto che
la scelta di Simone è ricaduta sull'implementazine di un IHttpModule, è evidente
che il mio post non è una ripetizione perchè in realtà il metodo che ho usato è
basato su una tecnica che nell'articolo di MSDN che Simone ha citato viene
appena sfiorata. Eccomi quindi a proporvi il metodo che ho recentemente
utilizzato per il rewriting dell'url in una applicazione che vedeva le proprie
pagine riportate parzialmente nel database e parzialmente sul filesystem.
La mia implementazione di rewrite è basata sulla creazione di
un HttpHandlerFactory. Se consideriamo un HttpModule come un
filtro che intercetta tutte le chiamate alle pagine di una applicazione, e un
HttpHandler un gestore per particolari tipi di chiamate che si
sostituisce a quello delle normali pagine, la HttpHandlerFactory invece
è una classe che viene usata dal runtime per istanziare un HttpHandler
specifico in base alla chiamata che esso riceve. Implementando l'interfaccia
IHttpHandlerFactory, in sostanza ci si pone nella HttpPipeline
al livello più alto possibile e si è in grado di decidere quale handler
istanziare in base all'url che si riceve ed in seguito passare ad esso la
chiamata.
Questa condizione nel mio caso era davvero esemplificativa. Si
trattava infatti di effettuare una semplice query nel database - query che con
l'implementazione di un meccanismo di caching è diventata unica in tutta la vita
dell'applicazione - e in base al risultato decidere se istanziare l'handler
delle pagine aspx normalmente oppure variando leggermente l'input per
ottenere oltre al rewrite, il caricamento di una specifica pagina in base a
quanto specificato nel database. Vedo di spiegarmi meglio; tanto per cominciare
vediamo come fa il runtime ad istanziare l'handler per le pagine aspx.
IhttpHandler
page =
PageParser.GetCompiledPageInstance(url, pathTranslated, context);
Per
mezzo di questa semplice riga, è possibile indicare al runtime di produrre una
istanza di IHttpHandler del tutto uguale a quella che produce esso
stesso in condizioni normali. I parametri indicano il path virtuale, il path
fisico della pagina e l'HttpContext della chiamata. Ecco quindi che in
realtà utilizzando tale metodo si può "imbrogliare" il runtime facendo caricare
ad esso un file che si trovi in un percorso diverso da quello reale che esso ha
calcolato sulla base della chiamata. Il trucco per fare il rewriting in questo
caso è di avere una singola pagina "dbpage.aspx", nella root
dell'applicazione cui redirigere tutte le chiamate che fanno riferimento al
database e - nel mio caso - rifilare al runtime una pagina
"fisica" scelta fra una rosa di possibili layout per mezzo di
un campo nel database stesso. Ecco il codice:
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
PortalPage page = this.Indexer.FindPage(url);
if (page != null)
{
context.Items["CurrentPage"] = page;
return PageParser.GetCompiledPageInstance("~/dbpage.aspx", this.MapLayout(page.Layout), context);
}
return PageParser.GetCompiledPageInstance(url, pathTranslated, context);
}
public string MapLayout(string layoutName)
{
return string.Format("~/layouts/{0}.aspx", layoutName);
}
Il codice parla da solo, ma vediamo lo stesso di spendere due
parole. La prima riga reperisce l'istanza della PortalPage da un oggetto tenuto
in cache, che rappresenta la struttura caricata dal database una volta per
tutte. A questo punto, se la pagina viene trovata essa viene inserita nel
contesto della chiamata e poi viene effettuato il rewrite modificando l'url in
~/dbpage.aspx e passando come path fisico una diversa pagina aspx - che
per inciso contiene una serie di WebPartZones in un
ContentPlaceHolder. Nel caso in cui la pagina non esista nel database,
i parametri vengono passati così come sono ricevuti al PageParser
che quindi costruirà l'IHttpHandler consueto.
Risultato di questo giochetto è che parte delle pagine
dell'applicazione sono definite in una tabella del database e l'utente potrà
navigare tale struttura come se fosse fusa con quella sul filesystem stesso.
Non mi rimane di segnalarvi che questa tecnica è utilizzabile
anche con il framework 1.1, praticamente senza alcuna modifica.
powered by IMHO 1.3
Dell ha messo in vendita il monitor dei miei sogni...
peccato che costi 2200$.
So che Apple ci era già arrivata, ma io non uso Mac.
Link: Dell
3007WFP
powered by IMHO 1.3
per leggere il post originale o inviare un commento visita il seguente indirizzo:
Ragazzi che monitor!
Per noi che ormai viviamo di googling, è abbastanza semplice
forgiare delle sequenze di keyword che riescano a produrre il risultato voluto.
Personalmente posso dire che ormai riesco a "parlare" con Google abbastanza
facilmente, ma spesso mi sono chiesto, di fronte magari all'impaccio di qualche
conoscente davanti alla casella di ricerca, se non fosse possibile migliorare
ancora l'imbattibile motore di ricerca. Oggi ho scoperto che qualcuno ci sta
provando. Si tratta di lexxe, un engine che è in grado di interpretare le domande poste da
un utente trovando dei risultati molto più vicini alla richiesta di quello che
fa Google con il medesimo input.
Source: http://google.blognewschannel.com/index.php/archives/2006/01/07/lexxe-search-engine/
powered by IMHO 1.3
Ho provveduto ad aggiornare la roadmap di IMHO 2.0 per rispecchiare l'andamento del progetto. Le
modifiche più significative sono il termine della lavorazione del Preview
Panel, l'anticipazione di alcuni dei servizi della Media Library alla
prima versione, e l'inizio dello sviluppo dell'editor.
powered by IMHO 1.3
Ricordo ancora la pompa con cui ho annunciato il mio 100esimo post sul blog di UgiDotNet.
Per l'occasione ho addirittura postato una chicca di codice per Terminal Server.
Stamane mi è capitato di scrivere il millesimo post, a distanza di 1 anno e 3
mesi da quel giorno e credetemi se vi dico che ci ho messo un po' ad
accorgermene. Ma non si tratta di disattenzione, anzi se permettete si tratta di
maggiore attenzione a quello che scrivo che alla quantità di post.
Beh, fatemi gli auguri... ci risentiamo a 10000... sempre che
mi sopportiate tanto.
powered by IMHO 1.3
La lotta agli hacker negli USA sta raggiungendo dei
risultati grotteschi. Ecco cosa è successo a un tizio che ha incoraggiato i suoi
compagni a premere ripetutamente F5 nella home page del sito della scuola.
link: http://www.cantonrep.com/index.php?ID=261925&Category=15&fromSearch=yes
powered by IMHO 1.3
per leggere il post originale o inviare un commento visita il seguente indirizzo:
Attenti a quell'F5!
Su Channel 9 trovate un video che mostra il chitarrista Robert Fripp, fondatore dei King Crimson, impegnato in una sessione presso il campus Microsoft per realizzare la colonna sonora del nuovo Sistema Operativo Windows Vista.
Tags: Robert+Fripp, Windows+Vista
powered by IMHO 1.3
Date un'occhiata a questi screenshots che mostrano cosa ci
dovremmo attendere dalla prossima versione dell'inimitabile Flight Simulator.
L'unico gioco che valga la pena di essere giocato... IMHO naturalmente.
Link: http://www.fsplanet.com/fsplanet_com_fsx.htm
powered by IMHO 1.3
Prosegue con questo post la saga, iniziata quando ho parlato
degli ExpressionBuilders, dedicata alle risorse
tipicamente utilizzate nelle applicazioni ASP.NET per depositare stringhe, e in
genere proprietà da utilizzare nel rendering dell'interfaccia. In quel post
spiegavo come realizzare un ExpressionBuilder che permettesse facilmente dei
leggere le risorse da un database anziche da un più consueto file resx. Esiste
tuttavia un altro metodo, per fare la medesima cosa, che in realtà è quello che
il framework indica come quello corretto. Chi avesse provato a realizzare
l'ExpressionBuilder forse si sarà reso conto che pur funzionando egregiamente
esso ha delle limitazioni fastidiose. Giusto per indicarne una per tutte,
quella che a me ha fatto approfondire ulteriormente l'argomento, qualora vi
trovaste a localizzare un SiteMap ben presto vi renderete conto che in
questo l'ExpressionBuilder non vi è di alcun aiuto. Il motivo è presto detto: la
funzione degli ExpressionBuilder non è quella di leggere risorse, ma
semplicemente quello che tipicamente viene usato per questa operazione, il
ResourceExpressionBuilder è solo una minima sfaccettatura all'interno
dell'architettura del framework dedicata all'argomento.
Se come ho fatto io imbracciate il vostro reflector,
e lo puntate sulla classe System.Web.SiteMapNode, vi accorgerete che la
proprietà Title (una di quelle che è passibile di localizzazione),
racchiude al suo interno molta più logica di quella che ragionevolmente ci si
potrebbe aspettare. Ecco un estratto da Reflector:
public virtual string Title
{
get
{
if (this._provider.EnableLocalization)
{
string text1 = this.GetImplicitResourceString("title");
if (text1 != null)
return text1;
text1 = this.GetExplicitResourceString("title", this._title, true);
if (text1 != null)
return text1;
}
if (this._title != null)
return this._title;
return string.Empty;
}
}
Procedendo ulteriormente nell'esplorazione in breve si
scoprira che il codice porta ad istanziare il medesimo
ResourceExpressionBuilder e quindi in definitiva tentare di localizzare
una proprietà della SiteMap corrisponde ad usare l'ExpressionBuilder
"Resources". Di primo acchito questo mi è parso davvero strano. L'idea che mi
ero fatto era che a quel punto si potessero usare esclusivamente i consueti file
resx per localizzare una SiteMap.
In realtà nel framework esiste una interfaccia
IResourceProvider, che è il punto giusto cui agganciarsi per spostare
la fonte delle risorse dai normali file al database. Un po' celato all'interno del DOM del web.config si trova un
attributo dell'elemento <globalization> denominato resourceProviderFactoryType. In tale attributo si dovrà
immettere il tipo, derivato da ResourceProviderFactory, che ha il
compito di creare il ResourceProvider deputato a recuperare le
risorse. Mi rendo conto che
detto così pare un po' complesso, ma questo meccanismo è molto efficace perchè
consente di avere diversi ResourceProvider che lavorano assieme,
istanziati alla bisogna in base al tipo di risorse che si desidera prelevare. In
particolare il ResourceProviderFactory dispone di due metodi, uno per
le risorse cosidette "locali" e uno per quelle "globali". Per
comprendere la distinzione si dovra pensare alle risorse locali come quelle
dedicate ad una singola pagina, infatti il relativo metodo richiede in ingresso
il path della pagina, mentre per globali si intendono quello usate da tutte le
pagine dell'applicazione. In quest'ultimo caso il metodo richiede una
"classKey" cioè un qualificatore che permette di distinguere dei gruppi
di risorse.
Non è finita però. Dopo aver implementato il
ResourceProviderFactory dovremmo procedere all'implementazione di
IResourceProvider che richiede la realizzazione di due metodi. Il primo per la lettura di una
singola risorsa, e il secondo che restituisce un IResourceReader, che serve al runtime per
enumerare le risorse presenti. La classe che implementa IResourceReader sarà lanostra
terza ed ultima fatica. Ecco di seguito il codice di entrambe le
classi:
public class SqlResourceProvider : IResourceProvider
{
public object GetObject(string key, CultureInfo culture)
{
// leggi una singola risorsa
return GetResourceFromDB(key, culture);
}
public System.Resources.IResourceReader ResourceReader
{
get
{
// leggi un set di risorse
return new SqlResourceReader(
GetResourcesFromDB(CultureInfo.CurrentUICulture));
}
}
}
public class SqlResourceReader : IResourceReader
{
private Dictionary<string, object> resources;
public SqlResourceReader(Dictionary<string, object> resources)
{
this.resources = resources;
}
public IDictionaryEnumerator GetEnumerator()
{
return resources.GetEnumerator();
}
public void Close()
{ }
public IEnumerator IEnumerable.GetEnumerator()
{
return resources.GetEnumerator();
}
public void Dispose()
{
Close();
}
}
L'istanza del ResourceProvider andrà creata nel
ResourceProviderFactory che registreremo in configurazione. In questo modo
avremo interposto il nostro provider a tutti i tentativi di lettura di
risorse di localizzazione effettuati da parte dei componenti del framework,
ed in questo modo potremmo facilmente localizzarli. Anche la SiteMap di cui ho
detto poco fa.
powered by IMHO 1.3
Chiunque abbia sviluppto un'applicazione che si appoggia a diversi web-services si sarà trovato nella condizione di avere una moltitudine di proxy generati che fanno capo a diversi oggetti del dominio applicativo. Molto spesso ci si trova nella condizione di avere dei duplicati in questi oggetti che per il fatto stesso di essere classi diverse, pur avendo le medesime proprietà non sono "compatibili". Perciò, ad esempio avendo un webservice che restituisce un Ordine ed un secondo webservice che lo richiede in input non è possibile passare al secondo direttamente l'oggetto restituito dal primo.
Nel framework 2.0 esiste una soluzione a questo problema che però comporta la generazione "manuale" dei proxy richiamando direttamente l'eseguibile wsdl.exe con lo switch /sharedtypes e passando a quest'ultimo gli url dei webservices che condividono i tipi. In questo modo verranno generati i proxy facendo capo ad una gerarchia di oggetti unica.
Source: http://weblogs.asp.net/israelio/archive/2005/01/04/346337.aspx
Chi volesse creare un provider custom per sitemap, non avrà che da implementare i due metodi astratti della classe StaticSiteMapProvider, da cui già eredita la consueta XmlSiteMapProvider. In questo articoletto di Jeff Prosise, in breve viene spiegato come fare, con un piccolo esempio di codice nel quale viene realizzato un SiteMapProvider per Access.
Link: http://msdn.microsoft.com/msdnmag/issues/05/06/WickedCode/
Ecco un paio di simpatici tools per generare Sitemap. La prima (http://odetocode.com/Blogs/scott/archive/2005/11/29/2537.aspx) è una macro che genera il file web.sitemap dalla struttura del sito web ricavata da visual studio. Il secondo (http://weblogs.asp.net/bleroy/archive/2005/12/02/432188.aspx) scandisce la struttura del sito ASP.NET e genera un file xml adatto a Google Sitemaps.
Ecco qualcuno che ha deciso, a ragion veduta, che il mac non è il suo sistema operativo...
Link: http://www.russellbeattie.com/notebook/1008724.html
Buon 2006 a tutti... pigroni!!!