68

v2005 04 vbj64

Embed Size (px)

Citation preview

Page 1: v2005 04 vbj64
Page 2: v2005 04 vbj64
Page 3: v2005 04 vbj64
Page 4: v2005 04 vbj64

E D I T O R I A L E

VBJ N. 64 - Luglio/Agosto 20054

Qualche giorno fa ho partecipato, insieme al mio co-autore Giuseppe Dimauro, alla presentazione del nostro libro Microsoft .NET Framework: Regole di stile e best practice,

in una libreria di Milano. L’incontro è stato particolarmente in-teressante perchè ha messo a confronto due autori, un editore (Mondadori), e alcuni lettori che avevano idee abbastanza chiare su quello che un libro “tecnico” deve offrire.

Una delle proposte più largamente condivise è che un libro dovreb-be avere una vita parallela al software – in particolare, il software

a cui il libro fa riferimento – e in particolare le edizioni successive del libro dovrebbero essere fornite sotto forma di upgrade, a prezzi contenuti. Ad esempio il lettore potrebbe portare la sua copia presso la libreria e ritirare la nuova versione a un prezzo scontato, un po’ come si fa con la rottamazione degli autoveicoli. È un’idea molto interessante, ma purtroppo ci sono un paio di impedimenti che ne ostacolano l’attuazione.

Il primo problema è che produrre un libro costa tantissimo, in termini di tempo e de-naro. Esistono le spese del materiale (la carta), di stampa, di rilegatura, di spedizione, e di magazzino. In Italia si aggiunge spesso il compenso del traduttore, che non è affatto trascurabile. L’idea di offrire la seconda edizione di un libro a prezzi da upgrade si scontra con tutte queste spese fisse, a cui occorre sommare le spese necessarie per mettere su la macchina organizzativa che ritira i libri usati per poi mandarli al macero. Alla fine della fiera, lo sconto che si potrebbe offrire sarebbe talmente piccolo – nell’ordine del 10-15 percento – da non fare una differenza sostanziale per le tasche dei lettori.

Una buona parte di queste spese si potrebbero facilmente superare offrendo la nuova versione del libro sotto forma di e-book da distribuire via Internet. Questo meccanismo risolverebbe alcuni problemi ma ne creerebbe altri. In particolare, la storia della musica in formato MP3 e degli strumenti peer-to-peer insegna che basta che uno solo dei lettori metta il proprio e-book a disposizione sulla rete per vanificare quasi completamente i legittimi guadagni dell’autore e dell’editore. Io parlo per esperienza personale: una volta l’e-book del mio libro Programmare Microsoft Visual Basic 6 finì su un sito Web italiano e fu scaricato da quasi 10.000 persone in meno di due settimane! Se si fosse trattato di un sito in lingua inglese probabilmente il numero sarebbe stato dieci volte maggiore.

In conclusione, occorre sicuramente trovare un meccanismo che “premi” i lettori che decidono di acquistare le edizioni successive di un libro; per il momento, tuttavia, non esiste una soluzione che soddisfi tutti gli “attori” coinvolti. Se qualcuno ha qualche idea in proposito, mi può scrivere via email o tramite il mio blog personale.

Francesco [email protected]

www.dotnet2themax.it/blog

Libri in versioneupgrade

o n l i n e . i n f o m e d i a . i tn. 64 - luglio/agosto 2005

bimestrale - anno undicesimo

Direttore Re spon sa bi leMarialetizia Mari ([email protected])

Direttore EsecutivoFran ce sco Balena ([email protected])

Managing EditorRenzo Boni ([email protected])

CollaboratoriAndrea Benedetti Filippo Bonanni

Stefano Corti Andrea Ferendeles

Davide Mauri Lorenzo Vandoni

Vito Vessia

Direzione Natale Fino ([email protected])

Marketing & Advertising Segreteria: 0587/736460

[email protected]

AmministrazioneSara Mattei

([email protected])

GraficaManola Greco ([email protected])

Technical BookLisa Vanni ([email protected])

Segreteria Enrica Nassi

([email protected])

StampaTIPOLITOGRAFIA PETRUZZI

Citta’ di Castello (PG)

Ufficio AbbonamentiTel. 0587/736460 - Fax 0587/732232e-mail: [email protected]

www.infomedia.it

Gruppo Editoriale Infomedia srlVia Valdera P., 116 - 56038 Ponsacco (PI) Italia

Tel. 0587/736460 - Fax 0587/[email protected]

Sito Web www.infomedia.it

Manoscritti e foto originali anche se non pub bli ca ti, non si restituiscono. È vietata la ri pro du zio ne

anche parziale di te sti e immagini.

Si prega di inviare i comunicati stampa e gli inviti stampa per la redazione all’indirizzo: [email protected]

Visual Basic Journal è una rivista diGruppo Editoriale Infomedia S.r.l. Via Valdera P, 116 Ponsacco - Pisa.

Registrazione presso il Tribunale di Pisa n. 20/1999

Page 5: v2005 04 vbj64

VBJ 64

IN OFFERTA VBJ 64

Scrivi a

[email protected]

specificando

nell’oggetto della

e-mail:

IN OFFERTAVBJ n. 64

OPPURE

inviaci il coupon

sottostante

al numero di fax

0587/732232

Potrai acquistare

i libri qui riportati con

uno

SCONTOECCEZIONALE

del 10% anche se

acquisti solo un libro

OPPURE

del 20% se acquisti

3 libri

Design Patternsdi E. Gamma - R. Helm - R. Johnson - J. Vlissides

Addison Wesley ItaliaISBN 887192150X414 pp - 39,00 €

C# Essentials di B. Albahari et al.

O’ ReillyISBN 0596003153

216 pp - 24,95 €

MasteringVisual Studio .NET di I. Griffiths et al.

O’ ReillyISBN 0596003609

416 pp - 39,95 €

Head FirstDesign Patterns

di Eric Freeman et al.

O’ReillyISBN 0596007124688 pp - 44,95 €

Maximizing ASP.NET:Real World,

Object-OrientedDevelopment

di J. Putz

Addison Wesley ISBN 0321294475 336 pp - 46,95 €

Professional DotNetNuke ASP.NET Portals di S. Walker et al.

John WileyISBN 0764595636456 pp - 45,80 €

Page 6: v2005 04 vbj64

6

SOMMARIO

VBJ N. 64 - Luglio/Agosto 2005

N.64

L U G L I O / A G O S T O

8Performance con GAC e NGEN in ASP.NETPerchè il primo avvio di una applicazione ASP.NET impiega più tempo? Cosa accade dietro le quinte?

Ecco le risposte che cercate ed i test che avreste sempre voluto fare.

di Andrea Ferendeles

SPECIALE

19

22

Le novità VB nel nuovo Visual StudioLa nuova versione di Visual Studio porta con sé diverse interessanti novità relative al linguaggio Visual Basic.

di Lorenzo Vandoni

Un motore di scripting per l’ambiente .NETRealizziamo un programma in grado di compilare ed eseguire al volo file di testo contenenti codice VB.NET.

di Filippo Bonanni

SQL Server 2005 ed il CLR: un matrimonio annunciatoSQL Server 2005 rappresenta una grande evoluzione nella gestione di database. Vediamo come l’integrazione con il fra-mework .NET possa migliorare la vita degli sviluppatori e dei DBA.

di Andrea Benedetti

I design pattern più famosi implementati in VB.NET (terza puntata)Il pattern Factory Method consente di delegare a una sottoclasse la scelta di quale oggetto istanziare.

di Lorenzo Vandoni

VB.NET

DATABASE

29

SOFTWARE ENGINEERING

39

Page 7: v2005 04 vbj64

7N. 64 - Luglio/Agosto 2005 VBJ

All’indirizzo ftp.infomedia.it/pub/VBJ sono liberamente scaricabili tutti i listati relativi agli articoli pubblicati. La presenza di questa immagine indica l’ulteriore disponibilità, allo stesso indirizzo, di un progetto software relativo all’articolo in cui l’immagine è inserita. Il nome identifica la cartella sul sito ftp.

Codice allegato

Editoriale 4

.NET Tools 61a cura di Davide Mauri

RUBRICHE

Verificare la disponibilità di un control OCX con VB6A volte è necessario che un programma si comporti in modo diverso a seconda della disponibilità di un determinato control OCX.

di Lorenzo Vandoni

Controllo Remoto in Visual Basic .NET (prima puntata)di Stefano Corti

XML Class GeneratorCome utilizzare il potente motore di scripting di Windows per produrre ed istanziare classi COM completamente costruite a runtime, per usufruire di tante interessanti potenzialità.

di Vito Vessia

TECNICHE

42

APPLICATIVI

44

I MITI

52

Page 8: v2005 04 vbj64

8 VBJ N. 64 - Luglio/Agosto 2005

Performance con GAC e NGEN in ASP.NET

di Andrea Ferendeles

Il tema delle performance in un’applicazione ASP.NET è sempre stato un tema caldo, non tan-to per un’effettiva mancanza di prestazioni da par-

te del .NET Framework, quanto invece per la ric-chezza dei tool messi a disposizione nell’SDK del framework stesso.In particolar modo il tool ngen.exe, presen-te nel SDK, è stato sempre oggetto di dispu-te e discussioni a me personalmente mai trop-po chiare e soprattutto di diffi cile verifi cabilità. Vi siete mai chiesti perché il “primo avvio” di una ap-plicazione ASP.NET impiega più tempo dei successi-vi? Cosa succede dietro le quinte?

Scopo di questo articolo è quello di indagare, at-traverso semplici test ed osservazioni, nelle directory Temporary ASP.NET Files, con un’attenzione parti-colare ai tempi di risposta ed alla concomitanza di altri fattori quali la GAC (Global Assembly Cache).

Premessa

Quando nell’ormai lontano 2002 fu rilasciato per la prima volta ASP.NET, una delle feature che colpì tut-ti noi, poveri sviluppatori ASP ☺, fu proprio la capa-cità di una applicazione ASP.NET di essere compila-ta, anziché interpretata da IIS. Col passare del tem-po ci siamo abituati a questa feature e ci siamo spin-ti sempre più alla ricerca delle migliori performance possibili in applicazioni Web.

Un aspetto che mi è rima-sto sempre oscuro e/o diffi cile da digerire e da capire è il “pri-mo avvio” di una applicazione ASP.NET.

In uffi cio ho provato per gioco a fare una serie di interviste ai miei colleghi di lavoro, ascoltando le loro reazioni al “primo avvio” di una applicazione ASP.NET.

Ecco alcune reazioni: “lento”, “lentissimo”, “ma che sta facen-do …”, “sul mio PC non è così lento” (sviluppatore junior), “ba-sta … vado a casa” (afi ciona-do ASP).

Quindi ho chiesto loro cosa si poteva fare per migliorare i tempi di risposta del primo avvio (stes-so ordine di cui sopra):

“niente”, “assolutamente nien-te”, “proviamo sul mio PC”, “fac-ciamola in ASP … però a casa mia”. Ovviamente sto scherzan-do, però una cosa ho deciso di farla; mi sono rimboccato le ma-niche (alla Peter Norton), mi sono armato di “cacciavite” e di “cro-nometro” ed ho deciso, in prima persona, di testare e capire il “pri-mo avvio”: i risultati sono stupe-facenti!

GAC

Facciamo una breve pre-messa su GAC, ngen.exe e

Performance

Perchè il primo avvio di una applicazione ASP.NET impiega più tempo? Cosa accade dietro le quinte?Ecco le risposte che cercate ed i test che avreste sempre voluto fare.

GAC

Andrea Ferendeles ha iniziato ad appassionarsi di informatica dai tempi del VIC20. Ha sviluppato in BASIC, Logo, Turbo Pascal, C, C++, Visual Basic, fino ad arrivare ai linguaggi .NET, quali VB.NET e C#. È certificato MCP, MCAD, MSF, MCSD (.NET), MCDBA, MCT. Può essere contattato via email: [email protected].

Page 9: v2005 04 vbj64

9N. 64 - Luglio/Agosto 2005 VBJ

la directory dai mille misteri C:\WINDOWS\Microsof t .NET\Framework\v1.1.4322\Temporary ASP.NET Files. La Global Assem-bly Cache sembra essere il repository di tutti quegli assembly .NET “condivisi” che rivesto-no il ruolo di estensori del .NET Framework.In realtà per condividere un assembly sarebbe suffi ciente, anzi auspicabile, utilizzare un fi le di confi gurazione con all’interno il tag <codeBase version=”1.0.0.0” href=”fi le://C:\myabsolutepath\MyAssembly.dll”/>.

Possiamo però, con una forzatura, registrare in GAC anche assembly di business logic, solo per fare piccoli test ed osservare eventuali benefi ci

sui tempi di risposta del “primo avvio” in ASP.NET.

Questi metodi ci consentono, di fatto, di evitare una duplicazione inutile oltrechè dannosa alle fasi di debbugging, testing e deploy-ment, che viceversa, con il mo-dello side-by-side di .NET non sarebbe possibile evitare.

Il requisito per registrare un as-sembly in GAC è uno: rendere l’assembly Strong Named, cioè dotarlo di nome (ovvio), versione, culture e di una coppia di chiavi pubblica-privata generabile con il tool sn.exe passandogli il pa-rametro –k. Fatto ciò possiamo o utilizzare l’altro tool dell’SDK (gacutil.exe con il parametro – i) oppure (evviva Microsoft) fare drag&drop nella directory della

GAC (in genere c:\windows\assembly). Ecco un esempio di generazione di una coppia di chiavi pubblica-privata con il tool sn.exe

Prompt MS-DOS di Visual Studio .NETC:\>sn.exe –k mykeys.snk

Ecco invece una porzione del fi le AssemblyInfo per indicare al compilatore la nostra intenzione di rendere l’assembly in questione Strong Named.

C#[assembly: AssemblyVersion(“1.0.0.0”)]

[assembly: AssemblyKeyFile(“c:\\mykeys.snk”)]

GAC

Fi gu ra 1 Come appare la struttura di C:\WINDOWS\Assembly dal prompt MS-DOS

<!-- compilation Attributes: tempDirectory=”directory” debug=”[true|false]” // Default: false strict=”[true|false]” // Default: false explicit=”[true|false]” // Default: false batch=”[true|false]” // Default: true batchTimeout=”timeout in seconds” // Default: 15 seconds maxBatchSize=”max number of pages per batched compilation” // Default: 1000 classes maxBatchGeneratedFileSize=”max combined size (in KB) of the generated source files per batched compilation” // Default: 3000KB numRecompilesBeforeAppRestart=”max number of recompilations before appdomain is cycled” // Default: 15 recomplations defaultLanguage=”name of a language as specified in a <compiler/> tag below” // Default: VB --> <compilation debug=”false” explicit=”true” defaultLanguage=”vb”>

Ri qua dro 1 Il meccanismo di compilazione può essere modificato attraverso il tag <compilation>

Page 10: v2005 04 vbj64

10 VBJ N. 64 - Luglio/Agosto 2005

Quando la culture non è indicata è per default neutral.

Per fi nire ecco invece come registrare il nostro assembly – supponiamo MyAssembly.dll – nel-la GAC attraverso il tool gacutil.exe sempre del .NET Framework SDK.

Prompt MS-DOS di Visual Studio .NETC:\>gacutil.exe /i MyAssembly.dll

Ovviamente abbiamo detto che la registra-zione in GAC può essere effettuata più sem-plicemente facendo drag&drop dell’assembly MyAssembly.dll nella directory c:\windows\assembly.

Osserviamo inoltre che dalla esplorazione (tra-mite explorer.exe di Windows) della directory c:\windows\assembly ci si accorge che questa non sembra essere una vera e propria “folder” del fi le system che contiene fi le (provate a fare click con il tasto destro del mouse su un fi le qualunque, non ci sono i classici comandi co-pia, taglia, incolla, ecc… del menu contestuale). Infatti quando installiamo il .NET Framework vie-ne applicata una patch ad explorer.exe per dare un nuovo aspetto alla cartella Assembly ed at-tribuire un diverso signifi cato al drag&drop qui operato (registrazione appunto e non copia ed incolla). Proviamo ora invece a curiosare con il prompt MS-DOS proprio nella cartella in que-stione. Come si vede dalla Figura 1 in realtà la struttura del fi le system per questa cartella è nascosta e ben diversa.

Proprio nella sotto-directory GAC troveremo diverse altre cartelle – una per ogni assem-bly presente in GAC – ed alla fi ne della struttura, una copia origina-le in IL (Intermediate Language generato da uno dei compilato-ri .NET) dell’assembly registrato in GAC. Quando da una applica-zione ASP.NET, il code-behind ri-chiede i servizi di un assembly, questo deve essere innanzitutto individuato e solo dopo una se-rie di procedimenti che portano alla sua individuazione univoca, esso viene compilato just-in-time e quindi portato in RAM. Questo meccanismo di identifi cazione è estremamente più facile e dun-que più veloce per tutti gli as-

sembly che risiedono in GAC, in quanto que-sti sono Strong Named e dunque già univoca-mente identifi cabili. Inoltre gli assembly in GAC, a fronte di una chiamata, non devono essere portati, cioè copiati, nella directory dell’assem-bly chiamante ma possono restare tranquilla-mente lì dove sono; sono appunto condivisi. Quest’ultimo punto sarà proprio uno degli ele-menti chiave, che rendono la GAC ed il mec-canismo di condivisione degli assembly tramite codebase, due meccanismi privilegiati dal pun-to di vista delle performance di start-up. Per il momento fermiamoci qui e passiamo ad ana-lizzare un altro tool fondamentale per le nostre prove: ngen.exe.

NGEN

Dato che nel .NET framework è previsto que-sto meccanismo di compilazione Just-In-Time, si è pensato bene di inserire nell’SDK una utility di pre-compilazione in codice nativo allo scopo di migliore le performance di “primo avvio” (le richieste successive abbiamo detto infatti “pe-scherebbero” dalla RAM).

Il tool in questione si chiama ngen.exe; vedia-mo un esempio di generazione di immagine na-tiva, sempre con il nostro assembly esemplifi -cativo MyAssembly.dll.

Prompt MS-DOS di Visual Studio .NETC:\>ngen.exe MyAssembly.dll

Fi gu ra 2 Alcuni file sono identificati come "Native Images"

GAC

Page 11: v2005 04 vbj64

11N. 64 - Luglio/Agosto 2005 VBJ

Il tool ngen.exe non richiede che l’assembly debba essere per forza Strong Named, moti-vo per il quale sotto la directory c:\windows\assembly (vedi Figura 1) ci sono due folder distinte: GAC e NativeImages1_xxxxx.

Gli stessi progettisti del .NET Framework hanno allora, loro stessi, applicato questo concetto ad alcuni assembly (vedremo più avanti perché solo alcuni e non tutti) del .NET Framework ed in particolare a quelli mostra-ti in Figura 2.

Guardate dove nella colonna Type compa-re la dicitura Native Images.

La soluzione sembra arrivare: compiliamo tutto in nativo e le performance saranno as-sicurate. Prima di tirare conclusioni affretta-te proviamo a porci tre domande:

• perché NON tutti gli Assembly del .NET Framework hanno un’immagine nativa? (ad es. System.Web.dll, System.Data.dll, ecc…)

• l’utility ngen.exe potrà a native-compile-time applicare le stesse ottimizzazioni che il JIT compirà a JIT-compile-time?

• come si comportano le immagini native nel caso di più di un Application Domain (caso di ASP.NET)?

Obiettivi e scenari di riferimento

Vado allora ad illustrarvi quelli che saranno gli obiettivi dei test che seguiranno. Lo scenario di riferimento è ASP.NET del .NET Framework 1.1 ma le stesse considerazioni possono esse-re applicate con alcuni correttivi anche ad altre tipologie di applicazioni, tipo Smart Client, Win-dows Services e così via.

Ricordiamo che l’obiettivo principe è capire ed ottimizzare il “primo avvio” di una applicazione ASP.NET senza far ovviamente decadere le per-formance nelle successive richieste. Innanzi tutto vi rimando ad un articolo molto ben fatto di Dino Esposito - vedi bibliografi a - che spiega tra le altre cose, il meccanismo che ASP.NET utilizza per “fondere”, durante la fase di pre-compilazio-ne, il codice di scripting lato server nelle pagine .aspx, .asmx, .ascx ecc., con il code-behind che risiede invece nell’assembly della nostra appli-cazione ASP.NET (cartella bin). Mi limiterò sem-plicemente a dire che tale meccanismo porta il .NET Framework (ed ASP.NET in particolare)

a generare tutta una serie di folder ed assem-bly sotto la cartella di sistema, di solito deno-minata C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files.

Ora, possiamo sicuramente affermare che tale processo porterà via senz’altro alcuni preziosi millisecondi al nostro “primo avvio”; vedremo in seguito se e come evitare in qualche modo que-sto processo e guadagnare quindi millisecondi o addirittura secondi utili. Scriviamo una mini applicazione ASP.NET (Listato 1) composta da un’unica Web Form (WebForm1.aspx) con all’in-terno un controllo TextBox chiamato TextBox1 e con il seguente codice in risposta all’evento Page Load della Web Form stessa. Come si vede, al caricamento della WebForm andremo a creare nella root del nostro disco (ricordatevi di dare i permessi necessari all’applicazione o cambiate application pool) un semplice fi le di testo deno-minato times.txt, con dentro due valori numeri-ci, la cui differenza algebrica fornirà il numero di millisecondi necessari a eseguire il metodo Method() di una istanza della classe Class100.N.B.: il 1 gennaio 1900 ore 00:00:00 è un qual-siasi DateTime noto e di riferimento.

Vediamo nel Listato 2 la defi nizione della classe Class100. Il metodo Method() si limiterà a resti-tuire il risultato di un’altro metodo chiamato an-ch’esso Method() ma di una istanza della clas-se Class99 che creerà on-the-fl y.

Come avrete già intuito ripeteremo questo mec-canismo, scrivendo in tutto 100 classi dove la classe 100 invoca la 99, la 99 invoca la 98, e così via fi no ad arrivare all’ultima invocazione di un metodo Method() di una classe Class1, la cui defi nizione è riportata nel Listato 3.

GAC

Li sta to 1 L'applicazione per effettuare i test

private void Page_Load(object sender, System.EventArgs e) { StreamWriter sw=new StreamWriter(“c:\\times.txt”,true); DateTime startdate=new DateTime(1900,1,1,0,0,0,0); DateTime enddate=DateTime.Now; TimeSpan diff=(TimeSpan)enddate.Subtract(startdate); sw.Write(“{0};”,diff.TotalMilliseconds.ToString());

this.TextBox1.Text=new Generate.Class100().Method();

enddate=DateTime.Now; diff=(TimeSpan)enddate.Subtract(startdate); sw.WriteLine(“{0}”,diff.TotalMilliseconds.ToString()); sw.Close(); }

Page 12: v2005 04 vbj64

12 VBJ N. 64 - Luglio/Agosto 2005

Ovviamente questo meccanismo non rispecchia assolutamente la realtà e non vuole essere nem-meno la base di partenza per un benchmark; vuole invece portare all’esasperazione i mecca-nismi di JITting e pre-compilazione nella Tempo-rary ASP.NET Files proprio per rendere evidenti le differenze nei tempi di risposta e consentirne dunque una facile valutazione.

Tutte e 100 le classi sono dotate dell’attribu-to AssemblyKeyFile in modo da rendere tutti e 100 gli assembly generati (uno per ogni classe, Class1.dll, Class2.dll, … Class100.dll) Strong Na-med e pronti dunque ad essere registrati all’oc-correnza dei nostri test, in GAC. Inoltre, tutte le chiamate si riducono alla fi ne alla restituzione di una stringa “something” (che vedremo apparire sul controllo textbox della nostra WebForm1.aspx al termine del caricamento della pagina stessa. Nel codice allegato all’articolo troverete una uti-lity per generare il sorgente di queste classi, per generare una serie di fi le batch (.bat) per com-pilarle, per pubblicare/rimuoverle dalla GAC e per creare/eliminare le relative immagini native con ngen.exe.

La serie di chiamate in cascata alle 100 classi ci assicurerà, per quanto concerne i nostri test, che il JIT “attraversi” tutte le classi e quindi tutti gli assembly, senza escluderne nemmeno una e ci darà modo di rendere “evidenti”, in termini di tempo trascorso, le differenti strategie adottate nei diversi scenari.

Supponendo di aver compilato tutte e 100 le classi e di aver aggiunto un riferimento al-l’assembly Class100.dll nella nostra applica-zione ASP.NET, che d’ora in avanti chiame-remo per comodità WebGenerate, vediamo i differenti scenari in cui fare i test e per i quali sarà interessante osservare in termini di tem-po quanti millisecondi trascorrono prima di vedere la stringa “something” apparire nella nostra TextBox.

Scenario 1 - “No Gac, No Native Images”Nel primo scenario non metteremo nulla in

GAC e non genereremo alcuna immagine nati-va. Insomma la situazione tipica di una applica-zione ASP.NET con esecuzione side-by-side.

Effettueremo in questo scenario, così come nei successivi, un totale di tre 3 prove.

Scenario 1 - Prova 1“First Execution, No Gac, No Native Ima-

ges, No Temporary ASP.NET Files”Per questa prima prova, ci dovremo as-

sicurare che la directory C:\WINDOWS\Microsof t .NET\Framework\v1.1.4322\Temporary ASP.NET Files sia vuota (elimi-natene tranquillamente tutto il contenuto ma NON la cartella stessa e solo dopo aver riav-viato IIS – iisreset.exe) quindi riavviamo il ser-vizio IIS, proprio per simulare il nostro ormai famigerato “primo avvio”. Se ora tentassimo

di accedere tramite browser alla nostra WebForm1.aspx avrem-mo un risultato logicamente non corretto, in quanto il riavvio di IIS non rimette automatica-mente in esecuzione il proces-so w3wp.exe.

Teniamo dunque in considera-zione questo aspetto e superia-molo con un piccolo stratagem-ma. Creiamo un’ulteriore e ba-nale applicazione ASP.NET che faccia da “gateway” al solo sco-po di scopo di assicurare che i processi w3wp.exe e aspnet_wp.exe siano già in esecuzio-ne quando approderemo alla WebForm1.aspx dell’applica-zione WebGenerate.

Visto che ci siamo faremo in modo che questa ulteriore ap-plicazione ASP.NET, che d’ora in avanti chiameremo PreWebGe-

Fi gu ra 3 Le 101 cartelle generate dal codice

GAC

Page 13: v2005 04 vbj64

13N. 64 - Luglio/Agosto 2005 VBJ

nerate, “ci porti” lei stessa e attraverso una Response.Redirect(…), alla WebForm1.aspx della applicazione WebGenerate.

PreWebGenerate avrà anch’essa un’unica Web Form, denominata WebForm1.aspx, con all’interno un controllo link button, (LinkBut-ton1) con il codice (vedi Listato 4) in rispo-sta all’evento di click.

Questa funzione scriverà in verità per prima il fi le times.txt, inserendo come nel caso pre-cedente i millisecondi intercorsi tra la mezza-notte del 1 gen 1900 e la data ed ora attua-le, e facendo una semplice sottrazione con il valore scritto dall’applicazione WebGenera-te otterremo i millisecondi impiegati dal .NET Framework per la “preparazione” all’esecu-zione dell’applicazione WebGenerate stessa e cioè la creazione dei fi le necessari nella car-tella Temporary ASP.NET Files, esclusi i tempi di esecuzione dei metodi Method() delle 100 clas-si. Apriamo dunque il browser e puntiamo all’in-dirizzo web: http://localhost/PreWebGenerate/WebForm1.aspx

Ora facendo click sul controllo LinkButton1 in-vocheremo l’applicazione WebGenerate che fi -nalmente compilerà tramite JIT prima l’assem-bly di code-behind (WebGenerate.dll) e quindi a cascata gli assembly Class100.dll, Class99.dll … fi no all’ultimo assembly Class1.dll, dando vita alla comparsa della stringa “something” nel browser; se farete voi stessi la prova tenetevi dentro la sensazione del tempo trascorso.

Nel fi le c:\times.txt troveremo, al termine dei test, 3 valori numerici separati dal caratte-re punto e virgola (;) e nel seguente formato: valore1;valore2;valore3.

Se chiamiamo diff1 e diff2 rispettivamente valore2-valore1 e valore3-valore2, possiamo affermare che:

• diff1 conterrà il tempo totale, espresso in millisecondi, necessario alla creazione del-le cartelle e dei fi le temporanei nella folder Temporary ASP.NET Files più il tempo, sem-pre espresso in millisecondi, necessario al JIT per compilare tutti gli assembly coinvol-ti dall’applicazione WebGenerate (101 as-sembly).

• diff2 conterrà invece i millisecondi ne-cessari alla esecuzione di tutto il codice in questione e dunque dei tempi di ese-cuzione dei diversi metodi Method() del-le 100 classi più il codice presente nel code-behind.

Prima di procedere oltre, verifi cate che il fi le times.txt esista nella root del disco C (ricordate-vi i permessi) e che effettivamente contenga i 3 valori di cui sopra. Verifi cate inoltre che la car-tella Temporary ASP.NET Files contenga nelle sue sottodirectory, 101 sotto cartelle.

Per trovare le 101 cartelle dovrete entrare nel-la directory WebGenerate e quindi in ulterio-ri 2 cartelle (nei miei test il percorso comple-to è C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files\webgenerate\65eab3c7\fad2b067\assembly\dl2) come visualizzato in Figura 3; il percor-so esatto differirà da macchina a macchina, in quanto ASP.NET utilizza un meccanismo di no-menclatura basato su algoritmo di Hash e sulla chiave pubblica dell’assembly stesso.

GAC

Li sta to 2 Definizione della Class 100

using System; [assembly: System.Reflection.AssemblyKeyFile(“mykeys.snk”)]

namespace Generate { public class Class100 { public Class100() { } public string Method() { return new Class99().Method(); } } }

Li sta to 3 Alla fine delle iterazioni viene eseguita la Class 1

using System;[assembly: System.Reflection.AssemblyKeyFile(“mykeys.snk”)]namespace Generate{ public class Class1 { public Class1() { }

public string Method() { return “something”; } }}

Page 14: v2005 04 vbj64

14 VBJ N. 64 - Luglio/Agosto 2005

Scenario 1 - Prova 2 “Second Execution, No Gac, No Native Ima-ges, Temporary ASP.NET Files (101 Classes)”

Questa prova prevede semplicemente la chiusura e la riapertura del browser sulla pagi-na WebForm1.aspx dell’applicazione PreWeb-Generate, quindi facciamo click e attendiamo nuovamente la scritta “something”.

Questo test dovrebbe simulare successive richieste di utenti differenti (nuove sessioni) laddove l’applicazione non abbia subito mo-difi che e laddove IIS non sia stato mai riav-viato (ad esempio reboot della macchina o iisreset.exe).

Scenario 1 - Prova 3 “First Execution, No Gac, No Native Images, Temporary ASP.NET Files (101 Classes)”

Questa terza ed ultima prova di questo sce-nario prevede solo il riavvio di IIS (iisreset.exe) senza alterare il contenuto della directory Temporary ASP.NET Files, dove sono già presenti i fi le .xml, .cs, .dll, ecc. contenuti nelle varie sotto cartelle temporanee.

Questo è una variante della Prova 2 e si ve-rifi ca ad esempio quando IIS viene riavviato o quando la macchina viene fatta ripartire ma non si è modifi cato il contenuto della directory Temporary ASP.NET Files e l’applicazione non ha subito ovviamente modifi che al codi-ce e/o non è stata mai ricompilata.

Al termine della prova entrate nella directory Temporary ASP.NET Files e osservate data ed ora di modifi ca delle .dll.

Vi accorgerete che il .NET Framework (ovve-ro ASP.NET), a prescindere dalla loro validità,

le ha rigenerate tutte e comunque! Questo meccanismo di compilazione può essere al-terato, per modalità e quantità, attraverso il tag <compilation> presente nel fi le di confi gu-razione machine.confi g del .NET framework, riportato nel Riquadro 1.

I valori di default, come si vede, sono 3.000 pagine .aspx oppure 3.000KB.Dato che è raro trovare in un’applicazio-ne ASP.NET più di 3.000 pagine ovve-ro pagine la cui dimensione superi in to-tale i 3.000KB, la nostra applicazione su-birà nella maggior parte dei casi una compilazione completa e indiscriminata.Modifi cate all’occorrenza questi valori per ot-tenere i due possibili ed opposti comporta-menti; aumentati questi valori se l’applicazio-ne è ad esempio composta da più di 3.000 pagine, assicurandovi dunque una compila-zione completa in start-up.

Perderemo qualche istante in più all’inizio, benefi ciando però in seguito di codice già compilato.

Oppure riduciamo all’osso questi valori, di-stribuendo così nel tempo il carico di com-pilazione.

Scenario 2 - “Gac, No Native Images”Analogamente allo Scenario 1 condurremo

anche qui le stesse tre prove ma stavolta re-gistrando in GAC tutti e 100 gli assembly (Class100.dll, Class99.dll, … Class1.dll).

Questa scenario prende spunto dall’osser-vazione che tutto quello che sta in GAC o eventualmente in directory condivise tra-mite codebase, non viene mai copiato nel-

GAC

Tabella 1 I tempi calcolati sul PC dell'autore

c:\times.txt

3324018372781,25;3324018383453,13;3324018383468,75

3324018575078,13;3324018575078,13;3324018575078,13

3324018661859,38;3324018677140,63;3324018677156,25

3324019487906,25;3324019488968,75;3324019489609,38

3324019663046,88;3324019663046,88;3324019663046,88

3324019748125,00;3324019748906,25;3324019749500,00

3324020183125,00;3324020184218,75;3324020185031,25

3324020275984,38;3324020275984,38;3324020275984,38

Page 15: v2005 04 vbj64

15N. 64 - Luglio/Agosto 2005 VBJ

la directory del chiamante.Ovviamente questo procedi-mento introduce operazioni aggiuntive in deployment e non sono consigliabili duran-te la normale fase di test.

Scenario 2 - Prova 1 “First Execution, Gac, No Native Images, No Tem-porary ASP.NET Files (1 Class)”

Se utilizzate Visual Studio .NET ricordatevi di registrare in GAC gli assembly (nel codi-ce allegato all’articolo c’è un fi le batch GacAll.bat che lo fa per voi) e quindi ricordatevi di togliere e aggiungere nuo-vamente il riferimento all’as-sembly Class100.dll nell’ap-plicazione ASP.NET WebGe-nerate.

Quindi ricompiliamo, facciamo ripartire IIS tramite iisreset.exe e svuotiamo l’intera di-rectory Temporary ASP.NET Files.

Riapriamo il browser e puntiamo nuovamente all’indirizzo: http://localhost/PreWebGenerate/WebForm1.aspx. Ora se andate a controllare la directory Temporary ASP.NET Files trovere-te con grande stupore solo UNA (1) cartella, quella dell’assembly di code-behind.

Quanto tempo avremo risparmiato? Le consi-derazioni sui tempi vediamole alla fi ne, intanto terminiamo tutte le prove necessarie.

Il fi le c:\times.txt viene scritto in append, quindi prenderemo tutti i dati alla fi ne dei test.

Scenario 2 - Prova 2 “Second Execution, Gac, No Native Ima-ges, Temporary ASP.NET Files (1 Class)”

Stesse modalità dello Scenario 1 – Prova 2.

Scenario 2 - Prova 3 “First Execution, Gac, No Native Images, Temporary ASP.NET Files (1 Class)”

Stesse modalità dello Scenario 1 – Prova 3.

Scenario 3 - “Gac, Native Images”Analogamente allo Scenario 1 e 2 condurre-

mo anche qui le stesse tre prove ma stavolta non solo tenendo in GAC tutti e 100 gli assem-bly (Class100.dll, Class99.dll, … Class1.dll) ma

generando con ngen.exe le rispettive 100 im-magini native.

Questa scenario prende spunto dalle featu-res che il tool ngen.exe offre, ed ovvero, com-pilare il codice nativo senza scomodare il JIT a run-time.

Scenario 3 - Prova 1 “First Execution, Gac, Native Images, No Temporary ASP.NET Files (1 Class)”

Se utilizzate Visual Studio.NET ricordatevi, la-sciando tutto in GAC, di generare le immagi-ni native dei 100 assembly e solo dopo di to-gliere e re-inserire il riferimento all’assembly Class100.dll nell’applicazione ASP.NET Web-Generate.

In realtà questo passo è obsoleto in quanto, per Visual Studio .NET è ininfl uente l’esistenza o meno di immagini native in fase di compila-zione; se ne accorgerà solo il CLR - Common Language Runtime - appunto a run-time.

Nel codice allegato all’articolo c’è un fi le ba-tch NativeAll.bat che lo fa per voi.

Quindi ricompiliamo, facciamo ripartire IIS (iisreset.exe) e svuotiamo l’intera directory Tem-porary ASP.NET Files.

Riapriamo il browser, puntiamo nuovamente all’indirizzo: http://localhost/PreWebGenerate/WebForm1.aspx e procediamo come già illu-strato in precedenza. Ora se andate a control-lare la directory Temporary ASP.NET Files trove-rete sempre e solo una (1) cartella, quella del-l’assembly di code-behind.

GAC

Fi gu ra 4 I risultati del test in forma grafica

Page 16: v2005 04 vbj64

16 VBJ N. 64 - Luglio/Agosto 2005

Ancora una volta le considerazioni sui tempi te-niamocele per la fi ne … siamo quasi arrivati.

Scenario 3 - Prova 2 “Second Execution, Gac, Native Images, Temporary ASP.NET Files (1 Class)”

Stesse modalità degli altri Scenari – Prova 2.

Scenario 3 - Prova 3 “First Execution, Gac, Native Images, Tem-porary ASP.NET Files (1 Class)”

Stesse modalità degli altri Scenari – Prova 3.

Risultati

Raccogliamo ora il frutto delle nostre prove, andando a prelevare con molta cura il conte-nuto del fi le c:\times.txt.

Ora potete rimuovere tutte le immagini native e tutti gli assembly dalla GAC tramite due fi le batch che trovate nel materiale di corredo al-l’articolo: NoNativeAll.bat e NoGacAll.bat.

Portiamo questi numeri sotto un foglio di calcolo, facciamo le dovute sottrazioni per ricavare i valori diff1 e diff2 delle 9 prove ef-fettuate (3 per scenario), e facciamo le nostre brave considerazioni sui tempi ottenuti.

Chiamiamo inoltre tot la somma tra diff1 e diff2, che sarà per noi il tempo totale tra ge-nerazione, compilazione ed esecuzione in mil-lisecondi, della nostra applicazione WebGe-nerate.

In Tabella 1 trovate il contenuto del mio fi le c:\times.txt ed in Tabella 2 i valori diff1, diff2 e tot desunti dalle prove effettuate sulla mia macchina.

ConclusioniSe avete avuto la pazienza di arrivare fi n qui,

le conclusioni sono a questo punto estrema-mente semplici ma allo stesso tempo estrema-

mente interessanti.Dalla Figura 4 e della Tabella 2 si evince che:

• Lo Scenario 2, ovvero tutto in GAC e senza immagini native offre le perfor-mance migliori: 1,7 sec. circa al “pri-mo avvio” contro i ben 10,7 sec. cir-ca dello Scenario 1 (un’eternità).Ovviamente il grosso del risparmio lo ab-biamo ottenuto grazie al fatto che nello Scenario 2 abbiamo evitato al processo aspnet_wp.exe, in virtù dell’esecuzione side-by-side (default), di “copiare” 100 assembly in più nella directory Tempo-rary ASP.NET Files e di doverne verifi ca-re nuovamente l’integrità, verifi ca già ef-fettuata in fase di registrazione.

Nel caso invece di una assembly condiviso tra-mite codebase saremmo noi ad indicare tramite il tag <assemblyIdentity name=”MyAssembly.dll” publicKeyToken=”xxxxxxxxxxxxxx” … />, iden-tià e posizione dell’assembly in questione.In totale avremo solo 19 fi le in 7 car-telle, relativi al solo assembly di code-behind, anziché 220 fi le in 207 car-telle come nel caso dello Scenario 1.Confrontando il valore diff2 dello Sce-nario 2 con lo stesso dello Scena-rio 1, osserviamo però che il tem-po di esecuzione necessario nel-le successive richieste è aumentato.Questo fenomeno potrebbe essere dovu-to ai meccanismi di individuazione che il run-time utilizza per “pescare” un assem-bly dalla GAC. • Lo Scenario 3 si comporta addi-rittura peggio dello Scenario 2 (ma … come … ngen.exe?… si proprio lui).

GAC

Li sta to 4 Il codice che risponde al click

private void LinkButton1_Click(object sender, System.EventArgs e) { StreamWriter sw=new StreamWriter(“c:\\times.txt”,true); DateTime startdate=new DateTime(1900,1,1,0,0,0,0); DateTime enddate=DateTime.Now; TimeSpan diff=(TimeSpan)enddate.Subtract(startdate); sw.Write(“{0};”,diff.TotalMilliseconds.ToString()); sw.Close(); Response.Redirect(“http://localhost/WebGenerate/WebForm1.aspx”); }

Vi siete mai chiesti perché

il “primo avvio” di una ap-

plicazione ASP.NET impiega

più tempo dei successivi?

Page 17: v2005 04 vbj64

17N. 64 - Luglio/Agosto 2005 VBJ

GAC

Questo dimostra come vere le perples-sità prima ipotizzate sull’utility ngen.exe.Il JIT compila ed applica delle ottimizzazio-ni di compilazione a JIT-compile-time, che ngen.exe, a native-compile-time, non può nemmeno lontanamente immaginare di fare.Inoltre è noto che le immagini nati-ve restano confi nate nel loro Applica-tion Domain e quindi comunque non po-trebbero essere utilizzate da ASP.NET che è invece Cross Application Domain.Notate che la differenza tra Scenario 3 e Sce-nario 2, in tutte e tre le prove è di circa 0,2 secondi quindi abbastanza trascurabile; ma perché perderli? E soprattutto perché dover rigenerare a manina le immagini native a fron-te di ricompilazione degli assembly?

• Lo Scenario 1 segue in coda con le peggio-ri performance di start-up in assoluto (ov-vero quello che tutti noi di solito usiamo).ASP.NET perde la maggior parte del tempo a “copiare” i 100 assembly ed a creare tutta la struttura su fi le system (vedi prova 3 dello sce-nario 1), nella directory Temporary ASP.NET Files\WebGenerate - 220 fi le in 207 cartelle.

Ma perché vengono copiati nella directory Temporary ASP.NET Files\WebGenerate\...? Non potrebbero restare lì dove sono?Questo comportamento è dovuto al fatto che l’assembly di code-behind della nostra appli-cazione WebGenerate verrà preso in carico dal processo aspnet_wp.exe nella directory Temporary ASP.NET Files\WebGenerate\... e non nella directory \bin, meccanismo ne-cessario alla “fusione” tra codice di scrip-ting lato server e code-behind. Questa fu-sione non può essere attuata nella cartel-la \bin (non ci piace che la nostra cartel-la venga “sporcata”), occorre invece una cartella temporanea: la directory Tempo-rary ASP.NET Files\WebGenerate appunto.Questo è tra l’altro il meccanismo che ci consente di ricompilare una applicazio-ne ASP.NET mentre essa è in esecuzio-ne; l’assembly nella directory \bin è infatti sempre libero da ogni vincolo di ownership. Ora, visto che:

• quando un assembly a fa richiesta di un assembly b … b deve essere portato nel-

Tabella 1 I risultati del test sul PC dell'autore

Valori diff1, diff2 e tot a confronto

(a) (b) (a+b) Note

diff1 diff2 tot

Scenario 1 No Gac, No Native Images

Prova 1 10.671,88 15,62 10.687,50 First Execution, No Temporary ASP.NET Files

Prova 2 0,00 0,00 0,00Second Execution, Temporary ASP.NET Files

(101 Classes)

Prova 3 15.281,25 15,62 15.296,87First Execution, Temporary ASP.NET Files

(101 Classes)

Scenario 2 Gac, No Native Images

Prova 1 1.062,50 640,63 1.703,13 First Execution, No Temporary ASP.NET Files

(1 Class)

Prova 2 0,00 0,00 0,00 Second Execution, Temporary ASP.NET Files

(1 Class)

Prova 3 781,25 593,75 1.375,00 First Execution, Temporary ASP.NET Files (1

Class)

Scenario 3 Gac, Native Images

Prova 1 1.093,75 812,50 1.906,25First Execution, No Temporary ASP.NET Files

(1 Class)

Prova 2 0,00 0,00 0,00Second Execution, Temporary ASP.NET Files

(1 Class)

Prova 3 750,00 781,25 1.531,25First Execution, Temporary ASP.NET Files (1

Class)

Page 18: v2005 04 vbj64

18 VBJ N. 64 - Luglio/Agosto 2005

la stessa directory (o sotto directory) del-l’assembly a (a meno di non usare un co-debase o la GAC).

• nel caso della nostra applicazione (Web-Generate), l’assembly è WebGenerate.dll che ormai vive in Temporary ASP.NET Files\WebGenerate\....

• L’assembly richiesto è, nel primo caso, Class100.dll, quindi questo viene copiato anch’esso in una sotto directory dell’as-sembly richiedente (Temporary ASP.NET Files\WebGenerate\....).

• L’assembly Class100.dll fa poi richiesta a sua volta di Class99.dll … e in cascata vengono quindi copiati tutti in Temporary ASP.NET Files\WebGenerate\....

È proprio questa operazione di “copia” ed “in-colla” che rende il nostro famigerato “primo avvio” lento … lentissimo.

Successive richieste, fatte alla stessa appli-cazione, a patto di non aver alterato il conte-nuto della Temporary ASP.NET Files, saran-no estremamente più performanti perché non innescheranno più lo stesso meccanismo, in quanto tutto l’occorrente è già nelle directory in questione.

Altre considerazioni possono essere fatte su un altro ipotetico scenario – direi quasi maso-chistico – che mi sono guardato bene dal pre-sentarvi ed ovvero: niente in GAC … ma tutto con immagini native!

Se ci provate, otterrete performance ancora peggiori dello Scenario 1, per tutte le consi-derazioni fatte fi n ora e che quindi potete ben immaginare.

Per dovere di cronaca vi dico inoltre che i tem-pi non subiscono variazioni di rilievo passan-do dalla compilazione in modalità DEBUG, alla compilazione in modalità RELEASE (i tempi che ho riportato in tabella si riferiscono alla compi-lazione in modalità RELEASE).

Tali conclusioni dovrebbero portarci anche a capire quale è stato il motivo per il quale, in fase di installazione del .NET Framework, alcu-ni assembly siano stati registrati anche in for-mato nativo. La presenza di un assembly con immagine nativa interrompe il meccanismo di JITting in cascata sugli assembly da questo re-ferenziati. Sarebbe infatti dispendioso per il JIT tutte le volte ricompilare a run-time ad esem-pio, l’assembly System.dll.

In conclusione non voglio impartire istruzio-ni su cosa fare, ad esempio condividere tut-to con codebase o pubblicare tutto in GAC o non utilizzare mai ngen.exe. Spero soltanto di aver acceso una piccola luce sui meccanismi che ASP.NET innesca dietro le quinte, quan-do un’applicazione web parte la prima volta; lascio a voi ogni considerazione sul da farsi, caso per caso.

Solo a partire dal .NET Framework 2.0, sarà possibile fi nalmente e veramente pre-compila-re le applicazioni ASP.NET e soprattutto creare immagini native attraverso una nuova versione del tool ngen.exe, che promette stavolta dav-vero miracoli.

Vi rimando, in bibliografi a, ad un articolo mol-to interessante di Reid Wilkes - MSDN Maga-zine, aprile 2005 – che anticipa le caratteristi-che salienti e più interessanti della nuova ver-sione del tool ngen.exe presente nell’SDK del .NET Framework 2.0.

Bibliografi a

[1] Jeffrey Richter – “JIT Compilation and Per-formance - To NGen or Not to NGen ?”, http://www.codeguru.com/Csharp/.NET/net_general/toolsand3rdparty/article.php/c4651

[2] Dino Esposito – “Programmare Micro-soft Asp.Net”, Mondadori Informatica, Chapter 2 – Web Form Internals - http://www.dotnet2themax.com/ShowContent.aspx?ID=750fdf64-9ff5-460f-85b2-908d7941e0fe&Page=0&#RET

[3] G. Di Mauro, F. Balena - “Practical Guide-lines and Best Practices for Microsoft Visual Basic and Visual C# Developers”, Microsoft Press.

[4] Reid Wilkes - “NGen Revs Up Your Per-formance with Powerful New Features”, MSDN Magazine April 2005, http://msdn.microsoft.com/msdnmag/issues/05/04/NGen/default.aspx

GAC

L’obiettivo principe è

capire ed ottimizzare il

“primo avvio” di una

applicazione ASP.NET

Page 19: v2005 04 vbj64

19N. 64 - Luglio/Agosto 2005 VBJ

Le novità VBnel nuovoVisual Studio

di Lorenzo Vandoni

Nel momento in cui scrivo è stata resa dispo-nibile la seconda beta della nuova versione di Visual Studio 2005. Molte, come spesso ca-

pita, sono le novità. In questo articolo cercheremo di concentrarci sugli aspetti sintattici e semantici relati-vi al linguaggio Visual Basic.

L’avvento dei generics

La novità principale è costituita dalla possibilità di uti-lizzare la programmazione generica. In pratica, diventa possibile creare delle classi del tipo “lista di X”, dove X è un tipo qualsiasi, oppure delle funzioni di ordina-mento in grado di ordinare oggetti di classi diverse. La programmazione generica permette di creare fun-zioni e classi in grado di utilizzare oggetti di un gene-rico tipo T, offrendo la possibilità di validarne la cor-rettezza durante la compilazione. I vantaggi principali di questo approccio, già discusso in questa rubrica, sono quelli di consentire una maggiore effi cienza, e di permettere un maggiore controllo del codice. Non sarà possibile, infatti, introdurre delle stringhe all’interno di una classe defi nita come lista di numeri interi. La de-fi nizione di una classe generica in Visual Basic.NET può essere scritta in questo modo:

Public Class Lista(Of T)

Private moItem() As T

Public Sub Add(ByVal oItem As T)

...

End Sub

End Class

La parola chiave Of serve ad in-dicare un parametro di tipo gene-rico, in questo caso rappresenta-to dal marcatore T. Per usare que-sta classe si potrà scrivere:

Dim oLista1 As New Lista(Of Integer)

Dim oLista2 As New Lista(Of String)

La dichiarazione di questi due oggetti provocherà due diverse istanziazioni della classe gene-rica, in cui il marcatore T verrà rispettivamente sostituito dai tipi Integer e String. In questo modo, una chiamata del tipo

oLista1.Add(“abc”)

provocherà un errore di compi-lazione. È possibile inoltre impor-re vincoli sul tipo generico T, spe-cifi cando che debba implementare una specifi ca interfaccia, o derivare da una classe base, scrivendo:

La nuova versione di Visual Studio porta con sé diverse interessanti novità relative al linguaggio Visual Basic

Lorenzo Vandoni è laureato in Informatica, ed è uno spe-cialista di progettazione e sviluppo con tecniche e linguaggi object-oriented. Ha collaborato alla realizzazione di framework, strumenti di sviluppo e software commerciali in C++, Java e Visual Basic. Può essere contattato tramite e-mail all’indirizzo [email protected].

VB.NET

Page 20: v2005 04 vbj64

20 VBJ N. 64 - Luglio/Agosto 2005

Public Class GenericClass(Of T As Base)

La scrittura di una funzione generica può es-sere effettuata in questo modo:

Sub Sort(Of T)(ByVal oArr() As T)

...

End Sub

La funzione potrà essere poi richiamata scrivendo:

Sort(Of Integer)(oIntArray)

La gestione degli operatori

La possibilità di sovrascrivere e personalizzare la gestione degli operatori non aggiunge di fatto un nuovo tipo di funzionalità al linguaggio, ma permette di progettare in modo più elegante l’in-terfaccia di alcune classi. Per esempio, suppo-nendo di voler dotare una nostra classe Pun-to di un operatore di somma, non saremo più costretti a creare un metodo Add, ma potremo sfruttare l’operatore +, permettendo agli utenti della classe di scrivere, ad esempio:

Dim a As Punto(3,4)

Dim b As Punto (5,6)

Dim c As Punto = a + b

invece di

Dim c As Punto = a.Add(b)

Per sovrascrivere un operatore nel contesto di una nostra classe è suffi ciente implementa-re un metodo nel modo seguente:

Public Operator +(ByVal p1 As Punto, ByVal p2 As Punto)

As Punto

Come si vede, occorre utilizzare la parola chia-ve operator immediatamente prima del nome dell’operatore che si intende sovrascrivere.

I tipi parziali

Gli sviluppatori Visual Basic, fi no alla versione 6, erano abituati a far coincidere la nozione di classe o form con la nozione di fi le: ogni fi le cls o frm cor-rispondeva ad una classe o ad una form, e la de-fi nizione di ogni classe o form era completamente inclusa all’interno di un solo fi le. Con l’avvento di .NET, è diventato possibile includere la defi nizione

di più classi, e potenzialmente anche di un’intera applicazione, all’interno di un unico fi le vb. Con Vi-sual Basic 2005 diventa possibile anche fare l’ope-razione inversa, cioè suddividere l’implementazio-ne di un’unica classe tra più fi le vb. Questa pos-sibilità, fi nora disponibile solo in C++, può essere utile solo in casi molto particolari. Uno di questi, però, risulta particolarmente importante. Si tratta del caso delle classi il cui codice viene generato automaticamente da Visual Studio, come le form. Nelle versioni precedenti di Visual Studio, il codi-ce generato veniva posto in una particolare regio-ne all’interno dello stesso fi le vb dove sarebbe sta-to poi scritto il codice da parte dello sviluppatore. Questo codice viene ora posto, invece, all’interno di un fi le separato, chiamato nomefi le.designer.vb. Al di là di questa particolarità, la possibilità di uti-lizzare i tipi parziali può essere utile anche per per-mettere a più sviluppatori di intervenire contempo-raneamente sul codice di una stessa classe, o per suddividere in più sezioni fi siche parti logicamente distinte di una classe di grosse dimensioni.

Le altre novità

Oltre ai generics, alla gestione degli operatori e ai partial type, la nuova versione di Visual Basic porterà con sé altre interessanti novità, elenca-te brevemente nei punti successivi, seguendo un personalissimo ordine di preferenza:

• Finalmente è disponibile la parola chiave Con-tinue, che permette di passare direttamen-te al ciclo successivo di un loop, saltando tutte le righe di codice comprese tra questa istruzione e la fi ne del loop. I programmato-ri C/C++ conoscono bene questa istruzione, mentre i programmatori Visual Basic hanno da sempre avuto a disposizione solo l’istru-zione exit, che permette di uscire dal loop, e hanno dovuto simulare la continue con qual-che stratagemma, il più comune dei quali è un GoTo ad una label posta immediatamen-te prima della successiva iterazione.

• I commenti XML, già disponibili in C# nella versione precedente di Visual Studio, per-mettono di aggiungere commenti al codice utilizzando una speciale sintassi basata su XML. La stesura di commenti in questo for-mato viene supportata dall’editor, senza la ne-cessità di apprenderne la sintassi, e consente di ottenere in modo automatico la documen-tazione di progetto e informazioni aggiuntive da visualizzare tramite Intellisense.

VB.NET

Page 21: v2005 04 vbj64

21N. 64 - Luglio/Agosto 2005 VBJ

VB.NET

• Un’altra interessante innovazione è costi-tuita dalla parola chiave My, che fornisce un percorso di accesso alternativo a mol-te tra le classi più utilizzate del framework. Per esempio si può scrivere My.User.Identity per accedere alle informazioni relative al-l’utente dell’applicazione. In modo analo-go, My.Computer e My.Application permet-tono di accedere rapidamente alle informa-zioni relative al sistema e all’applicazione.

• La parola My può essere anche utilizzata per accedere da programma agli elementi del pro-getto. Per esempio, si può aggiungere un ele-mento all’insieme dei Settings del progetto, e accedere a quell’elemento da programma scrivendo My.Settings.<NomeElemento>

• A molti sarà capitato di utilizzare operazio-ni di cast non sicure, cioè di dovere esegui-re alcune operazioni dipendenti dal tipo di una variabile, senza conoscere esattamente il tipo di quest’ultima. Per potere verifi care la correttezza del cast era fi nora necessa-rio gestire l’eventuale eccezione all’interno di un blocco try, in questo modo:

Try

Dim y as MyType = CType(x,MyType)

Catch

End Try

Viene ora offerta la possibilità di utilizzare una scorciatoia, costituita dall’istruzione TryCast, che consente di verifi care l’avvenuta conversione sem-plicemente controllando che il valore della variabile di destinazione non sia uguale a Nothing:

Dim y as MyType = TryCast(x,MyType)

If y IsNot Nothing Then

In questo frammento di codice si può vede-re anche un’altra delle novità minori introdotte con Visual Basic 2005, cioè la possibilità di uti-lizzare il nuovo operatore IsNot invece di scri-vere If Not y Is Nothing.

• È ora possibile associare regole di visibilità diverse alle due parti Get e Set di una stes-sa proprietà. Si può fare in modo, cioè, che la parte Get sia pubblica mentre la parte Set sia privata o protetta.

• I tipi unsigned, in C, non mi sono mai piaciu-ti molto, li ho sempre considerati soprattutto come un modo per compiere errori più facil-mente. Non si può negare però che in alcuni

casi abbiano una loro utilità. Il fatto che siano stati resi disponibili anche in Visual Basic può essere comodo soprattutto nel caso in cui si debbano richiamare funzioni C che usino uno di questi tipi come parametro o valore di ritorno.

• Un’altra cosa che non mi è mai piaciuta mol-to, è la possibilità di utilizzare, fi no a Visual Basic 6, istanze “implicite” di form, scriven-do ad esempio

MyForm.Show

invece di

Dim x as New MyForm

x.Show

Con Visual Basic 2005, questa possibilità è stata reintrodotta.

L’ambiente di sviluppo

Al di là degli aspetti strettamente legati al lin-guaggio, la nuova versione di Visual Studio por-terà con sé diverse nuove funzionalità. Tra queste, vale sicuramente la pena di citare la possibilità, cara ai programmatori Visual Basic, di modifi ca-re il codice durante il debug. Questa funzionali-tà, non disponibile in Visual Studio 2003, è sta-ta nuovamente introdotta con la versione 2005. Sono stati inoltre introdotti diversi miglioramenti nell’editor di codice, che ora è in grado, come nel caso degli strumenti Offi ce, di suggerire possibili correzioni per diversi tipi di errore, e di mostrare tramite tooltip i valori di variabili anche complesse durante le operazioni di debug. Anche la fi nestra Immediata è stata potenziata, e ora può essere utilizzata, come con Visual Basic 6, anche quan-do il programma non è in esecuzione.

Conclusioni

Abbiamo visto una veloce carrellata, non esausti-va, delle principali novità introdotte da Visual Ba-sic 2005. Alcune sono sicuramente interessanti, altre sono solo delle curiosità. La maggior parte di queste novità non sono particolarmente origi-nali, ma solo adattamenti di strumenti già presen-ti in altri linguaggi, o reintroduzioni di funzionalità già presenti in Visual Basic 6. Con queste nuove aggiunte, Visual Basic diventa un linguaggio piut-tosto completo, avendo eliminato molte limitazio-ni senza per questo perdere quella semplicità di utilizzo che lo ha reso popolare.

Page 22: v2005 04 vbj64

22 VBJ N. 64 - Luglio/Agosto 2005

Un motore di scripting per l’ambiente .NET

di Filippo Bonanni

Ho sempre considerato le tecnologie di scripting tra gli strumenti più utili messi a disposizione di un programmatore; ormai non conto più le

volte che ho utilizzato un fi le VBScript o JScript per risolvere piccole (ma non solo) esigenze: disporre di fi le di testo contenenti codice che viene elaborato a run-time da un interprete, facilmente modifi cabili e distribuibili, ha sicuramente contribuito a migliorare la mia vita di sviluppatore.

Purtroppo queste tecnologie presentano anche dei limiti, primo fra tutti l’utilizzo di un linguaggio non ti-pizzato, pertanto con l’avvento di .NET confi davo di trovare un epigono del Windows Scripting Host che mi permettesse di abbinare l’immediatezza dello scrip-ting alla potenza del codice “managed”.

Invece, per rimpiazzare i vecchi VBScript mi sono ritrovato a scrivere, oltre al codice, fi le batch per la compilazione, lasciandomi una sensazione di mal-contento ogni volta che editavo, ricompilavo ed ese-guivo. Il programma descritto in questo articolo è il frutto di tale insoddisfazione: un motore di scripting per .NET in grado di compilare al volo ed eseguire fi le di testo contenenti codice VB.NET.

Per rendere l’utilizzo assolutamente trasparente, ol-tre alla realizzazione dell’applicazione vedremo anche come creare un semplice setup che ci permetta di abbinare al programma, dei fi le con una particolare

Filippo Bonanni Si occupa di informatica dal 1998 ed al momento i suoi interessi sono rivolti principalmente all’am-biente .NET. Attualmente lavora nel team di sviluppo della D&T Informatica di Sesto Fiorentino in progetti web-based di interfacciamento a sistemi AS/400 e di archiviazione ottica in ASP.NET. Può essere contattato tramite e-mail all’indirizzo [email protected].

estensione che saranno compila-ti ed eseguiti sul classico doppio click del mouse.

Il programma completo di esem-pi e fi le di setup, chiamato Net-Script, è disponibile sul sito ftp di Infomedia; i fi le di script sono ri-conoscibili per l’estensione .vbns (Visual Basic Net Script).

Struttura del Programma

Come già discusso in un pre-cedente articolo (Computer Pro-gramming n. 146 “Un DTS con XML e la Refl ection di .NET”), la compilazione a run-time in .NET è ottenuta mediante l’ausilio del-le classi contenute nei namespa-ce System.CodeDom.Compiler e System.Refl ection (vedi [1]); ciò su cui ci focalizzeremo maggior-mente è l’utilizzo della Refl ection per ispezionare l’assembly com-pilato a run-time in cerca della classe contenente il punto d’in-gresso (la Sub Main) e la logica per referenziare in fase di com-pilazione assembly di terze parti non registrati nella GAC.

Il programma quindi è strut-turato secondo questa sempli-ce logica:

1. Recupero e lettura del fi le;

Scripting

Realizziamo un programma in grado di compilare ed eseguire al volo file di testo contenenti codice VB.NET

VB.NET

Page 23: v2005 04 vbj64

23N. 64 - Luglio/Agosto 2005 VBJ

bly da referenziare. Per farlo utilizziamo la classe CompilerParameters del namespace System.CodeDom.Compiler (vedi [1])

Dim CompParam as New CompilerParameters()

CompParam.GenerateExecutable=true

CompParam.GenerateInMemory = true

CompParam.IncludeDebugInformation = Debug

CompParam.ReferencedAssemblies.add(“System.dll”)

CompParam.ReferencedAssemblies.add(“System.Data.dll”)

CompParam.ReferencedAssemblies.add(“System.Xml.dll”)

CompParam.ReferencedAssemblies.add(“System.Drawing.dll”)

CompParam.ReferencedAssemblies.add(“System.Web.dll”)

CompParam.ReferencedAssemblies.add(“System.Windows.Forms.dll”)

CompParam.ReferencedAssemblies.add(“System.Management.dll”)

CompParam.ReferencedAssemblies.add(“Microsoft.VisualBasic.dll”)

Le prime tre righe specifi cano che creeremo un fi le di tipo eseguibile, che sarà eseguito in memoria (e quindi non salvato su disco) e che in base al valore della variabile booleana Debug (settata a True se abbiamo passato il parametro /d) potranno essere incluse le informazioni per il debug. Le altre righe invece aggiungono le refe-renze agli assembly più comuni contenuti nella GAC, così da avere ampia libertà nella scrittura del codice. Gli eventuali riferimenti ad assem-bly esterni sono specifi cati nella variabile strin-ga contenente il codice XML: dopo averla letta tramite un oggetto XmlDocument, utilizziamo un XmlNodeList e una semplice query Xpath (vedi [2]) per recuperare tutti i nodi-fi glio dell’elemen-to principale <references>

2. Compilazione con l’aggiunta di eventuali ri-ferimenti ad assembly esterni;

3. Recupero del punto d’ingresso ed esecu-zione del codice compilato.

I fi le di script

Il Listato 1 mostra un modello di fi le conte-nente il codice che verrà compilato ed esegui-to dal nostro programma.

Il codice VB.NET è composto dalla direttiva Option Explicit On, dalle importazioni di name-space e dalla classe MyApplication contenen-te il punto d’ingresso Sub Main. I riferimenti ad assembly “privati” sono dichiarati all’inter-no del codice mediante una struttura XML in-serita come righe di commento:

‘///<references>

‘/// <prv>C:\NetAssembly\test.dll</prv>

‘///</references>

Questa struttura sarà estrapolata dal codice, memorizzata in un oggetto XmlDocument e uti-lizzata per recuperare il percorso completo de-gli assembly da referenziare.

Recupero e lettura del fi le

Per recuperare ed eseguire il codice si usa la classica forma da riga di comando in cui si pas-sa al programma il percorso completo del fi le sor-gente “Netscript.exe c:\testfi le.vbns”, prevedendo anche il passaggio del parametro /d che ci per-metterà di specifi care se la compilazione debba avvenire in modalità debug.

La lettura avviene in due fasi tramite l’utilizzo di uno StreamReader: la prima fase recupera solo le righe che iniziano per ‘/// contenenti la struttura XML sopra descritta, verifi cando che siano rac-chiuse tra i due tag <references> e </references> e generando un’eccezione in caso contrario (Li-stato 2). La seconda fase non fa altro che recu-perare nuovamente il fi le ma stavolta per intero

sr.close()

sr=New StreamReader(filename)

Compilazione con l’aggiunta di eventuali riferimenti ad assembly esterni

Prima di compilare occorre fornire al Provider le informazioni riguardanti il tipo di fi le che ot-terremo (se eseguibile o meno) e gli assem-

Li sta to 1 Un file di script d’esempio

Option Explicit On

Imports SystemImports Microsoft.VisualBasic

‘-- ESEMPIO DI AGGIUNTA REFERENZE -- ‘-- RIMUOVERE SE NON NECESSARIO --‘///<references>‘/// <prv>C:\NetAssembly\test.dll</prv>‘///</references>

Public Class MyApplication Public Shared Sub Main() ‘Qui il nostro codice End SubEnd Class

VB.NET

Page 24: v2005 04 vbj64

24 VBJ N. 64 - Luglio/Agosto 2005

‘Carico in un XmlDocument il codice xml delle referenze

Dim doc as XmlDocument

Dim nodeList as XmlNodeList

doc=New XmlDocument()

doc.LoadXml(xml)

‘recupero tutti i nodi-figlio del nodo principale

nodeList=doc.SelectNodes(“/references/*”)

Compiendo un ciclo su tutti i nodi-fi glio tro-vati, recuperiamo il testo contenuto negli ele-menti <prv> che indica il percorso dell’as-sembly.

Per referenziarlo correttamente, non basta aggiungerlo alla lista come abbiamo fatto per gli assembly della GAC, occorre anche che sia contenuto in una directory che verrà scan-sionata dal Common Language Runtime du-rante il caricamento dei medesimi.

Tipicamente la directory predefi nita è la bin contenuta all’interno della path principale del programma. Visto però la natura temporanea

dell’assembly che dobbiamo importare è me-glio specifi care una directory alternativa nella quale copieremo il fi le specifi cato nell’elemen-to <prv> ; per farlo ricorriamo al fi le .confi g e all’elemento <probing> (vedi [1]) che permette di specifi care ulteriori percorsi di ricerca

<configuration>

<runtime>

<assemblyBinding xmlns=”urn:schemas-microsoft-

com:asm.v1”>

<probing privatePath=”bin;bin_temp;”/>

</assemblyBinding>

</runtime>

</configuration>

L’assembly sarà sovrascritto ogni volta che eseguiremo lo script onde evitare problemi di disallineamento tra la versione già presente nella directory temporanea e quella da refe-renziare; se il fi le è trovato e copiato corretta-mente viene aggiunto alla lista dei riferimenti. La ricerca dell’assembly è effettuata prima di

tutto come se all’in-terno del tag <prv> fosse specifi cato il percorso comple-to poi, nel caso in cui il fi le non ven-ga trovato, ante-ponendo il percor-so in cui risiede lo script che si sta eseguendo (Lista-to 3). Per la com-pilazione utilizziamo la classe VBCode-Provider contenu-ta nel namespace Microsoft.VisualBasic (vedi [1]): tale clas-se, tramite il metodo VBCodeProvider.CreateCompiler() forni-sce un’istanza del compilatore di codi-ce Visual Basic che utilizzeremo, insieme ai parametri specifi -cati in CompParam, per compilare il co-dice sorgente ed ot-tenere il programma da eseguire

Li sta to 2 La lettura del codice avviene in due fasi tramite l'utilizzo di uno

StreamReader

Dim sr as StreamReadersr=New StreamReader(filename)

‘Ciclo per recuperare la struttura xml di referenze agli assemblyDo While sr.Peek() >= 0

strTemp=sr.ReadLine()

If strTemp.IndexOf(“’///”)>=0 Then strTemp=strTemp.SubString(strTemp.IndexOf(“’///”)+4)

‘segnalo che sto leggendo il tag di apertura If strTemp.StartsWith(“<references>”) Then GetReference=True End If

‘Se ho trovato il tag di apertura recupero le righe successive If GetReference Then xml+=strTemp.Replace(“’///”,””) & VbCrLf End If ‘segnalo che sto leggendo il tag di chiusura If strTemp.StartsWith(“</references>”) Then GetReference=False End If End IfLoop

If GetReference Then Throw New XmlReferenceException(String.Format(“L’elemento <references> non è stato chiuso con </references>”))

VB.NET

Page 25: v2005 04 vbj64

25N. 64 - Luglio/Agosto 2005 VBJ

Dim Provider as New VBCodeProvider

Dim Comp as ICodeCompiler=Provider.CreateCompiler()

Dim CompRes as CompilerResults

CompRes=Comp.CompileAssemblyFromSource(CompParam,

sr.ReadToEnd())

Dopo la compilazione CompRes contiene l’as-sembly o, nel caso di errori, una lista delle eccezioni generate che potranno essere mostrate a video

If CompRes.Errors.Count > 0 Then

For i=1 to CompRes.Output.Count-1

Console.WriteLine(CompRes.Output.Item(i))

Next

Else

‘Qui la procedura di esecuzione dello script descritta

più avanti

End If

Recupero del punto d’ingresso ed esecu-zione del codice compilato

Per recuperare il punto di ingresso è suffi cien-te leggere la proprietà EntryPoint relativa all’as-sembly appena generato

Dim EntryPoint as String

EntryPoint=CompRes.CompiledAssembly.EntryPoint.Name

La proprietà restituisce un oggetto MethodInfo ed a sua volta dispone di una proprietà Name che ci fornirà il nome del membro contenente il punto d’ingresso: ora non resta che richiamarlo per eseguire il nostro script. Per farlo recupe-riamo il membro tramite un oggetto Type ese-guendo un ciclo all’interno dei tipi contenuti nel nostro assembly

Dim t as Type

for i=0 to CompRes.CompiledAssembly.GetTypes.Length - 1

If CompRes.CompiledAssembly.GetTypes(i).GetMember(Entry

Point).Length > 0 Then

t = CompRes.CompiledAssembly.GetTypes(i)

Exit For

End If

Next

A questo punto possiamo eseguire il nostro pro-gramma ricorrendo al metodo InvokeMember della classe Type, passandogli il nome dell’EntryPoint e specifi cando che si tratta di un metodo

t.InvokeMember(CompRes.CompiledAssembly.EntryPoint.Name,

BindingFlags.InvokeMethod, Nothing , Nothing , Nothing)

Il Listato 4 riporta tutta la procedura dall’aggiunta dei riferimenti alla compilazione ed esecuzione

Ri qua dro 1 Una rappresentazione delle chiavi create

HKEY_CLASSES_ROOT | |_.vbns (Predefinito=”NetScriptFile”) | |_ShellNew (FileName=”C:\Programmi\D&T Informatica\NetScript\Modelli\model.vbns”)

HKEY_CLASSES_ROOT | |_NetScriptFile (Predefinito=”File NetScript”) | |_DefaultIcon (FileName=”C:\Programmi\D&T Informatica\NetScript\NetScript.exe, 0”) | |_Shell (Predefinito=”Open”) | |_Debug (Predefinito=”Esegui con NetScript (debug mode)”) | | | |_command (Predefinito=””C:\Programmi\D&T Informatica\NetScript\NetScript.exe” %1 /d”) | |_Open (Predefinito=”Esegui con NetScript”) | |_command (Predefinito=””C:\Programmi\D&T Informatica\NetScript\NetScript.exe” %1”)

VB.NET

Page 26: v2005 04 vbj64

26 VBJ N. 64 - Luglio/Agosto 2005

Realizzazione del Setup

Per rendere l’applicazione comoda da utiliz-zare occorrono tre cose:

• L’abbinamento dei fi le .vbns al programma in modo che vengano eseguiti sul doppio click del mouse

• L’aggiunta delle voci di “esecuzione” e di “esecuzione in modalità debug” al menu contestuale che appare cliccando col pul-sante destro sui fi le .vbns

• La creazione di un fi le .vbns vuoto come quello del Listato 1 tramite la voce “Nuo-

vo” del menu contestuale proprio come per i fi le di Testo, Word, Excel ecc.

Tutto questo lo si può realizzare intervenendo sul Registro di Confi gurazione tramite un sem-plice fi le di script che eseguiremo per effettuare l’installazione; senza dilungarsi sulla struttura del Registry, analizziamo le voci che dovranno esse-re scritte nella chiave principale HKEY_CLAS-SES_ROOT. Creiamo la chiave .vbns che conter-rà il riferimento ad un’altra chiave NetScriptFile e il percorso completo del fi le modello da utilizzare quando l’utente selezionerà “File NetScript” dalla voce “Nuovo” del menu contestuale.

Li sta to 3 Il codice per la ricerca dell'assembly

‘Ciclo alla ricerca di elementi <prv>For i=0 to nodeList.Count-1 reference=Nothing

If nodeList.Item(i).Name=”prv” Then

‘recupero il percorso completo dell’assembly e lo copio nella directory temporanea specificata nel file .config reference=nodeList.Item(i).InnerText reference=tempBinDir & reference.Substring(reference.LastindexOf(“\”)+1) ‘Cerco il file come specificato nell’elemento <prv> e se non lo trovo provo a cercarlo nella cartella in cui risiede lo script If File.Exists(nodeList.Item(i).InnerText) Then File.Delete(reference) File.Copy(nodeList.Item(i).InnerText, reference) Else

If File.Exists(filepath & nodeList.Item(i).InnerText) Then

File.Delete(reference) File.Copy(filepath & nodeList.Item(i).InnerText, reference) Else Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”,

nodeList.Item(i).InnerText)) End If

End If

End If

‘Se sono riuscito a copiare l’assembly, lo aggiungo alle referenze If Not(reference is Nothing) Then

If File.Exists(reference) Then CompParam.ReferencedAssemblies.add(reference) Else Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”, reference)) End If

End IfNext

VB.NET

Page 27: v2005 04 vbj64

27N. 64 - Luglio/Agosto 2005 VBJ

Li sta to 4 Tutta la procedura di compilazione ed esecuzione (continua)

sr=New StreamReader(filename)CompParam.GenerateExecutable=trueCompParam.GenerateInMemory = trueCompParam.IncludeDebugInformation = Debug

CompParam.ReferencedAssemblies.add(“System.dll”)CompParam.ReferencedAssemblies.add(“System.Data.dll”)CompParam.ReferencedAssemblies.add(“System.Xml.dll”)CompParam.ReferencedAssemblies.add(“System.Drawing.dll”)CompParam.ReferencedAssemblies.add(“System.Web.dll”)CompParam.ReferencedAssemblies.add(“System.Windows.Forms.dll”)CompParam.ReferencedAssemblies.add(“System.Management.dll”)CompParam.ReferencedAssemblies.add(“Microsoft.VisualBasic.dll”)

‘Cerco referenze aggiuntiveIf xml.Trim()<>”” Then If Debug Then Console.WriteLine(“Referenze aggiutive:”) Console.WriteLine(xml) Console.WriteLine() End If ‘Carico in un XmlDocument il codice xml delle referenze doc=New XmlDocument() doc.LoadXml(xml)

‘recupero tutti i nodi-figlio del nmodo principale nodeList=doc.SelectNodes(“/references/*”)

‘Ciclo alla ricerca di elementi <prv> For i=0 to nodeList.Count-1 reference=Nothing If nodeList.Item(i).Name=”prv” Then ‘recupero il percorso completo dell’assembly e lo copio nella directory temporanea specificata nel file .config reference=nodeList.Item(i).InnerText reference=tempBinDir & reference.Substring(reference.LastindexOf(“\”)+1) If File.Exists(nodeList.Item(i).InnerText) Then File.Delete(reference) File.Copy(nodeList.Item(i).InnerText, reference) Else If File.Exists(filepath & nodeList.Item(i).InnerText) Then File.Delete(reference) File.Copy(filepath & nodeList.Item(i).InnerText, reference) Else Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”,

nodeList.Item(i).InnerText)) End If End If End if ‘Se sono riuscito a copiare l’assembly, lo aggiungo alle referenze If Not(reference is Nothing) Then If File.Exists(reference) Then CompParam.ReferencedAssemblies.add(reference) Else Throw New XmlReferenceException(String.Format(“Impossibile trovare il percorso dell’assembly {0}”, reference)) End If End If NextEnd If

VB.NET

Page 28: v2005 04 vbj64

28 VBJ N. 64 - Luglio/Agosto 2005

Nel Riquadro 1 è mostrata una rappresenta-zione della chiave creata; tra parentesi indichia-mo, a sinistra dell’uguale i valori stringa e a de-stra i dati in esso contenuti. Il percorso del fi le modello è contenuto all’interno del valore strin-ga FileName della chiave ShellNew. La chiave NetScriptFile a cui punta il valore predefi nito di .vbns è quella che contiene tutti i riferimenti al programma e precisamente:

• Il nome che appare nella voce “Nuovo” del menu contestuale (“File NetScript”);

• Il riferimento all’icona associata ai fi le .vbns;• Le due voci per l’esecuzione in modalità

normale e debug (rispettivamente la chiave Open e la chiave Debug).

Il dato del valore stringa Predefi nito della chia-ve Shell indica quale, tra le sottochiavi presen-ti, è quella che contiene il comando predefi nito da eseguire sul doppio click, mentre i dati dei valore stringa delle chiavi Open e Debug mo-strano il testo che appare quando si clicca col pulsante destro su un fi le .vbns. Il marcatore %1 è utilizzato dal Sistema Operativo per so-stituire ad esso il percorso completo del fi le di script che stiamo eseguendo. La scrittura nel Registro di Confi gurazione è effettuata tramite le classi Registry e RegistryKey contenute nel namespace Microsoft.Win32 (vedi [1]); la prima è una classe statica che fornisce un rapido ac-cesso in lettura/scrittura a ciascuna delle chiavi principali del Registry, restituendo un oggetto

RegistryKey con il quale creare i valori all’inter-no della chiave selezionata. Di seguito mostria-mo la scrittura della chiave .vbns

Dim rk as RegistryKey

rk=Registry.ClassesRoot.CreateSubKey(“.vbns”)

rk.SetValue(“”,”NetScriptFile”)

rk.close()

rk=Registry.ClassesRoot.CreateSubKey(“.vbns\\ShellNew”)

rk.SetValue(“FileName”,ModelDir & FileModello)

rk.close()

Conclusioni

Forse qualcuno si sarà accorto che nella tratta-zione abbiamo volutamente evitato di affrontare l’argomento della referenza di assembly di terze parti registrati nella GAC: in questo caso infatti è impensabile ricorrere alla soluzione di specifi care l’intero percorso data la complessità della struttu-ra di directory e sottodirectory, con nomi tutt’al-tro che facili da ricordare. In un prossimo articolo affronteremo questa lacuna, realizzando un pro-gramma che ci permetterà di specifi care solo il nome dell’assembly e interrogare in modo rapido la GAC per conoscerne il percorso completo.

Riferimenti

[1] .NET Framework SDK - http://msdn.micro- soft.com/netframework/support/documentation[2] XPath Tutorial - http://www.w3schools.com/

xpath

Li sta to 4 (segue) Tutta la procedura di compilazione ed esecuzione

CompRes=Comp.CompileAssemblyFromSource(CompParam, sr.ReadToEnd())‘Nel caso di errori li mostro a videoIf CompRes.Errors.Count > 0 Then For i=1 to CompRes.Output.Count-1 Console.WriteLine(CompRes.Output.Item(i)) NextElse ‘Recupero del punto di ingresso dell’assembly generato EntryPoint=CompRes.CompiledAssembly.EntryPoint.Name for i=0 to CompRes.CompiledAssembly.GetTypes.Length - 1 If CompRes.CompiledAssembly.GetTypes(i).GetMember(EntryPoint).Length > 0 Then t = CompRes.CompiledAssembly.GetTypes(i) Exit For End If Next ‘Esecuzione del programma t.InvokeMember(CompRes.CompiledAssembly.EntryPoint.Name, BindingFlags.InvokeMethod, Nothing , Nothing , Nothing)End If

VB.NET

Page 29: v2005 04 vbj64

29N. 64 - Luglio/Agosto 2005 VBJ

SQL Server 2005ed il CLR:un matrimonio annunciato

di Andrea Benedetti

DATABASE

Tra le varie novità introdotte con la nuova ver-sione di SQL Server, sicuramente l’integrazione con il framework .NET è quella che agli occhi

dello sviluppatore risulta più intrigante.È necessario chiarire subito un punto: T-SQL, ov-

vero il dialetto proprietario di casa Microsoft, non è morto.

Anzi le nuove funzionalità ed i nuovi operatori sono talmente tanti e tali che potrebbe essere necessa-rio rimettersi un po’ sui libri: large value data type, event notifi cations, DDL triggers, pivot, row_num-ber, ecc…

Il T-SQL, che si compone di linguaggio per le query e accesso ai dati e di linguaggio procedurale, re-sta, di fatto, l’unica scelta per recuperare e mani-polare dati (il CLR diventa un’alternativa alla scrit-tura di codice procedurale) e la scelta migliore con comandi basati su set di dati.

Conviene utilizzarlo sempre quando possibile ed impiegare il CLR solo dopo aver verifi cato l’assen-za di istruzioni equivalenti o in presenza di logica che non può essere espressa in maniera dichiarati-va nel linguaggio di interrogazione.

Sicuramente possiamo trarne vantaggio, in termini di performance, in tutti quei casi in cui, navigando un set di record, dobbiamo “lavorare” ciascuna riga

Andrea Benedetti si occupa di disegno e manutenzione di basi di dati e di sviluppo di applicazioni web-based finalizzate all’erogazione di servizi sanitari per conto di Studiofarma Srl di Brescia. Può essere contattato tramite e-mail all’indirizzo [email protected].

o dove devono essere fatti calcoli ed aggregazioni complesse.

Tutte le procedure che conten-gono ed utilizzano cursori diven-tano quindi ottime candidate per una riscrittura in codice gestito.

Prima di questa versione l’unico modo per scrivere logica lato ser-ver era quella di costruire stored procedure estese che non pote-vano fornire nessun controllo del-le risorse da esse utilizzate.

SQL Server ci mette oggi a di-sposizione un nuovo modello di programmazione fornito dal-la completa integrazione con il framework (versione 2.0), con il motore del CLR inserito diretta-mente nel motore di SQL Ser-ver (SQL Server fa da host al CLR, quindi in un unico domi-nio di applicazione); CLR che di-venta un’ottima e robusta alter-nativa alla scrittura di procedu-re estese.

Infatti se un’applicazione client, scritta in .NET, gira al di fuori di un database ed accede ad esso attraverso una connessione, con SQL Server 2005 siamo in grado, invece, di scrivere codice gestito che verrà eseguito direttamente all’interno del database stesso.

SQL Server 2005 rappresenta una grande evoluzione nella gestione di database. Vediamo come l’integrazione con il framework .NET possa migliorare la vita degli sviluppatori e dei DBA.

Page 30: v2005 04 vbj64

30 VBJ N. 64 - Luglio/Agosto 2005

Lo strumento di sviluppo utilizzato, Visual Stu-dio 2005, ci permette di utilizzare il medesimo ambiente sia per scrivere ed effettuare debug di applicazioni/oggetti database, sia per rea-lizzare applicazioni e componenti client.

Inoltre permette lo sviluppo di oggetti (fun-zioni, procedure, trigger, tipi e aggregazioni), operazioni di debug ed installazione automa-tica sul server.

Di default i database di esempio non vengo-no installati.

È necessario quindi, durante l’installazione, entrare nelle opzioni avanzate e selezionare i database interessati (database che, eventual-mente, possono anche essere installati in un secondo momento).

Come abbiamo appena detto, di default, il CLR viene disabilitato. Possiamo vedere la confi gurazione estesa (Figura 1) del server con queste semplici istruzioni:

USE master

GO

DATABASE

Chiariamo che il runtime viene/verrà carica-to solo alla richiesta di esecuzione di codi-ce gestito.

Se questa richiesta non verrà mai fatta, è evi-dente che il CLR non verrà mai caricato.

Inoltre viene data la facoltà al DBA di disabi-litare completamente l’integrazione con il fra-mework (di default è già così, quindi per uti-lizzarlo dovremo abilitarlo confi gurando appo-sitamente il server).

L’integrazione ed i principali benefi ci

Per iniziare a prendere dimestichezza con questo matrimonio annunciato, abbiamo co-struito una macchina di test (una macchina vir-tuale ospitata in Microsoft Virtual PC con si-stema operativo Windows Server 2003 Stan-dard Edition) in tre passi: installazione Visual Studio 2005 (in beta 1), disinstallazione del Framework 2.0 (non può essere soprascritto) e, per fi nire, installazione di SQL Server 2005 (in beta 2).

Fi gu ra 1 La configurazione del server

Page 31: v2005 04 vbj64

31N. 64 - Luglio/Agosto 2005 VBJ

EXEC sp_configure ‘show advanced option’, ‘1’

RECONFIGURE

EXEC sp_configure

GO

Tra i record visualizzati dall’istruzione sp_confi gure è suffi ciente individuare la voce “clr enabled” ed identifi care il valore attualmente impostato. Per abilitarlo sarà suffi ciente ese-guire l’istruzione:

EXEC sp_configure ‘clr enabled’, 1

Il CLR delega a SQL Server, ad esclusione del meccanismo di garbage collection, tutte quelle attività che, di regola, gestisce diretta-mente: allocazione di memoria, thread, locking, meccanismi di sincronizzazione...

SQL Server è infatti in grado di gestire la me-moria direttamente, possiede un suo algorit-mo di gestione dei thread (cooperative thread scheduling) e può decidere autonomamente di abortirli in caso di eccezioni e anche, se necessario, scaricare l’AppDomain, ovvero il runtime caricato.

La prima procedura .NET

Creiamo adesso un piccolo esempio per poter capire meglio come viene gestita l’integrazio-ne. Lanciamo Visual Studio 2005 e, dal menu “File”, “New”, “Project”, “Database”, sceglia-mo il nuovo tipo di progetto, presente da que-sta versione: “SQL Server Projects”. Assegnia-mo un nome, SqlCrlTest, e premiamo “OK” (Figura 2).

Si aprirà a video una fi nestra per poter con-fi gurare la connessione verso il server sql: in-dichiamo il nome del server, selezioniamo la modalità di autenticazione (integrated securi-ty), selezioniamo il database AdventureWorks e premiamo “OK”.

Visual Studio apre quindi una nuova solution di progetto, con il nome stabilito, vuota.

Facciamo quindi tasto destro sul progetto SqlClrTest, click su “Add” e, tra i vari oggetti proposti, scegliamo “Stored Procedure”.

Assegniamo un nome al fi le e, quindi, al me-todo interno alla classe che andremo a scri-vere: up_SelectProducts.cs ed infi ne premia-mo “Add”.

Diamo una prima occhiata alla classe che Vi-sual Studio ha costruito con il suo template.

Il metodo up_SelectProducts() è defi ni-to static void; tale metodo è decorato dal-l’attributo [SqlProcedure]; tra gli assem-bly referenziati ne troviamo due fi no ad oggi sconosciuti: System.Data.SqlServer e System.Data.SqlTypes.

Il tipo di ritorno della procedura deve essere defi nito static (o shared in VB.NET; la classe non verrà infatti istanziata direttamente) e, per default, viene defi nito void (altrimenti potreb-be essere defi nito come Int in quanto le sto-red procedure possono ritornare solo interi o nulla, ed il tipo SQL Server Int è compatibile con i tipi CLR SqlInt32, SqlInt16, System.Int32 e System.Int16).

L’attributo [SqlProcedure] consentirà, all’at-to dell’installazione, di far riconoscere il tipo di oggetto defi nito nel codice e, quindi, di ca-ricarlo correttamente.

Il namespace System.Data.SqlServer contie-ne ed espone le classi per accedere e lavora-re nel processo di SQL Server (SqlExecution Content Class).

Si tratta di un in-memory (in-process) provider (quindi assai diverso dal provider SqlClient), studiato ed ottimizzato per questo, che ci met-te in condizione di scrivere codice che verrà

DATABASE

Li sta to 1 L’equivalente in C# della stored procedure

using System;using System.Data;using System.Data.Sql;using System.Data.SqlServer;using System.Data.SqlTypes;

public partial class StoredProcedures{ [SqlProcedure] public static void up_SelectProducts(string

ProductName) { SqlPipe myPipe = SqlContext.GetPipe(); System.Data.SqlServer.SqlCommand cmd =

SqlContext.GetCommand(); cmd.CommandText = “SELECT ProductID, Name,

ProductNumber “ + “ FROM Production.Product “ + “ WHERE name like @ProductName + ‘%’”; cmd.Parameters.Add(“@ProductName”,

SqlDbType.VarChar); cmd.Parameters[0].Value = ProductName;

SqlDataReader dr = cmd.ExecuteReader();

myPipe.Send(dr); }};

Page 32: v2005 04 vbj64

32 VBJ N. 64 - Luglio/Agosto 2005

eseguito direttamente all’interno del database sotto forma di stored procedure, user-defi ned function, triggers, ecc...

Il codice scritto ed eseguito non dipenderà in alcun modo dalle connessioni al database.

Infatti nella scrittura di procedure e funzioni non sarà necessario creare connessioni (come era invece indispensabile nella scrittura di sto-red procedure estese) grazie alla classe Sql-Context.

Questa classe espone il metodo GetConnec-tion() che ritorna un oggetto SqlConnection che implementa l’interfaccia ISqlConnection.

La SqlConnection ritornata ha alcune diver-sità rispetto al medesimo oggetto esposto dal namespace SqlClient: non esiste un costrutto-re per la classe (si può solo utilizzare tramite il metodo statico GetConnection()), esiste un nuo-vo metodo CreateExecutionContext, non imple-menta l’interfaccia IDisposable (quindi non è

DATABASE

Li sta to 2 UDT che rappresenta il tipo “codice fiscale”

using System;using System.Data.Sql;using System.Data.SqlTypes;

[Serializable][SqlUserDefinedType(Format.UserDefined, MaxByte

Size = 512)]public class UDT_codiceFiscale : INullable,

IBinarySerialize{ private Boolean _isNull; private string cf = “”;

public override string ToString() { if (this.IsNull) { return “NULL”; } else { return this.cf; } }

public bool IsNull { get { return this._isNull; } }

public static UDT_codiceFiscale Null { get { UDT_codiceFiscale h = new UDT_

codiceFiscale(); h._isNull = true; return h; } }

public static UDT_codiceFiscale Parse(SqlString s)

{

if (s.IsNull || s.Value.ToLower().Equals(“null”))

return null;

string valore = Convert.ToString(s); if (s.ToString().Length < 16) { throw new NotSupportedException

(“Lunghezza errata”); }

UDT_codiceFiscale u = new UDT_codiceFiscale();

u.cf = s.ToString(); u._isNull = false; return u; }

public string myCF { get { return this.cf; } }

#region IBinarySerialize Members

void IBinarySerialize.Read(System.IO.BinaryReader r)

{ this.cf = r.ReadString(); }

void IBinarySerialize.Write(System.IO.BinaryWriter w)

{ if (this.cf == null) this.cf = string.Empty; w.Write(this.cf); }

#endregion}

Page 33: v2005 04 vbj64

33N. 64 - Luglio/Agosto 2005 VBJ

possibile chiamarne la Dispose()). Il secondo namespace, System.Data.SqlTypes, fornisce le classi dei tipi che corrispondono perfetta-mente ai tipi defi niti in SQL Server.

Se infatti esiste il tipo System.Int32 che non richiede né conversioni, né marshaling, il tipo System.Decimal non ha un esatto tipo corri-spondente (ad esempio il valore massimo che può assumere il tipo SqlDecimal è di molto superiore al valore che può assumere il tipo decimal del CLR, ed una possibile conver-sione di questo valore genererebbe un erro-re di runtime).

Utilizzando questi tipi, quindi, non è neces-sario effettuare alcuna conversione, renden-do di fatto più veloce l’esecuzione del codi-ce gestito.

Realizziamo, a titolo di esempio, una sem-plice stored procedure in T-SQL:

CREATE PROCEDURE dbo.up_SelectProductsTSQL

(

@ProductName varchar(50)

)

AS

SET NOCOUNT ON

SELECT ProductID, Name, ProductNumber

FROM Production.Product

WHERE name like @ProductName + ‘%’

SET NOCOUNT OFF

GO

L’equivalente scritto in C# è visibile nel Li-stato 1. La prima considerazione cade sulla defi nizione della classe: partial class Stored-Procedures.

Il Framework 2.0 introduce questa defi nizione di classe parziale che consente di suddividere defi nizioni, strutture e interfacce su più fi le.

Risulta quindi evidente che ogni classe che andremo a scrivere, appartenente a col-lezioni di oggetti (User Defi -ned Functions, Triggers, ecc.), deve necessariamente essere defi nita parziale mentre non lo sarà, ad esempio, per gli User Defi ned Type.

L’oggetto SqlPipe serve per inviare, invocando il suo me-todo Send, i risultati al client e rappresenta, in output, un Ta-bular Data Stream (TDS, il for-mato di scambio dati che SQL Server utilizza da sempre).

SqlPipe è in grado di inviare qualsiasi oggetto che imple-menti l’interfaccia IDataRe-cord e si ottiene dal contesto corrente, così come il SqlCom-

DATABASE

Fi gu ra 2 La selezione di un nuovo progetto

Li sta to 3 Caricamento dell’assembly e creazio- ne del tipo con istruzioni T-SQL

-- Carico l’assembly:CREATE ASSEMBLY SqlClrTestFROM ‘C:\....\SqlClrTest.dll’

-- Creo il tipoCREATE TYPE [UDT_codiceFiscale]EXTERNAL NAME SqlClrTest.UDT_codiceFiscale

-- Creo la tabella:CREATE TABLE ANA(anaID int primary key not null,cf UDT_codiceFiscale)

-- Dichiaro una variabile di tipo UDT_codiceFiscaleDECLARE @CF UDT_codiceFiscaleSET @CF = CONVERT(UDT_codiceFiscale,

‘BNDNDR75E09G702H’)-- Insert sulla tabellaINSERT ANA (anaID, cf) VALUES (1, @cf)

-- Select sulla tabella [nome colonna].[nome pro-prietà]

SELECT cf.myCF FROM ANA

Page 34: v2005 04 vbj64

34 VBJ N. 64 - Luglio/Agosto 2005

mand di cui abbiamo impostato il comando da eseguire ed il parametro necessario.

Scritto il codice non ci resta che effettuare l’installazione del nostro progetto: dal menu “Build”, “Deploy Solution”.

Con questa operazione Visual Studio 2005 genera ed esegue le necessarie istruzioni SQL per installare l’assembly all’interno del databa-se e per creare la stored procedure costruita nel codice (si può verifi care facilmente apren-do una traccia di SQL Profi ler per visualizza-re le istruzioni che vengono eseguite diretta-mente sul server).

A questo punto non ci resta che aprire SQL Server Management Studio, espandere il nodo Databases del nostro server, espandere il db AdventureWorks, “Programmability” e “Stored Procedures”.

Possiamo notare che, oltre ad essere pre-sente la procedura up_SelectProductsTSQL, creata poco fa, esiste anche la procedura up_SelectProducts installata direttamente dall’am-biente di sviluppo insieme all’assembly Sql-ClrTest che possiamo trovare all’interno della

collezione Assemblies sempre all’interno del-la voce “Programmability”.

Clicchiamo con il tasto destro sul nostro as-sembly per visualizzarne le proprietà ed ana-lizzare la voce “Permission set”.

Per gli assembly caricati all’interno di SQL Ser-ver, e quindi per il codice gestito, esistono tre categorie di sicurezza (Code Access Security): SAFE, EXTERNAL_ACCESS, UNSAFE.

• SAFE è il set di permessi di default, permet-te di eseguire calcoli ed accesso ai dati uti-lizzando il provider in-process, è l’equiva-lente di una procedura scritta in T-SQL.

• EXTERNAL_ACCESS viene utilizzato in sce-nari in cui si rende necessario accedere a ri-sorse esterne al server (ad esempio lettura/scrittura fi le).

• UNSAFE quando un assembly richiede ac-cesso a risorse particolari (ad esempio le API Win32).

Nelle prime due categorie il runtime si preoc-cupa di verifi care che il codice sia type-safe per

DATABASE

Fi gu ra 3 Esecuzione della stored procedure

Page 35: v2005 04 vbj64

35N. 64 - Luglio/Agosto 2005 VBJ

assicurarsi che non esistano accessi diretti a locazioni di memoria, insomma fa di tutto per eliminare possibili buffer overruns, puntatori a locazioni errate, ecc.

In conclusione, il percorso che abbiamo fat-to è stato: scrivere il codice sorgente, com-pilare, effettuare il deploy dell’assembly (Vi-sual Studio si preoccupa per noi di registrare e caricare nel db la funzione scritta), la con-nessione al db e l’esecuzione della procedu-ra (Figura 3).

Così come abbiamo effettuato la registra-zione ed il caricamento dell’assembly, con al-trettanta semplicità Visual Studio ci permet-te il debug diretto del nostro codice. Per pri-ma cosa aggiungiamo al nostro progetto uno script di test facendo tasto destro sul proget-to e click su “Add Test Script”.

Visual Studio aggiunge una nuova cartella chiamata “TestScripts”, all’interno della qua-le si trova un fi le “Test.sql”.

L’esecuzione di questo fi le di script sarà la prima operazione che verrà fatta al lancio del-l’applicazione. Non ci resta che inserire nel fi le di script l’istruzione T-SQL da lanciare: EXEC dbo.up_SelectProducts ‘c’, imposta-re un breakpoint sulla prima istruzione della nostra procedura e premere F5 per esegui-re il codice. Il nostro ambiente di sviluppo ci consentirà di eseguire passo passo le istru-zioni che abbiamo generato e di controllare così ogni parte del fl usso di lavoro.

Così come abbiamo restituito un set di re-cord, possiamo utilizzare lo stesso oggetto SqlPipe per inviare al client un eventuale mes-saggio di errore. Proviamo quindi a scatena-re un’eccezione (una divisione per zero) con due righe di codice:

[SqlProcedure]

public static void up_GeneraEccezione()

{

int a = 12;

int b = 0;

try

{

b = a / b;

}

catch(Exception ex)

{

SqlContext.GetPipe().Send(“Errore!”);

SqlContext.GetPipe().Send(“Msg: “ + ex.Message);

}

}

DATABASE

Li sta to 4 La classe UDA_csv.cs

using System;using System;using System.Data.Sql;using System.Data.SqlTypes;using System.Data.SqlServer;

[Serializable][SqlUserDefinedAggregate(System.Data.Sql.Format.UserDefined, MaxByteSize = 8000)]public class UDA_csv : IBinarySerialize{ System.Text.StringBuilder risultatoCSV;

public void Init() { risultatoCSV = new System.Text.StringBuilder(); }

public void Accumulate(SqlString Value) { if (Value.IsNull) { return; } else { risultatoCSV.Append(Value + “;”); } }

public void Merge(UDA_csv Group) { Accumulate(Group.ToString()); } public SqlString Terminate() { return new SqlString(risultatoCSV.ToString()); }

#region IBinarySerialize Members

void IBinarySerialize.Read(System.IO.BinaryRea-der r)

{ risultatoCSV = new System.Text.StringBuil-

der(); this.risultatoCSV.Append(r.ReadString()); }

void IBinarySerialize.Write(System.IO.BinaryWri-ter w)

{ w.Write(this.risultatoCSV.ToString()); }

#endregion}

Page 36: v2005 04 vbj64

36 VBJ N. 64 - Luglio/Agosto 2005

Mandandola in esecuzione, la procedura viene eseguita regolarmente, inviando al client il mes-saggio di errore generato dall’eccezione.

Da questi esempi risultano chiari, sempre te-nendo presenti le premesse fatte all’inizio, al-cuni vantaggi e benefi ci che possiamo ottene-re dall’integrazione del framework:

• scrivere logica di business direttamente nel database;

• alternare i linguaggi .NET a T-SQL;• utilizzare classi ed oggetti del framework (ad

esempio: crittografi a, accesso ai fi le…).

Possibili scenari di utilizzo

Possibili scenari di utilizzo potrebbero essere in-dividuati nel rimpiazzare stored procedure che uti-lizzano cursori o codice che carica intere tabelle o grossi result-set per costruire aggregazioni partico-

DATABASE

Tabella 1 Equivalenza dei tipi SQL Server e CLR

SQL Server CLR (SQL Server) CLR (.NET Framework)

Varbinary SQLBytes, SQLBinary Byte[]

Binary SQLBytes, SQLBinary Byte[]

Image

Varchar

Char

Nvarchar(1), Nchar(1) Char

Nvarchar SQLChars, SQLString String, Char[]

Nchar SQLChars, SQLString String, Char[]

Text

Ntext

Uniqueidentifier SQLGuid Guid

Rowversion

Bit SQLBoolean Boolean

Tinyint SQLByte Byte

Smallint SQLInt16 Int16

Int SQLInt32 Int32

Bigint SQLInt64 Int64

Smallmoney SQLMoney Decimal

Money SQLMoney Decimal

Numeric SQLDecimal Decimal

Decimal SQLDecimal Decimal

Real SQLSingle Single

Float SQLDouble Double

Smalldatetime SQLDateTime DateTime

Datetime SQLDateTime DateTime

SQL_variant Object

Table ISQLResultSet

Cursor

Timestamp

Xml SqlXml

Page 37: v2005 04 vbj64

37N. 64 - Luglio/Agosto 2005 VBJ

DATABASE

lari o per accedere a risorse esterne (fi le, consumo di web service, ecc.) Proviamo a scrivere tre pic-coli esempi che possano spiegare meglio l’utilizzo di codice gestito con SQL Server 2005.

User Defi ned DataType

Vediamo come poter defi nire un UDT che rappresenti il tipo “codice fi scale” (Listato 2).

Ancora una volta tasto destro sul nostro pro-getto, “Add”, “User-Defi ned Types” ed impostia-mo il nome come UDT_codiceFiscale.cs.

La classe, decorata con l’attributo [Serializa-ble], prevede l’implementazione dell’interfaccia INullable ed i metodi: ToString, Parse e Null.

Nell’esempio, banalmente, andremo ad in-serire un membro privato di tipo stringa chia-mato “cf”.

Avendo una proprietà stringa, la classe non può avere un SqlUserDefi nedTypeAttribute im-postato a Native in quanto soltanto i tipi che hanno un’identica rappresentazione nel CLR possono essere impostati come tali.

Impostando tale attributo a Format.UserDefi ned è allora necessario implementare anche l’inter-faccia IBinarySerialize con i suoi membri Read

Fi gu ra 4 Esecuzione User Defined Aggregate

e Write per abilitare il codice a leggere e scri-vere lo stream di byte.

Impostiamo la proprietà MaxByteSize, uti-lizzata solo con il tipo di formato che abbia-mo impostato, così da indicare la dimensione massima dello stream, tenendo presente che il valore non può superare gli 8.000 byte.

Questa volta dopo aver scritto e compilato il codice eseguiamo, a titolo di esempio, il cari-camento dell’assembly e la creazione del tipo con istruzioni T-SQL, direttamente da una fi -nestra di query del Management Studio (Li-stato 3). Sicuramente non è opportuno co-struire tipi personalizzati per entità comples-se (persona, impiegato).

User Defi ned Aggregate

La scrittura di un UDA, ovvero una funzione che lavora su di un set di righe e ritorna un valore scalare (come le funzioni di aggrega-zione sum, max, min, avg), può risultare molto più semplice ed il risultato molto più veloce e meno pesante in termini di risorse.

Oggi, l’integrazione con il Framework ci forni-sce questo nuovo modo per aggregare dati.

Page 38: v2005 04 vbj64

38 VBJ N. 64 - Luglio/Agosto 2005

DATABASE

Proviamo a costruire una lista di valori se-parati da “;”, una sorta di fi le csv.

Facciamo tasto destro sul nostro progetto, “Add”, “Aggregate” ed impostiamo il nome come “UDA_csv.cs”.

Anche questa volta Visual Studio fornisce uno scheletro della classe che andremo a co-struire grazie ai template presenti per ciascun oggetto costruibile.

La classe, serializzabile, deve implementa-re quattro diverse funzioni: Init, Accumulate (che conterrà tutta la logica di aggregazione), Merge, Terminate (Listato 4).

Una volta eseguito il deploy del nostro pro-getto possiamo eseguire, tramite una nuova query nel SQL Server Management Studio (Fi-gura 4), la nostra funzione:

select DepartmentID, dbo.UDA_csv(NationalIDNumber) as

myCSV

from HumanResources.Employee

group by DepartmentID

User Defi ned Function

L’ultimo esempio che vediamo tratta la defi -nizione di una UDF. Apriamo con Visual Stu-dio una nuova classe, selezionando l’elemen-to “User-Defi ned Function”, che chiameremo “UDF_regex.cs”. Dicevamo poco sopra che uno dei principali vantaggi dell’integrazione con il CLR è sicuramente il fatto di poter utiliz-zare classi e funzioni che il framework offre.

Potremmo quindi implementare una soluzio-ne che utilizzi le classi che il namespace System.Security.Cryptography offre, oppure po-tremmo scrivere una funzione che validi, tra-mite un’apposita regular-expression, un indi-rizzo e-mail (Listato 5). Eseguiamo il deploy dell’applicazione e proviamola tramite due semplici istruzioni T-SQL:

select dbo.UDF_regex(‘aaa’) as isValidEmail

select dbo.UDF_regex(‘[email protected]’) as isValidEmail

Conclusioni

Come abbiamo cercato di verifi care, il sup-porto al Common Language Runtime porta si-curamente vantaggi.

Detto questo dobbiamo sempre, prima di usare codice .NET, accertarci che non esista-no istruzioni equivalenti in T-SQL, anche veri-fi cando le nuove estensioni e funzionalità in-trodotte. Per quanto riguarda la defi nizione di tipi nelle tabelle, la comunità degli sviluppatori e dei DBA ha iniziato da un po’ di tempo una discussione (assai utile) sul loro uso.

L’utilizzo del Framework potrebbe essere inu-tile quando il tipo da costruire ha un solo va-lore. Ad oggi il problema maggiore riguarda le indicizzazioni e le ricerche, così come sono sempre da valutare la fl essibilità offerta e la possibilità di ridefi nire gli operatori.

Insomma la discussione continua.

Riferimenti

[1] http://www.microsoft.com/sql/2005/default.asp

[2] newsgroup: microsoft.public.it.sql

Li sta to 5 Validazione di un indirizzo e-mail tra- mite un’apposita regular-expression

using System;using System.Text.RegularExpressions;using System.Data.Sql;using System.Data.SqlTypes;

public partial class UserDefinedFunctions{ [SqlFunction] public static SqlInt16 UDF_regex(string eMail) { string regExEmail = @”\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*”;

Regex re = new Regex(regExEmail); if(re.IsMatch(eMail)) { return 1; } else { return 0; } }};

Prima di questa versione

l’unico modo per scrivere lo-

gica lato server era di costrui-

re stored procedure estese

Page 39: v2005 04 vbj64

39N. 64 - Luglio/Agosto 2005 VBJ

I design pattern più famosi implementati in VB.NET

di Lorenzo Vandoni

In questa breve serie di articoli abbiamo fi nora esa-minato i pattern Iterator e Observer, due tra i più famosi ed utilizzati tra quelli descritti nel libro “De-

sign Pattern” di Gamma, Helm, Johnson e Vlissides. Questa puntata è dedicata ad un pattern meno noto, chiamato Factory Method.

Il pattern Factory Method

Anche in questo caso la descrizione del pattern verrà suddivisa in tre paragrafi , che evidenziano ri-spettivamente lo scopo, le motivazioni e l’applicabi-lità del pattern.

• Intent. Lo scopo del pattern è quello di defi nire una generica interfaccia che permetta la creazio-ne di un oggetto, delegando però alle sottoclassi la scelta di quale tipo di oggetto istanziare.

• Motivation. Questo pattern può essere utile so-prattutto per chi si occupa della realizzazione di framework o librerie di classi. In questi casi può capitare la necessità di defi nire, in una classe A, dei metodi di creazione (metodi, cioè, che resti-tuiscano come risultato della loro esecuzione un riferimento ad un nuovo oggetto), offrendo però la

possibilità alle sottoclassi di A di modifi care il tipo dell’ogget-to creato. Alcuni esempi pos-sono essere quello di una ge-nerica collezione con un me-todo che restituisca un itera-tore, oppure quello di un og-getto che rappresenti una con-nessione a un database, con un metodo che restituisca un oggetto command.

• Applicabilità. Il pattern è appli-cabile in tutti quei casi in cui il tipo dell’oggetto creato non è prevedibile a priori, oppure lo è ma si vuole offrire la possibilità di crearne delle sottoclassi.

Soluzione del problema

La soluzione del problema è co-stituita dal diagramma di classi mostrato in Figura 1. I paragrafi seguenti ne forniscono una de-scrizione più dettagliata.

• Structure. Le classi Product e Creator sono quelle che fanno parte dell’ipotetico framework. La classe Creator fornisce un metodo FactoryMethod che si preoccupa di creare un generi-

DP 3Il pattern Factory Method consente di delegare a una sottoclasse la scelta di quale oggetto istanziare

Terza puntata

Lorenzo Vandoni è laureato in Informatica, ed è uno spe-cialista di progettazione e sviluppo con tecniche e linguaggi object-oriented. Ha collaborato alla realizzazione di framework, strumenti di sviluppo e software commerciali in C++, Java e Visual Basic. Può essere contattato tramite e-mail all’indirizzo [email protected].

SOFTWARE ENGINEERING

Page 40: v2005 04 vbj64

40 VBJ N. 64 - Luglio/Agosto 2005

di una classe più specifi ca, in questo caso ConcreteProduct. Il FactoryMethod, ovvia-mente, mantiene la stessa signature, e in particolare continua a restituire un generi-co riferimento ad un oggetto Product.

• La classe ConcreteProduct deriva da Product e rappresenta un caso concre-to di oggetto creato dal FactoryMethod (ad esempio, un iteratore forward only o un comando per SQL Server).

• Collaborations. La struttura dinamica del pattern è molto semplice, e non vi sono par-ticolari collaborazioni da evidenziare tra gli oggetti coinvolti. Semplicemente, un ogget-to ConcreteCreator si preoccuperà di istan-ziare un oggetto ConcreteProduct all’inter-no della sua implementazione del metodo FactoryMethod.

Conseguenze e benefi ci

Vediamo ora quali possono essere i punti di forza della soluzione ed alcune sue possibili ap-plicazioni pratiche:

• Consequences. Il principale vantaggio è dato dalla possibilità di aggiungere nuove classi concrete come sottoclassi di Creator e Pro-duct, senza modifi care la struttura del fra-mework, e soprattutto senza dover modifi -care il codice delle applicazioni client.

• Known uses. Questo pattern è usato in molti framework commerciali ed open source. Un esempio di utilizzo che può risultare molto fa-

miliare ai lettori di questa rivista è costituito dal metodo Create-Command defi nito nell’interfac-cia IDbConnection. Questo me-todo permette di creare un ge-nerico comando IDbCommand a partire da una generica con-nessione, e viene implemen-tato da parte di tutte le classi che implementano l’interfaccia IDbConnection, come ad esem-pio OleDbConnection e Sql-Connection. Ovviamente, ogni connessione creerà un’istanza della classe comando defi nita dal proprio provider.

co Product. Le sottoclassi di Creator hanno la facoltà di fornire una versione specializzata di FactoryMethod, che crea un oggetto più spe-cifi co, ConcreteProduct, derivato da Product.

• Participants. Più in dettaglio, queste sono le re-sponsabilità delle interfacce e classi coinvolte:

• Creator è una classe base, che defi nisce il FactoryMethod. A seconda delle circo-stanze, si potrà trattare di una vera clas-se base, di una classe astratta o di una semplice interfaccia. Questo dipende più che altro dal fatto che la classe Product abbia una semantica specifi ca, o possa a sua volta essere considerata alla stre-gua di semplice interfaccia.

• La classe Product è una classe base che rappresenta un generico oggetto creato dal FactoryMethod. Anche in questo caso si può trattare di una vera classe, di una classe astratta o di una interfaccia. Nel caso di col-lezioni ed iteratori, ad esempio, può avere senso creare un generico iteratore concre-to, con funzionalità di base applicabili a tut-ti i tipi di collezioni. Nel caso di connessioni e comandi, al contrario, non ha senso im-plementare un generico comando. In ogni caso, Product dovrà però defi nire i metodi corrispondenti alla semantica dell’oggetto implementato (ad esempio, un iteratore o un comando). Tali metodi verranno poi even-tualmente sovrascritti dalle classi derivate.

• La classe ConcreteCreator deriva da Creator e sovrascrive il metodo Factory-Method, in modo da ritornare un’istanza

Fi gu ra 1 Architettura del pattern Factory Method

SOFTWARE ENGINEERING

Page 41: v2005 04 vbj64

41N. 64 - Luglio/Agosto 2005 VBJ

Implementazione del pattern in VB.NET

Come esempio di implementazione consideria-mo il caso di un generico oggetto i cui dati deb-bano essere visualizzati all’interno di un generi-co dispositivo di output. Per semplicità, restrin-geremo la casistica dei possibili dispositivi di ou-tput ai soli controlli che possano essere disposti su una Windows Form. Il fatto di utilizzare due classi diverse per gestire la memorizzazione dei dati e la visualizzazione degli stessi è una buona norma di progettazione, che tra l’altro abbiamo già visto formalizzata all’interno del pattern Ob-server. Poiché la logica di visualizzazione dipen-de, oltre che dal tipo di dispositivo, anche dal tipo di dati da visualizzare, è corretto assegnare all’oggetto visualizzato la responsabilità di creare il suo visualizzatore. Nell’esempio vengono quin-di defi nite due classi base, GenericViewer e Ge-nericObject, il cui codice viene mostrato nel Li-stato 1. GenericViewer rappresenta un oggetto Product, mentre GenericObject è il Creator. Per ognuna di queste due classi base viene defi nita una sottoclasse, per rappresentare un caso con-creto: Person è una sottoclasse di GenericObject, e viene utilizzata per mantenere dati personali; PersonViewer è uno specifi co visualizzatore, che “sa” come mostrare i dati personali su un gene-rico control. L’esempio è completato da una form con un pulsante alla cui pressione viene creato un oggetto Person, a cui viene richiesto di crea-re due viewer. L’oggetto Person risponde a que-

sti messaggi creando dei viewer specifi ci, ovve-ro due istanze di PersonViewer. A queste istanze vengono quindi passati due riferimenti a due di-versi TextBox, che verranno utilizzati per eseguire la visualizzazione. La chiamata al metodo Paint, infi ne, permette di eseguire la visualizzazione.

Conclusioni

Il codice dell’esempio è mostrato a titolo di esempio e sicuramente migliorabile, ma nella sua semplicità permette comunque di mostrare un esempio di applicazione del pattern Factory Method, e come sia possibile utilizzare contem-poraneamente due o più pattern.

In questo caso specifi co, il pattern Factory Method viene utilizzato per delegare ad una sottoclasse la scelta di quale oggetto istanziare in risposta all’esecuzione di un metodo specifi -co, mentre Observer viene utilizzato per sepa-rare la logica applicativa dalle modalità di visua-lizzazione. Come conseguenza dell’applicazione del primo pattern, siamo riusciti a creare un’ar-chitettura di base, costituita delle classi Gene-ricObject e GenericViewer, che permette di im-plementare diversi tipi di classi derivate mante-nendo la stessa interfaccia funzionale.

Un’importante conseguenza dell’applicazione del pattern Observer, invece, è costituita dal-la possibilità di avere più viewer separati per lo stesso oggetto. Factory Method è un Creational Pattern, ovvero un pattern che incapsula una so-luzione di progettazione per un problema relati-

vo all’istanziazione di uno o più oggetti. I pattern esaminati ne-gli articoli precedenti, Iterator e Observer, sono invece catego-rizzati come Beha-vioral pattern. Nel-la prossima puntata mostreremo, come ultimo esempio di implementazione, Adapter, un pattern di tipo Strutturale, ovvero un esempio appartenente alla terza ed ultima delle categorie di pattern presentate all’inter-no del libro Design Pattern.

Li sta to 1 Implementazione delle interfacce del pattern in VB.NET

‘la classe generic viewer rappresenta un generico Product Public Class GenericViewer Private moDevice As Control Private moSubject As GenericObject Public Overridable Sub Paint() moDevice.Text = moSubject.ToString End Sub End Class

‘la classe generic viewer rappresenta un generico Creator Public Class GenericObject Protected moViewers As System.Collections.ArrayList Public Overridable Function CreateViewer() As GenericViewer Dim oViewer As New GenericViewer moViewers.Add(oViewer) Return oViewer End Function End Class

SOFTWARE ENGINEERING

Page 42: v2005 04 vbj64

42 VBJ N. 64 - Luglio/Agosto 2005

Verificarela disponibilitàdi un control OCXcon VB6

di Lorenzo Vandoni

Supponiamo di voler scrivere un programma che utilizzi un controllo OCX, senza sapere se questo sarà effettivamente disponibile su tut-

te le macchine sulle quali il programma dovrà esse-re installato.

I motivi per cui una situazione del genere si può ve-rifi care sono molti.

Il control in questione potrebbe costituire una fun-zionalità separata, disponibile a pagamento, oppure potrebbe essere un control di terze parti, da acqui-stare separatamente.

Indipendentemente dal motivo per cui una situa-zione del genere si possa verifi care, è interessante studiare una soluzione per questo problema perché, come vedremo, nonostante sia piuttosto semplice, non è del tutto ovvia.

La differenza tra OCX e DLL

Se il componente non fosse implementato come controllo OCX, ma come ActiveX DLL, la soluzione al problema sarebbe molto semplice.

Basterebbe infatti utilizzare la seguente istruzione:

CreateObject(“NomeDll.NomeClasse”)

Questa istruzione, nel caso in cui l’oggetto in questione non esista o non sia stato registra-to, restituisce un errore del tipo ActiveX component can’t create object. Sarà quindi suf-ficiente intercettare l’errore con un’istruzione del tipo On Error GoTo per poter prendere le con-tromisure desiderate.

Con un control OCX que-sta tecnica però non è appli-cabile.

Un control normalmente vie-ne aggiunto staticamente alla form, in fase di design, e in ogni caso non può essere creato con un’istruzione CreateObject.

Nel caso il control sia aggiun-to staticamente alla form, però, qualora l’applicazione venga in-stallata su una macchina dove quel control non sia disponibile, si avrà un errore a runtime.

A volte è necessario che un programma si comporti in modo diverso a seconda delladisponibilità di un determinato control OCX

Lorenzo Vandoni è laureato in Informatica, ed è uno spe-cialista di progettazione e sviluppo con tecniche e linguaggi object-oriented. Ha collaborato alla realizzazione di framework, strumenti di sviluppo e software commerciali in C++, Java e Visual Basic. Può essere contattato tramite e-mail all’indirizzo [email protected].

TECNICHE

Page 43: v2005 04 vbj64

43N. 64 - Luglio/Agosto 2005 VBJ

La soluzione

La soluzione al problema consiste nell’ag-giunta del control a runtime, cosa che può es-sere fatta col metodo Add, applicato alla col-lection Controls della form. Per aggiungere a runtime un control di tipo Label, ad esempio, si può scrivere:

myForm.Controls.Add “VB.Label”, “MyLabel”

In questa istruzione, MyLabel costituisce il nome del nuovo control, che sarà quindi ac-cessibile scrivendo:

myForm.Controls.Item(“MyLabel”)

Per poter utilizzare questa tecnica bisogna però fare attenzione ad un paio di partico-larità:

• prima di tutto, il ProgId, ovvero il tipo del control, specifi cato come primo argomento della chiamata al metodo Add, non sempre coincide con il nome del control stesso.

Tanto per fare un esempio concreto, esiste un control di tipo DNSToolsCtl.DgnVoiceTxt, che ha come ProgId la stringa Dragon.VTxtCtrl.1. Fortunatamente Visual Basic viene in aiuto in questo frangente, segnalando il corret-to valore del ProgId all’interno della descri-zione dell’errore che si verifi ca tentando di creare il control utilizzando come ProgId il suo tipo.

• la creazione del control, in ogni caso, può fallire se non si aggiunge il control al proget-to, e non si deseleziona l’opzione Remove information about unused ActiveX controls, visibile tra le proprietà del progetto.

In alternativa a quest’ultima opzione, esiste anche la possibilità di creare dei control a run-

Se il componente fosse

implementato come Acti-

veX DLL la soluzione sa-

rebbe molto semplice

time senza referenziarli all’interno del progetto. Per poterlo fare, però, occorre affrontare il pro-blema delle licenze.

Il problema delle licenze

Alcuni control ActiveX sono protetti da una li-cenza, che consente il loro utilizzo solo da parte degli sviluppatori che li hanno acquistati.

Per uno sviluppatore Visual Basic, la gestione delle licenze è normalmente del tutto trasparen-te, essendo suffi ciente aggiungere il control alla form per poterlo utilizzare.

In questo caso, infatti, le informazioni di licenza vengono automaticamente incluse all’interno.

Nel caso si voglia però creare il control a run-time, senza referenziarlo all’interno del progetto, occorre tenere conto di questo aspetto.

Fortunatamente, la procedura da adottare non è troppo complicata.

L’elenco delle licenze disponibili all’interno dell’applicazione, è disponibile nella collezione Licenses. Per aggiungere una nuova licenza, è quindi suffi ciente scrivere:

Call Licenses.Add(“NomeDll.NomeControl”)

Nel caso in cui il control non sia correttamen-te installato e “licenziato” sulla macchina sul-la quale l’applicazione viene eseguita, questa istruzione provocherà un errore a runtime, che potrà essere intercettato in modo da eseguire le azioni opportune.

Conclusioni

Il problema presentato è piuttosto partico-lare, e probabilmente a molti sviluppatori non capiterà mai di verifi carlo. La ricerca di una soluzione ha permesso però di evidenziare al-cune caratteristiche interessanti e non mol-to conosciute legate alla gestione dei con-trol ActiveX.

TECNICHE

Un control normalmente

viene aggiunto

staticamente alla form

in fase di design

Page 44: v2005 04 vbj64

44 VBJ N. 64 - Luglio/Agosto 2005

Controllo Remoto inVisual Basic .NET

di Stefano Corti

APPLICATIVI

Il linguaggio Visual Basic .NET può essere defi ni-to a tutti gli effetti un moderno linguaggio di pro-grammazione orientato agli oggetti. Al pari di C# e

di Visual C++ .NET, costituisce parte integrante della nuova piattaforma .NET di Microsoft che, insieme al-l’ambiente di sviluppo Visual Studio, rappresenta un importante e raffi nato riferimento per tutti i program-matori che desiderano sviluppare applicazioni per i sistemi operativi Microsoft Windows.

Queste pagine costituiscono il primo di una serie di appuntamenti dedicati alla progettazione e allo svilup-po di un’applicazione completa in Visual Basic .NET. Più precisamente spiegheremo in dettaglio i processi per sviluppare un programma che consente di con-trollare, attraverso una rete basata su protocolli TCP/IP, una macchina remota. Realizzeremo quindi due di-stinti applicativi; uno denominato “Controller” desti-nato a girare sul PC controllante ed un secondo ap-plicativo che chiameremo “Target” che dovrà essere eseguito sulla macchina che ci prefi ggiamo di con-trollare. Con l’applicazione che ci accingiamo ad illu-strare potremo eseguire le seguenti operazioni:

• Sessioni di chat-line tra Controller e Target in varie modalità di dialogo.

• Esplorare le unità logiche e le cartelle del compu-ter remoto.

• Creare nuove cartelle, rinominare e cancellare fi le sul computer remoto.

Stefano Corti si occupa di programmazione PHP e JSP lato server, della piattaforma .NET e di integrazione di sistemi legacy con le nuove realtà del web, soprattutto in ambito ge-stionale, bancario e finanziario. È attualmente alle dipendenze di un primario gruppo bancario italiano. Può essere contattato tramite e-mail all’indirizzo [email protected].

• Trasferire fi le dalla macchina locale alla macchina remota e viceversa.

• Ottenere le schermate video del computer remoto.

• Ottenere informazioni su alcuni aspetti dell’hardware del com-puter remoto.

• Remotizzare in real-time la shell di DOS.

• Operare sul registry del com-puter remoto.

Analizzeremo in dettaglio alcu-ne importanti classi raggruppate in specifi ci namespace e ci de-dicheremo alla creazione di nuo-ve classi istanziabili per rendere operative le funzioni proposte. I programmi presentati non sono destinati ovviamente ad ambien-ti di produzione, ma ci offriranno l’occasione di soffermarci attenta-mente su alcuni importanti aspet-ti del linguaggio e della Class Li-brary del .NET framework.

Introduzione ai Socket TCP e alle classi del namespace

System.Net.Sockets

La nostra applicazione preve-de un modello progettuale di tipo “Client/Server”.

Il programma Target in esecu-zione sulla macchina remota, è

RemotePrima puntata

Page 45: v2005 04 vbj64

45N. 64 - Luglio/Agosto 2005 VBJ

dura Sub richiesta, che nel nostro caso risulta essere runServerTarget().

rThread = New Thread(AddressOf runServerTarget)

rThread.Start()

Si noti la presenza dell’operatore AddressOf che consente di creare un delegato di funzio-ne che fa riferimento alla funzione specifi ca-ta nell’argomento. Nel nostro caso si tratta di una procedura Sub. Quindi invochiamo il me-todo Start() che innesca l’avvio del Thread. È importante precisare che queste linee di codi-ce devono essere collocate all’interno del co-struttore della Form1 Sub New(), subito dopo la chiamata a InitializeComponent(), affi nchè il Thread contenente la procedura runServerTar-get() venga avviato nel momento in cui la Form viene caricata e inizializzata. In seguito tornere-mo maggiormente in dettaglio su questi aspetti delle procedure multi-threading. Ora focalizzia-mo la nostra attenzione sulla progettazione det-tagliata del server di base, insieme al relativo client. Per prima cosa dobbiamo fare in modo che l’indirizzo IP della macchina Target venga reso noto all’utente, affi nchè possa essere co-municato al Controller, laddove il provider di connettività fornisca una soluzione basata sul-l’assegnazione di IP dinamici. Per ottenere l’in-dirizzo IP del computer Target, defi niamo una particolare funzione che chiameremo getLoca-lIp() che riportiamo integralmente.

Private Function getLocalIp() As Strin

Dim HostName As String

Dim indiIp As String

HostName = System.Net.Dns.GetHostName()

indiIp = System.Net.Dns.GetHostByName(HostName).

AddressList(0).ToString()

Return indiIp

End Function

La funzione ritorna un valore stringa indiIp contenente l’indirizzo IP della macchina sulla quale è in esecuzione. La classe Dns, appar-tenente al namespace System.Net, espone il metodo GetHostName() che consente di otte-nere il nome host del computer locale, passa-to alla variabile stringa HostName. Tale stringa viene poi passata come parametro del meto-do GetHostByName(); tale metodo esegue una query nel server Dns per ottenere informazio-ni sull’Host specifi cato. Il valore ritornato è di tipo IPHostEntry ed è relativo all’omonima clas-

APPLICATIVI

a tutti gli effetti un server in ascolto su una determinata porta, pronto ad accettare le con-nessioni e le richieste di un client che, nel no-stro caso, è costituito dal programma Controller. Utilizzeremo TCP come protocollo di traspor-to in quanto, a differenza dei socket di data-gramma basati su UDP, è fortemente orienta-to alla connessione tra processi in esecuzione, laddove vi è un effettivo controllo della corretta consegna dei pacchetti inoltrati. Dovremo suc-cessivamente sviluppare un protocollo applica-tivo basato su opportuni comandi a stringa li-bera che verranno inviati dal Controller e che dovranno essere comprensibili dal Target affi n-ché possa adeguatamente eseguire le opera-zioni richieste, al pari di quanto avviene con i comandi di protocolli standard quali ad esem-pio RETR, TOP, NOOP, DELE nel caso del pro-tocollo POP3 oppure GET e POST nel caso di HTTP. Sceglieremo quindi uno o più numeri di porta non assegnati da IANA (0 - 1023 well-known port numbers) per mettere in ascolto i vari servizi esposti dall’applicativo Target. Os-serviamo il Listato 1.

Per prima cosa dobbiamo importare nel pro-getto alcuni namespace che contengono le clas-si richieste. Il namespace System.Net.Sockets espone un certo numero di classi per la fun-zionalità di networking, mentre il namespace System.Threading contiene classi specifi che per la defi nizione di procedure multi-thread. Abbia-mo scelto la porta TCP 13000 per porre in stato di ascolto il server di base, la cui procedura di defi nizione è localizzata nella Sub runServerTar-get(). Il server di base ci consentirà di interpre-tare ed eseguire la maggior parte dei comandi impartiti dal controller, per lasciare a procedure specifi che le funzionalità di trasferimento fi le e remotizzazione desktop Target. Per questi par-ticolari aspetti utilizzeremo altri numeri di porta. Poiché è importante che i server in ascolto sul-le diverse porte siano in qualche modo sincro-nizzati e contemporaneamente attivi, dovremo inserire le relative procedure Sub in altrettanti Thread separati, il cui ciclo di vita deve essere inizializzato nel momento opportuno. Per ora occupiamoci del Thread principale, al cui in-terno è in funzione il server sulla porta 13000. Prima di tutto dobbiamo istanziare un nuovo oggetto Thread che chiameremo rThread. De-fi niamo rThread privato in quanto non vogliamo che sia visibile all’esterno della classe Form1 e poi creiamo un oggetto rThread passando come parametro del costruttore il nome della proce-

Page 46: v2005 04 vbj64

46 VBJ N. 64 - Luglio/Agosto 2005

se; tale classe presenta il metodo AddressList() che recupera un array di tipo IPAddress con-tenente una lista di indirizzi IP risolti a partire dal nome Host (nel nostro caso utilizziamo l’in-dice 0 dell’array in quanto dobbiamo ottenere un solo indirizzo di base). Il metodo ToString(), infi ne, converte il tipo ritornato in una stringa e lo assegna alla variabile (string) indiIp. Ovvia-mente l’intera funzione verrà dichiarata privata in quanto non vi è ragione per renderla visibi-le all’esterno della nostra classe base Form1. Provvediamo quindi a visualizzare l’informazio-ne ottenuta nella barra del titolo del nostro ap-plicativo Target.

Me.Text = “Remote Administrator - Target - Indirizzo IP:

“ & getLocalIp()

Premendo un apposito pulsante il nostro indiriz-

zo IP verrà anche visualizzato in un TextBox.

Private Sub showIpButton_Click(ByVal sender As

System.Object, ByVal e As System.EventArgs)

Handles showIpButton.Click

ipText.Text = “Indirizzo IP da comunicare al

Controller: “ & getLocalIp()

End Sub

La funzione getLocalIP() ci servirà anche per

passare il valore dell’indirizzo IP alle relative

linee di codice che implementano un sempli-

ce sistema di autenticazione essenzialmente

basato su controlli di assegnazione di indiriz-

zi IP, nome utente e relativa password. Questi

aspetti li vedremo dettagliatamente in seguito.

Li sta to 1 Struttura base del server

Imports System.IOImports System.ThreadingImports System.Net.Sockets

Private rThread as ThreadPrivate writer As BinaryWriterPrivate reader As BinaryReaderPrivate socketStream As NetworkStreamPrivate connection As Socket

Public Sub New() MyBase.New() InitializeComponent() rThread = New Thread(AddressOf runServerTarget) rThread.Start()End Sub

Public Sub runServerTarget() Dim ip As String = getLocalIp() Me.Text = “Remote Administrator - Target - Indirizzo IP: “ & getLocalIp() Dim listener As TcpListener Try listener = New TcpListener(13000) listener.Start() While True inCommands.Clear() inMessages.Clear() outMessages.Clear() inCommands.Text &= “PC pronto per accettare connessioni da Controller” & vbCrLf connection = listener.AcceptSocket() socketStream = New NetworkStream(connection) writer = New BinaryWriter(socketStream)

reader = New BinaryReader(socketStream)

inCommands.Text &= “Connessione accettata...” & vbCrLf

...

End While

...

End Try

APPLICATIVI

Page 47: v2005 04 vbj64

47N. 64 - Luglio/Agosto 2005 VBJ

Proseguiamo ora nel-la spiegazione del no-stro server. Procedia-mo quindi alla crea-zione di un oggetto Listener di tipo TcpLi-

stener e inizializzia-mo la nuova istanza della classe passando al costruttore il nume-ro di porta sulla quale desideriamo eseguire la sessione di ascolto del server.

Nel nostro caso si tratta della por-ta 13000. La classe TcpListener fornisce metodi che attendono e accettano richieste di connessioni in in-gresso in modalità di blocco sincrona.

Più precisamente il metodo Start() inizia-lizza il socket sottostante, lo associa al punto fi nale locale e attende tentativi di connessione in ingresso.

Quindi, all’interno di un ciclo While infi nito, in-vochiamo il metodo AcceptSocket() dell’oggetto Listener e ne passiamo il valore restituito alla variabile privata connection di tipo Socket. Ac-

ceptSocket() è un metodo di blocco che resti-tuisce un socket che è possibile utilizzare per inviare e/o ricevere dati.

Ora che abbiamo ottenuto un canale di comu-nicazione, dobbiamo preparare il socket per vei-colare i dati richiesti sotto forma di stream bina-ri di byte. Dunque creiamo un oggetto Network-

Stream – che chiameremo socketStream – e lo inizializzeremo passando al relativo costruttore il valore di connection. Sono necessari infi ne al-tri due oggetti; uno di tipo BinaryWriter e uno di tipo BinaryReader, che eseguono rispettivamen-te la scrittura e la lettura di tipi primitivi in codi-ce binario in un particolare fl usso. Il costruttore di queste due particolari classi è un costrutto-re di overload che accetta anche valori di tipo NetworkStream.

La classe NetworkStream, lo ricordiamo, for-nisce il fl usso di dati sottostante per l’accesso alla rete e per garantire il transito di dati all’in-terno del socket. In questo modo abbiamo otte-nuto l’apertura di un socket TCP e due oggetti

(writer e reader) istanziati da due classi che for-niscono metodi specifi ci per consentire la scrit-tura e la lettura dei fl ussi binari.

Non appena il server riceve una richiesta di connessione da parte del client, rappresentato dal programma Controller, è possibile scrivere tutto il codice che ci serve per elaborare i dati ricevuti. Abbiamo detto che il protocollo applica-tivo che andremo ad implementare è basato su comandi stringa formattati secondo precisi pa-rametri, ragione per cui è necessario estrarre le stringhe veicolate dal fl usso binario che abbia-mo a disposizione per mezzo dei metodi appli-cati agli oggetti writer e reader rispettivamente di tipo BinaryWriter e BinaryReader.

A tal fi ne, utilizziamo il metodo ReadString(), che legge e restituisce dal fl usso corrente una stringa avente un prefi sso di lunghezza e codi-fi cata come numero intero, 7 bit alla volta.

La stringa ottenuta può così essere analizzata per mezzo di semplici meccanismi di manipo-lazione di stringhe e i comandi inviati possono essere così ricostruiti ed eseguiti.

Dim reply As String

reply = reader.ReadString()

È importante osservare che tutto il codice di implementazione del server viene inserito in due

APPLICATIVI

Li sta to 2 I blocchi che intercettano le eccezioni nello scambio dei dati

Try...While True...Try...Catch inputOutputException As IOException sentText.Text &= “Interruzione nel trasferimento dati.” sentText.Text &= vbCrLf & “In attesa di riconnessione da Controller.” Finally inCommands.Text &= vbCrLf & _ “SESSIONE CHIUSA DA CONTROLLER.” & vbCrLf writer.Close() reader.Close() socketStream.Close() connection.Close() End Try End WhileCatch inputOutputException As IOException MessageBox.Show(“Eccezione Intercettata: “ & _ inputOutputException.Message, “Eccezione Sollevata”, _ MessageBoxButtons.OK, MessageBoxIcon.Error)End Try

Page 48: v2005 04 vbj64

48 VBJ N. 64 - Luglio/Agosto 2005

cicli While End While e Do Loop While nidifi ca-ti. Il ciclo while più esterno costituisce in realtà un loop senza fi ne (While True) che viene in-terrotto nel caso in cui venga generata ed inter-cettata un’eccezione. Il ciclo Do Loop While più interno invece consente di stabilizzare il server affi nchè sia in grado di continuare a ricevere i comandi dal Controller.

Tale ciclo si ripete a meno che venga ricevu-to il comando TERMINATE e fi nchè la proprietà connected di connection (che abbiamo visto es-sere un oggetto istanziato dalla classe Socket ) restituisce True. Notiamo che l’intera struttura viene inserita in un blocco Try Catch End Try all’interno del quale vi è un blocco Try Catch

Finally EndTry.Abbiamo previsto il primo blocco per intercet-

tare possibili eccezioni sollevate nel caso si do-vesse verifi care un errore mentre viene stabili-

ta la connessione fra gli end-point. Il secondo blocco invece si occupa di intercettare eccezio-ni generate da errori durante la lettura dei dati. In questo specifi co caso, dal momento che la connessione è già stabilita, includiamo nel bloc-co Finally tutto il codice che ci serve per rila-sciare le risorse occupate, invocando il meto-do close() per gli oggetti writer, reader, socket-Stream e connection (Listato 2).

Un’analoga struttura di intercettazione di pos-sibili eccezioni di tipo IOException dovrà esse-re implementata a livello client sull’applicativo Controller.

Realizzazione dell’architettura Client-Server e implementazione del programma Controller

Vediamo ora come realizzare una vera e pro-pria struttura di codice in grado di svolgere sem-

plici transazioni basa-te sul modello Client-Server. Abbiamo vi-sto, di massima, come realizzare il program-ma server. Ora ci con-centreremo sulla pro-gettazione del client e vedremo come i due sistemi possono dia-logare. È importan-te capire che ciò che dovremo essere in grado di estrarre dal-la sequenza binaria in transito sul socket aperto è sempre una sequenza di stringhe alfanumeriche, che contengono i coman-di del nostro protocol-lo. Di fatto, per sem-plifi care, dovrà esse-re possibile eseguire una sessione telnet sulla porta 13000 del server in esecuzione e quindi potere esse-re in grado di esegui-re alcune transazioni semplicemente scri-vendo i comandi di-rettamente nel prompt cmd.exe Osserviamo il Listato 3.

Li sta to 3 Struttura del client

Private output As NetworkStreamPublic Shared reader As BinaryReaderPublic Shared writer As BinaryWriter

Public Sub runClientController() Dim client As TcpClient Try Dim job As String() = Nothing Dim ip As String = inIP.Text Dim port As Int32 = CInt(inPort.Text) client = New TcpClient() client.Connect(ip, port) output = client.GetStream() writer = New BinaryWriter(output) reader = New BinaryReader(output) inMessages.Text &= vbCrLf & “Ricevuto flusso I/O” & vbCrLf Try Do messaggio = reader.ReadString() job = messaggio.Split(delimiter, 2) txtMessages.Clear() If (job(0) < > “REPLYDRIVESSEQUENCE” And _ job(0) < > “REPLYREMOTEFIL” And _ job(0) < > “READYTORECEIVEFILE” And _ job(0) < > “REPLYREMOTEDIR” And _ job(0) < > “REPLYBASESUBKEYS” And _ ... inMessages.Text &= vbCrLf & “Messaggio ricevuto da Macchina Target: “ & _ vbCrLf & messaggio End If

If job(0) = “ESTABILISHED” Then If job(1) = “AUTHREQUIRED” Then ....

APPLICATIVI

Page 49: v2005 04 vbj64

49N. 64 - Luglio/Agosto 2005 VBJ

Questa volta la procedura che contiene il

codice verrà chiamata runClientController() e

sarà anch’essa inserita in un Thread. In que-

sto caso il Thread non verrà inizializzato diret-

tamente nel costruttore Sub New() della Form

del programma Controller, altrimenti ad ogni av-

vio dell’applicativo la procedura verrà invoca-

ta e tenterà di connettersi ad un server, senza

nemmeno dare la possibilità all’utente di forni-

re le proprie credenziali e l’indirizzo IP del ser-

ver medesimo.

Abbiamo pensato quindi di eseguire la chia-

mata del metodo Start() dell’oggetto cThread

all’interno della procedura Sub ButtonIP_Cli-

ck attivata in seguito allo scatenarsi dell’even-

to collegato alla pressione del pulsante “Apri

Connessione”, contenuto nella scheda “Con-

nessioni” della GUI.

Come possiamo facilmente notare, il Thread

viene inizializzato solo a condizione che sia sta-

to effettivamente inserito un indirizzo IP valido,

oppure che sia stata compilata la scheda di au-

tenticazione. In pratica il campo di testo inIP

deve contenere l’indirizzo IP in formato stringa

e la proprietà TargetIP dell’oggetto objAu istan-

ziato dalla classe AuthenticationMechanism, di

cui parleremo in seguito, non deve contenere

la default network 0.0.0.0.

Private Sub ButtonIP_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles

ButtonIP.Click

If ((inIP.Text = “” Or inIP.Text Is Nothing) And

objAu.TargetIP = “0.0.0.0”) Then

MessageBox.Show(“Attenzione: Dovete inserire

un indirizzo IP valido”, _

“Messaggio di errore”, MessageBoxButtons.OK, _

MessageBoxIcon.Error)

Else

inIP.Text = objAu.TargetIP

cThread = New Thread(AddressOf runClient

Controller)

cThread.Start()

ButtonIP.Enabled = False

End If

End Sub

Vediamo in dettaglio la progettazione del client. Creiamo subito un’istanza client della classe TcpClient. Questa importante classe espone metodi per stabilire connessioni di tipo client per i servizi di rete Tcp.

Il costruttore di questa classe è di tipo over-load; in questo caso utilizziamo il costruttore di default, omettendo l’inserimento di parame-tri nell’interno del costruttore stesso.

Uno dei metodi di base che possiamo utiliz-zare è Connect.

Si tratta di un metodo di overload che con-nette un client a un host Tcp remoto utilizzan-do il nome host e il numero di porta specifi ca-ti. Il nome host (eventualmente il corrispettivo indirizzo IP della macchina remota) può essere passato come argomento di tipo String, men-tre il numero di porta deve essere necessaria-mente espresso come Integer32.

APPLICATIVI

Tabella 1 Alcuni comandi estraibili dallo stream binario

IPCOMINGFROM>192.168.201.10 Fornisce al server l’indirizzo IP del Controller

IDCOMINGFROM>CONTROLLER Fornisce al server l’identità alfanumerica del Client

ESTABILISHED>NOAUTH Avvisa di un errore nella fase di autenticazione

ESTABILISHED>OKAUTH Comunica esito positivo durante la fase di autenticazione

TEXTWITHREPLY>ARGOMENTOTrasmette a Target un messaggio di testo con risposta

obbligatoria

GETREMOTEMACHINEDATAS>NOARGS Richede a Target dati Hardware/Software macchina remota

GETDRIVES>NOARGS Richiede a Target elenco unità logiche e partizioni su mac-

china remota

STARTSELECTEDFILE>ARGOMENTO Avvia il processo associato al file selezionato il cui percorso

assoluto è rappresentato dalla stringa argomento

SETREGISTRYBASEKEY>ARGOMENTOImposta la chiave Hive specificata nella stringa argomento nel

registro remoto per una successiva esplorazione ed editazione

DOWNTRAY>NOARGS Minimizza l’applicativo Target nella Systray remota

Page 50: v2005 04 vbj64

50 VBJ N. 64 - Luglio/Agosto 2005

Con questi valori specifi cati, il programma Controller sarà in grado di connettersi al ser-ver che abbiamo precedentemente sviluppato. Istanziamo quindi un oggetto di tipo Network-Stream e due oggetti (writer e reader) di tipo BinaryWriter e BinaryReader, analogamente a quanto abbiamo eseguito durante la progetta-zione del server Target.

In questo modo siamo in grado di estrarre dal-lo stream binario i comandi stringa che ci ser-vono. È bene notare che, in questo contesto, gli oggetti reader e writer sono dichiarati pub-blici e statici.

Ciò signifi ca che potranno essere direttamente invocati da altre classi, senza bisogno di crea-re nuove istanze.

Di fatto questi oggetti dovranno essere ac-cessibili ed utilizzabili da una seconda specifi -ca classe che si occupa della remotizzazione dell’editor del registro, di cui parleremo diffu-samente in seguito.

Defi nizione del protocollo di comunica-zione e dei comandi a stringa libera

Ora che abbiamo visto come stabilire una con-nettività di tipo client/server tra i due nostri ap-plicativi, vediamo subito come defi nire un sem-plice protocollo applicativo basato su opportuni comandi a stringa libera che le due procedu-

re residenti su due diverse macchine devono scambiarsi per potere comunicare ed esegui-re le operazioni richieste. Come abbiamo visto, tali comandi testuali vengono iniettati sotto for-ma di stream binari nei fl ussi socket Tcp aperti. Adesso vediamo come ricostruire le sequenze e determinare i vari parametri passati come ar-gomento dei vari comandi.

Ricordiamo che molti protocolli in uso sulle reti TCP/IP quali FTP, HTTP, POP3, SMTP, funzio-nano in questo modo. Di fatto, senza utilizzare alcun client basato su GUI, è possibile esegui-re un Telnet sulla porta 110 del server remoto ed eseguire una sessione di consultazione dei messaggi eventualmente presenti, utilizzando il comando RETR oppure vedere solo il campo header del messaggio seguito da un numero di linee specifi cate (comando TOP); potremo infi ne cancellare determinati messaggi trami-te il comando DELE.

La stessa cosa può essere eseguita sulla porta 21 dove si trova in ascolto il server FTP (se previsto dall’amministratore del server re-moto). Abbiamo chiarito questo concetto, dal momento che il nostro server target si com-porta nel medesimo modo.

In pratica Controller e Target si scambiano continuamente informazioni tramite determinati comandi che ora andremo a defi nire.

La nostra procedura è basata appunto su questa tipologia di architettura, ad eccezione delle routine di trasferimento fi le e remotizza-zione schermate desktop remoto, operanti su differenti porte e con differenti modalità di tra-sferimento dati.

L’estrazione dei comandi dallo stream bina-rio è alquanto semplice utilizzando, come ab-biamo detto, il metodo ReadString().

Ogni comando è separato dal relativo para-metro da uno o più caratteri speciali (abbiamo utilizzato il carattere > e il carattere | ). Vedia-mo nella Tabella 1, a puro scopo esemplifi ca-tivo, alcuni comandi implementati.

Rammentiamo che è possibile visualizzare in uno specifi co TextBox tutti i comandi che, a basso livello, transitano tra le due procedu-re, al fi ne di poter comprendere agevolmente il meccanismo di comunicazione sottostante. Adesso ci dobbiamo occupare di scrivere il codice che svolge le funzioni di interpretazio-ne di questi comandi.

Abbiamo visto che nella stringa reply si trova la sequenza estratta contenente il comando e gli eventuali parametri operativi.

APPLICATIVI

Li sta to 4 Assegnamento alla variabile oFileDirName

Imports SystemImports System.IO

Public Class RemoteStructure

Private oFileDirName As String Private oInformation As String Private oReturned As String Private oDrive As String Private oSequenzaDrives As String Private oSequenzaDirFil As String Private oUnits As String() Private oDirectoryList As String() Private oDirectoryFileList As String() Private oStream As StreamReader

Sub New() Me.oFileDirName = “C:\” End Sub Sub New(ByVal FileName As String) Me.oFileDirName = FileName End Sub

Page 51: v2005 04 vbj64

51N. 64 - Luglio/Agosto 2005 VBJ

Dobbiamo innanzitutto separare, a livello di stringa, il comando propriamente detto dal va-lore specifi cato nell’argomento.

Per ottenere ciò, è suffi ciente chiamare il me-todo split() della classe String.

Questo metodo consente di individuare le sot-tostringhe dell’istanza interessata, che sono delimitate da uno o più caratteri ASCII speci-fi cati in una matrice, iniettando quindi le sot-tostringhe restituite in una matrice di ogget-ti String.

Dim reply As String = “”

Dim delimStr As String = “>”

Dim delimiter As Char() = delimStr.ToCharArray()

Dim job As String() = Nothing

Try

Do

reply = reader.ReadString()

job = reply.Split(delimiter, 2)

Dichiariamo job come matrice di tipo String e, dopo la chiamata del metodo Split(), otte-niamo la matrice a una dimensione dove job(0) contiene la stringa del comando, mentre job(1) contiene la stringa del parametro passato.

A questo punto non ci resta che analizzare il contenuto della matrice con indice 0 all’in-terno di una struttura Select Case End Select per determinare le azioni da intraprendere in seguito alla ricezione dei comandi inviati dal Controller ed utilizzando i valori stringa con-tenuti in job(1) come parametri da associare al comando in fase di esecuzione.

Select Case job(0)

Case Is = “SHOWALLRUNNINGPROCESSES”

inMessages.Clear()

inMessages.Text = “Attivata Richiesta Processi”

Try

writer.Write(objProc.displayAllProcesses())

Catch exception As SocketException

...

End Try

Case Is = “SHOWPROCESSDETAILS”

inMessages.Clear()

inMessages.Text = “Attivata Richiesta Dettagli Processi”

Try

writer.Write(objProc.showProcessDetails(job(1)))

Catch exception As SocketException

...

Catch e As Exception

End Try

...

End Select

Progettazione della classe RemoteStructure ed implementazione delle funzioni di base

Ora che abbiamo visto in dettaglio come rea-lizzare una semplice architettura basata sul mo-dello client/server operante sui socket TCP, oc-cupiamoci dell’implementazione del codice di una delle classi base del nostro progetto. Chia-meremo questa classe RemoteStructure e im-plementeremo metodi e proprietà per le fun-zioni di base di fi le manager remoto.

Scriveremo quindi funzioni pubbliche e pri-vate per l’esplorazione delle risorse su Target, per ottenere una lista completa delle unità logi-che (unità dischi fi sici e logici) presenti sul PC remoto, per eliminare fi le e cartelle e per otte-nere le proprietà dei fi le residenti sulla mac-china da controllare.

Prima di tutto, in questa fase, è necessario importare nel progetto il namespace System.IO che contiene al suo interno un elevato numero di classi che consentono la lettura e la scrittu-ra in fi le e fl ussi di dati e tipi che forniscono il supporto per i fi le e le directory di base.

Dichiariamo quindi alcune variabili di classe private. Una delle più importanti è sicuramen-te oFileDirName.

Si tratta di una variabile privata di tipo String che contiene il persorso assoluto di una di-rectory o di un fi le residente su PC remoto.

Questa variabile ci servirà spesso in quasi tutti i metodi pubblici della classe.

Realizziamo quindi un semplice costruttore sovraccarico che ci permette di inizializzare ogni oggetto di tipo RemoteStructure.

In caso di assenza di parametro, alla variabi-le oFileDirName verrà associato il valore strin-ga “C:\” corrispondente alla radice dell’unità logica C.

Altrimenti il costruttore potrà accettare an-che un parametro specifi cato – sempre di tipo String – consentendo, in fase di istanziazione dell’oggetto, di inizializzare l’oggetto stesso con un determinato path (Listato 4).

Nella prossima puntata vedremo come defi -nire i medoti e le proprietà più importanti del-la nostra classe RemoteStructure, che costi-tuirà anche una sorta di classe di base da cui deriveranno, con il meccanismo dell’eredita-rietà, altre importanti classi funzionali al no-stro progetto.

APPLICATIVI

Page 52: v2005 04 vbj64

52 VBJ N. 64 - Luglio/Agosto 2005

XML Class Generator

di Vito Vessia

I MITI

La metafora dell’XML come linguaggio dei lin-guaggi e come mezzo universale di interscambio delle informazioni è stata ironicamente descritta

nella premessa di [1] come “XML has replaced Java, Design Patterns, and Object Technology as the sof-tware’s industry solution to world hunger”… Al di là delle battute è indubbia l’importanza che ha assun-to ormai questo semplice e quasi banale formato di defi nizione dei dati nel mondo della comunicazione moderno. XML, nato come linguaggio di formattazio-ne dei contenuti, direttamente discendente da SGML, si è poi imposto come esperanto per qualsiasi infor-mazione si volesse produrre e distribuire.Al punto che, da completo sistema di defi nizione dei dati e quindi del contenuto, è diventato anche il lin-guaggio di defi nizione dei contenitori dei dati e quindi come vero e proprio protocollo applicativo.Un esempio per tutti è certamente SOAP, che usa proprio XML per defi nire un completo protocollo ap-plicativo per realizzare un RPC (Remote Procedu-re Call).

Altrettanto straordinario è stato l’impatto che han-no avuto i vari linguaggi di scripting nel mondo della programmazione: hanno unito la potenza dei linguag-gi di sviluppo tradizionali alla semplicità di scrittura e di deployment, in modo che qualsiasi ambito ap-plicativo potesse sfruttarne al massimo le peculiarità senza che fosse dotato di una complessa infrastrut-tura di compilazione e di runtime.

Scopo di questo articolo sarà presentare uno stu-dio su un possibile utilizzo dello scripting per realiz-

Vito Vessia È co-fondatore della codeBehind S.r.l. (http://www.codeBehind.it), una software factory di applicazioni enterprise, web e mobile, dove progetta e sviluppa applica-zioni e framework in .NET, COM(+) e Delphi di cui si occupa degli aspetti architetturali. È autore del libro “Programmare il cellulare”, Hoepli, 2002, sulla programmazione dei telefoni cellulari connessi al PC con protocollo standard AT+.Può essere contattato tramite e-mail all’indirizzo [email protected].

zare un generatore automatico di classi VBScript che rimappino (e ne facciano da proxy) la struttu-ra di un fi le XML qualunque, pur-chè dotato di schema (interno o esterno). In realtà la struttura del generatore di classi è semplice, ma alquanto complessa da spie-gare in modo astratto e sintetico in questa premessa.

Quindi non vi resta che leggere il seguito…

Gli schemi XML

Una interessante caratteristica dell’XML è la possibilità di veri-fi care la correttezza sintattica di un certo fi le rispetto ad un tem-plate standard di riferimento det-to schema.

Infatti l’XML è di per sé un for-mato piuttosto rigido, nel sen-so che deve essere garantita la correttezza sintattica, che preve-de una lunga serie di regole da rispettare, perché il fi le XML si possa defi nire well formed.

Queste vanno dal chiudere ogni tag che viene aperto, all’uso de-gli apici per i valori degli attribu-ti, alla presenza di un solo nodo padre per ciascun fi le e tanto al-tro ancora.

Non sarà argomento di questo articolo spiegare queste regole; la Rete e le librerie sono ormai sature di materiale sull’XML. Un buon consiglio per la lettura è certamente [1].

Generator

Come utilizzare il potente motore di scripting di Windows per pro-durre ed istanziare classi COM completamente costruite a runtime, per usufruire di tante interessanti potenzialità

Page 53: v2005 04 vbj64

53N. 64 - Luglio/Agosto 2005 VBJ

ment), con la struttura di ogni attributo che contiene (xs:attribute) compresi i tipi e le even-tuali constraint (es. use=”required”).

L’elenco dei nodi è fl at, cioè non presenta la tipica struttura gerarchica dei fi le XML che lega i nodi padre ai fi gli: questa gerarchia è invece defi nita attraverso la presenza dei nodi xs:sequence defi niti nei nodi xs:element.

Il principio di funzionamento è molto sempli-ce: per verifi care che il fi le XML sia semanti-camente corretto rispetto allo schema di ri-ferimento deve soddisfare tutte le regole di quest’ultimo.

Questa verifi ca di correttezza sarebbe un po’ troppo onerosa, anche se non impossibile, se realizzata a mano affi dandosi solo al nostro colpo d’occhio, ma in realtà il compito è rea-lizzato egregiamente da un buon parser XML di ultima generazione.

Si intende cioè un parser in grado di ma-nipolare e di comprendere gli schema XML di ultima generazione defi niti dal World Wide Web Consortium [2].

Nel nostro esperimento utilizzeremo il nuo-vo e potente parser Microsoft XML Core Ser-vices (MSXML) 4.0.

Questo componente racchiude al suo interno un classico DOM (Document Object Model) cioè un oggetto in grado di produrre un com-pleto object model a partire da un fi le XML, un SAX (Simple API for XML) cioè un compo-nente che non conserva in memoria l’intera struttura del fi le XML ma scatena degli eventi durante il parsing dello stesso.

I MITI

La verifi ca sintattica però non è tutto, ma si pone la necessità di defi nire un formato che descrivesse le regole semantiche a cui il fi le deve adeguarsi perché possa essere compre-so anche da chi non ne è l’autore.

Proviamo ad immaginare di voler defi nire un formato standard di fatture da condivide-re con i nostri clienti e fornitori, e per far ciò vogliamo basarci su XML; dobbiamo altresì specifi carne la logica (es. un nodo di testata che si chiama HEADER, che contiene alcu-ni attributi come Number, Year e altri anco-ra, poi devono esserci uno o più nodi di tipo ROW e così via).

Lo schema XML serve proprio a questo. In realtà gli schemi sono un’invenzione recen-te per l’XML: inizialmente il loro compito era svolta dai DTD (Document Type Defi nition) dalla logica molto simile.

Gli schemi hanno numerosi vantaggi: sono XML essi stessi, supportano l’ereditarietà e sono molto più potenti e completi. Osservia-mo ad esempio il Listato 1.

Lo schema di riferimento del fi le XML ap-pena mostrato è visibile nel Listato 2.

In realtà l’esempio è stato privato di numerosi attribu-ti e di alcuni nodi per ridur-ne l’occupazione tipografi ca, ma rende l’idea.

Per consultare il codi-ce Visual Basic si riman-da al sorgente che accom-pagna l’articolo, scaricabi-le liberamente dal sito ftp://ftp.infomedia.it.

Possiamo notare come il fi le XML presenti un riferimento allo schema a cui soggiace (Untitled2.xsd) e che lo sche-ma non sia altro che un con-tenitore di tutti i tipi di nodi presenti nel fi le (nodi xs:ele-

Fi gu ra 1 La gerarchia di oggetti dello Schema Object Model

XML è di per sé

un formato

piuttosto rigido

Page 54: v2005 04 vbj64

54 VBJ N. 64 - Luglio/Agosto 2005

È certamente più performante del DOM an-che se richiede una maggiore elaborazione e offre meno servizi.

La vera novità della versione 4.0 è invece la presenza del SOM (Schema Object Mo-del): un componente in grado di produrre un object model a partire da uno schema XML (Riquadro 1).

Data la natura completamente plain text del formato XML, sarebbe possibile editare, crea-re e modifi care questi fi le e gli schemi asso-ciati direttamente col Notepad, ma ci si ren-de conto ben presto che questo compito ha del disumano.

Ed è soprattutto vero quando si usano pe-santemente gli schemi.

Cosa c’entra lo scripting?

Giunti a questo punto della lettura vi chiede-rete cosa c’entra lo scripting in tutto questo. In effetti non c’entra apparentemente nulla, se non fosse che si possono realizzare interes-santi applicazioni dall’abbinamento della tec-nologia di scripting ActiveScript di Microsoft e l’XML con schema.

Infatti il disaccoppiamento tra schema XML e dati XML ricorda da vicino la metafora OOP della classe e dell’oggetto: la classe è la de-fi nizione formale delle regole e dei comporta-menti di un particolare aggregato strutturale di dati, l’oggetto è invece la stessa classe che prende vita e va in esecuzione su un partico-lare set di dati che corrisponde al prototipo dell’aggregato detto in precedenza.

Così se la defi nizione dei dati e dei compor-tamenti sugli stessi (la classe) è unica per una particolare tipologia applicativa, le sue “istan-ze” (gli oggetti) specifi che basate su set di dati differenti possono essere invece numerose e senza relazioni tra loro.

Così, nel caso specifi co, possiamo immagina-re di produrre un set di classi, un intero object model, a partire da uno schema XML.

Fatto questo saremo in grado di produr-re istanze di queste classi che si basano su fi le XML differenti, purché soggiacciano allo schema comune da cui sono state generate le classi. Vediamo un esempio di classi VB-Script che permettono di navigare il fi le XML dati.xml presente nella directory dei sorgenti che accompagnano l’articolo:

‘codice VBScript

Class vCRM

Private m_Dictionary

Private m_OnStartup

Private m_VERSION

Private m_AUTHOR

Ri qua dro 1 Schema Object Model (SOM)

Il SOM è basato sull’abstract W3C riferito in [3]. Esso prevede sostanzialmente l’accesso agli elementi conte-nuti in uno schema XML (proprietà, dichiarazioni, relazio-ni tra elementi). È quindi un set navigabile di classi che riflettono direttamente le specifiche W3C XSD (Schema Defi nition Language). In Figura 1 è riportata la gerarchia delle interfaccia implementate in questo componente. Si può notare come appunto la gran parte delle interfacce esposte sia discendenti da altre interfacce.

Vediamo subito un esempio di come si carica uno schema XML nel SOM. L’esempio è tratto dalla docu-mentazione a corredo del MSXML 4 [5]:

Dim oSchemaCache as New IXMLDOMSchemaCollection2Dim oSchema as ISchemaDim nsTarget as StringnsTarget = “http://www.sample.com/sampletarget”oSchemaCache.add nsTarget, “PO.xsd”Set oSchema = oSchemaCache.getSchema(nsTarget)

Viene dunque istanziato un oggetto IXMLDOMSche-maCollection2 e di questo viene invocato il metodo Add per aggiungere un nuovo schema (PO.xsd) con namespace http://www.sample.com/sampletarget alla collezione degli schema contenuta nell’oggetto appena istanziato. A questo punto è possibile farsi ritornare lo schema appena inserito grazie al metodo getSchema, ma questa volta nella forma di interfaccia ISchema che è l’interfaccia più importante del SOM perché è l’unica che permette di esplorare le caratteristiche dello sche-ma. Esistono comunque decine di interfacce diverse, alcune delle quali verranno adoperate nel proseguo di questo articolo. In generale si rimanda alla consultazione della ricca documentazione allegata al componente di Microsoft per una completa comprensione perché non è scopo di questa esposizione esplorare pienamente le caratteristiche del componente, cosa che richiederebbe da solo uno o più articoli interi.

I MITI

Realizzare un’intera

applicazione in VBScript

è probabilmente

poco pratico

Page 55: v2005 04 vbj64

55N. 64 - Luglio/Agosto 2005 VBJ

Public CUSTOMER

Public ORDER

Private Sub Class_Initialize()

set m_Dictionary = CreateObject

(“Scripting.Dictionary”)

m_OnStartup = True

set CUSTOMER = CreateObject

(“Scripting.Dictionary”)

set ORDER = CreateObject

(“Scripting.Dictionary”)

End Sub

Private Sub Class_Terminate()

set m_Dictionary = Nothing

set CUSTOMER = Nothing

set ORDER = Nothing

End Sub

Public Property Get Attributes()

Set Attributes = m_Dictionary ‘.Items

End Property

Public Property Get AUTHOR()

AUTHOR = m_AUTHOR

End Property

Public Property Let AUTHOR(valueField)

m_AUTHOR = valueField

End Property

End Class

Quella appena mostrata è la classe di navi-gazione del nodo vCRM del fi le XML in que-stione (fi le già parzialmente riportato in pre-cedenza in questo articolo).

Osservando le funzioni in essa contenute, possiamo notare la presenza di property let/get per accedere alla proprietà AUTHOR che, nel fi le XML, è l’attributo AUTHOR.

Esisterà quindi una coppia di questi meto-di nella classe per ogni attributo nel fi le XML d’origine.

Possiamo inoltre notare la presenza della property get Attributes che rappresenta la collezione di tutti gli attributi all’interno della classe, in modo che vi si possa accedere per indice e per chiave e, soprattutto, si possa applicare l’enumerazione COM (la for each). Così, per accedere all’elemento AUTHOR po-tremmo invocare:

‘codice VBScript

Dim oCRM

Set oCRM = New vCRM

oCRM.AUTHOR = “Vito Vessia”

Msgbox oCRM.AUTHOR

oppure accedere in modo simbolico alla pro-prietà:

MsgBox oCRM.Attributes(“AUTHOR”)

Li sta to 1 Un file XML

<?xml version=”1.0”?><vCRM AUTHOR=”Vito Vessia” mlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”F:

\ScriptClasses\XMLClasses\disk\Untitled2.xsd”> <ORDER DONE_NODE=”FALSE” CODE_SITE=”JRHOLD” CODE_CORRESPONDENT=”ARDILLO” CODE_INVOICE_CUST=”029900” CODE_SHIP_CUST=”029900” CODE_ORDER_TYPE=”%” CODE_FAIR=”COD_FIERA” CODE_PL_SOFA=”AJ” CODE_PL_ACCESS=”EF” CODE_CURRENCY=”ITL[~]” COD_CAUS_COMM=”G03” COD_CAUS_PROD=”MAG” CODE_DOC_TYPE=”003”> <HEADER_REMARK HRMRK_CODE_SITE=”JRHOLD” HRMRK_PROGRES=”1” HRMRK_CODE=”OCMC” HRMRK_REMARK=”Maximum[~]Care” HRMRK_ON_CREDITS=”0” HRMRK_ON_DDT=”1” HRMRK_ON_LABEL=”0” HRMRK_ON_INVOICE=”1” HRMRK_ON_OC=”1”/> <ROW CODE_DBALT=”031615” CODE_PL=”AJ” ROW=”1” NUM_REVIS_ROW=”0” ORDER_SET=”2” PO=”” CODE_PART=”073504224”> <ROW_REMARK RRMRK_CODE_SITE=”JRHOLD” RRMRK_PROGRES=”2” RRMRK_CODE=”OCMC” RRMRK_REMARK=”Maximum[~]Care”/> <COVER CODE_COVER=”15001011” CODE_QUEST=”Z0110001” FLG_ROW_MODIFIED=”” PROGR_RIV=”1” NUM_REVIS_COVER=”0” CVR_CODE_SITE=”JRHOLD”/> </ROW> </ORDER></vCRM>

I MITI

Page 56: v2005 04 vbj64

56 VBJ N. 64 - Luglio/Agosto 2005

e scorrerle tutte semplicemente con:

‘codice VBScript

Dim lAttribute

For Each lAttribute in oCRM.Attributes

Msgbox lAttribute

Next

La collezione degli attributi è realizzata usan-do un Dictionary a livello di classe che viene istanziato nella Class_Initialize.

Osservando però il nodo XML vCRM, possia-mo notare che esso possiede alcuni nodi fi gli.

Uno di questi è ORDER, la cui classe, secon-do il prototipo appena visto, dovrebbe essere come quella che segue (per ragioni di praticità in realtà se ne riporta solo un estratto):

‘codice VBScript

Class Order

Private m_Dictionary

Private m_OnStartup

Private m_CODE_FAIR

Private m_COD_CAUS_PROD

Private m_TOT_SIT

Public HEADER_REMARK

Public ROW

Public Property Get TOT_SIT()

TOT_SIT = m_TOT_SIT

End Property

Public Property Let TOT_SIT(valueField)

If Not IsNumeric(0 & valueField) Then

Err.Raise 1970, , “Invalid number!”

End If

m_TOT_SIT = valueField

End Property

End Class

Possiamo notare che la classe vCRM presen-tava una proprietà ORDER.

Questa rappresenta proprio la collezione dei nodi fi gli che, a tutti gli effetti, sono classi di tipo Order come quella appena mostrata.

Questa collezione è realizzata, ancora una vol-ta, col solito Dictionary.

Così, se vogliamo accedere e stampare a vi-deo la proprietà TOT_SIT del secondo nodo ORDER di una classe vCRM che fa da pro-xy al nostro XML vCRM, dobbiamo sempli-cemente scrivere:

Msgbox vCRM.ORDER(2).TOT_SIT

A sua volta, il nodo Order conterrà altri nodi fi gli ripercorrendo la stessa gerarchia del fi le XML originario, così da realizzare un mo-

Li sta to 2 Lo schema relativo al file del Listato 1

<?xml version=”1.0” encoding=”UTF-8”?><xs:schema xmlns:xs=“http://www.w3.org/2001/XMLSchema“ elementFormDefault=“qualified“> <xs:element name=»vCRM»> <xs:complexType> <xs:sequence> <xs:element ref=»ORDER»/> </xs:sequence> <xs:attribute name=”AUTHOR” type=”xs:string” use=”required”/> <xs:attribute name=”DATE” type=”xs:string” use=”required”/> </xs:complexType> </xs:element> <xs:element name=»ORDER»> <xs:complexType> <xs:sequence> <xs:element ref=»HEADER_REMARK» maxOccurs=»unbounded»/> <xs:element ref=”ROW” maxOccurs=”unbounded”/> </xs:sequence> <xs:attribute name=”DONE_NODE” type=”xs:string” use=”required”/> <xs:attribute name=”CODE_SITE” type=”xs:string” use=”required”/> </xs:complexType> </xs:element></xs:schema>

I MITI

Page 57: v2005 04 vbj64

57N. 64 - Luglio/Agosto 2005 VBJ

dello ad oggetti. Nel caso specifi co i nodi fi -gli di Order sono HEADER_REMARK e ROW e così via fi no a completare la gerarchia (es. vCRM.ORDER(2).ROW(3). COVER(1).CODE_QUEST). Si può vedere quindi come è possi-bile realizzare classi VBScript (o di scripting in generale in tecnologia Microsoft ActiveScrip-ting) che ripercorra, in forma più naturale ed immediata, la struttura di un fi le XML qualsiasi basandosi sullo schema a cui soggiace il fi le stesso. Sarà così possibile istanziare queste classi con i dati presenti nel fi le XML, infat-ti ogni attributo del nodo XML è esposto nel-la classe in forma di property get/let che per-metterà quindi anche di assegnare i valori di queste proprietà:

vCRM.ORDER(2).TOT_SIT = 3

Realizzare un’intera applicazione in VBScript è probabilmente poco pratico e ancor meno in-teressante dal punto di vista dell’implementa-zione in una situazione reale.

Si da il caso però che sia possibile utilizzare profi cuamente tali classi in applicazioni Visual Basic (VBA) o in generale in tutti quei linguag-gi ad alto livello capaci di gestire le interfacce

IDispatch di COM come mostrato in [4]. Bre-vemente vediamo come utilizzare direttamen-te da Visual Basic tali classi grazie al potente Microsoft Script Control [6] che è liberamente scaricabile dal sito di Microsoft e che sempli-fi ca drasticamente l’uso della tecnologia Acti-veScripting (il linguaggio usato sarà VBScript, alternativamente si sarebbe potuto usare qual-siasi altro linguaggio di scripting installato):

‘codice VBA

Dim oSC As Object ‘lo Script Control

Set oSC = CreateObject(“MSScriptControl.ScriptControl”)

With oSC

.AllowUI = True‘è permesso l’uso di interfaccia

utente negli script

.Language = “VBScript”

.UseSafeSubset = False ‘non impone nessuna

restrizione per la sicurezza

End With

‘a questo punto aggiungiamo il codice delle classi VBScript

che effettuano il proxy dei nodi XML come visto in precedenza

oSC.AddCode “<codice della classe>”

Fatto questo possiamo procedere all’istanzia-zione di queste classi VBScript e al loro uso all’interno di Visual Basic.

I MITI

Li sta to 3 Assegnazione di valori agli attributi in base alle caratteristiche

For Each lAttribute In lComplex.Attributes CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _ “ Public Property Get “ & lAttribute.Name & “()” & vbCrLf & _ “ “ & lAttribute.Name & “ = m_” & lAttribute.Name & vbCrLf & _ “ End Property” & vbCrLf & _ “ “ & vbCrLf & _ “ Public Property Let “ & lAttribute.Name & “(valueField)” & vbCrLf ‘& vbCrLf Set lProp = lAttribute Set lType = lProp.Type Select Case lType.itemType Case SOMITEM_DATATYPE_BYTE, SOMITEM_DATATYPE_DECIMAL, SOMITEM_DATATYPE_DOUBLE, _ SOMITEM_DATATYPE_FLOAT, SOMITEM_DATATYPE_INTEGER, SOMITEM_DATATYPE_LONG, SOMITEM_DATATYPE_SHORT: CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _ “ If Not IsNumeric(0 & valueField) Then “ & vbCrLf & _ “ Err.Raise 1970, , “”Invalid number!””” & vbCrLf & _ “ End If” & vbCrLf Case SOMITEM_DATATYPE_DATE, SOMITEM_DATATYPE_DATETIME: CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _ “ If Not IsDate(“””” & valueField) Then “ & vbCrLf & _ “ Err.Raise 2000, , “”Invalid date!””” & vbCrLf & _ “ End If” & vbCrLf End Select

Page 58: v2005 04 vbj64

58 VBJ N. 64 - Luglio/Agosto 2005

Infatti, trattandosi di normali istanze di inter-facce IDispatch per utilizzarle in Visual Basic è suffi ciente ottenere il puntatore all’istanza del-la classe stessa e assegnarlo ad un normale Object o Variant. Questo è possibile sfruttan-do il potente metodo Eval dello ScriptControl (e dello scripting più in generale) che permet-ta di valutare, facendosi ritornare il risultato in forma di variant, una qualsiasi espressione:

‘codice VBA

Dim oCRM As Object

Set oCRM = oSC.Eval (“New vCRM”)

A questo punto possiamo accedere a oMyOr-der direttamente da codice Visual Basic esat-tamente come abbiamo fatto all’interno di VB-Script:

‘codice VBA

oCRM.AUTHOR = “Vito Vessia”

Msgbox oCRM.AUTHOR

Il Class Generator

Il meccanismo appena mostrato è molto po-tente ma ha, nella forma in cui l’abbiamo mo-strato, un’utilità quasi nulla perché ci permette sì di scrivere le nostre classi di proxy intorno ad un qualsiasi XML con schema, ma ci costrin-ge comunque a scriverci manualmente, e per ogni differente schema, il codice di tali clas-si. Ci costringe inoltre, cosa ancora più inutile, a istanziare tali classi e a popolarle secondo i dati presenti nel fi le XML in questione.

A ben pensarci si tratterebbe di scrivere ogni volta sempre lo stesso codice perché gli sche-ma XML si descrivono sempre nelle stesse mo-dalità viste in precedenza e il popolamento del-le istanze è ancora più banale perché non è al-tro che un susseguirsi interminabile di for each

su nodi, sequence, ecc… Perché non affi dar-si quindi ad un generatore automatico di clas-si a partire da schema e di istanze delle classi create in precedenza a partire da fi le XML che soggiacciono allo stesso schema? Ebbene è quello che andremo a realizzare, seppur in una forma non esaustiva.

Il sorgente che accompagna l’articolo presenta il progetto Visual Basic di un ActiveX Dll (CPXML Proxies.vbp) e di un progetto di esempio che utilizza tale engine (CPXMLProxyDemo.vbp) sul fi le XML mostrato nell’articolo. Il motore è però generico e funziona su ogni XML con schema standard [3] e utilizza il nuovo SOM di MSXML 4 per produrre le classi a partire dagli schema e il DOM per produrre le istanze delle classi a partire dai nodi XML. Questo presen-ta solo due metodi pubblici. Il primo, Create-ClassesFromXMLSchema, permette di produr-re una gerarchia di classi VBScript a partire da uno schema XML:

‘codice VBA

Public Function CreateClassesFromXMLSchema

(XMLSchemaObject As Variant, _

Optional XMLSchemaRef As Variant) As String

Il primo parametro rappresenta proprio lo schema da cui produrre le classi.

È di tipo variant perché potrebbe essere sia il nome del fi le o la url dello schema e sia diret-tamente l’oggetto ISchema, ed infatti vengo-no fatti i controlli del caso (una IsObject e una QueryInterface) per comprenderne la natura.

Il secondo parametro è passato invece per referenza e viene usato come parametro di tipo out, cioè viene valorizzato in uscita; in pratica a questo viene assegnata l’istanza dello sche-ma appena prodotto.

Il valore di ritorno della funzione è invece pro-prio il codice sorgente delle classi.

A questo punto si procede scorrendosi la col-lection elements dello ISchema, ciascun ele-mento rappresenta un diverso nodo dei fi le XML sottoposti allo schema dato.

Vediamo un estratto della classe

Dim lSchemaItem As MSXML2.ISchemaItem

Dim lAttribute As MSXML2.ISchemaItem

Dim lSchema As MSXML2.ISchema

Dim lElem As MSXML2.ISchemaElement

Dim lComplex As MSXML2.ISchemaComplexType

Dim lProp As MSXML2.ISchemaAttribute

I MITI

Le classi che andremo a pro-

durre non perderanno nulla

delle caratteristiche dello

schema XML di cui vanno a

realizzare il wrapper

Page 59: v2005 04 vbj64

59N. 64 - Luglio/Agosto 2005 VBJ

Dim lType As MSXML2.ISchemaType

For Each lSchemaItem In lSchema.elements

CreateClassesFromXMLSchema =

CreateClassesFromXMLSchema & _

„Class „ & StrConv(lSchemaItem.Name,

vbProperCase) & vbCrLf

Set lElem = lSchemaItem

If lElem.itemType = SOMITEM_ELEMENT Then

Set lComplex = lElem.Type

CreateClassesFromXMLSchema =

CreateClassesFromXMLSchema & vbCrLf & _

A questo punto, per ogni classe che si va a creare, si procede con l’enumerazione della col-lection Attributes del Complex ottenuto effet-tuando il casting dell’oggetto nodo (come si può osservare nel codice appena mostrato).

Nel codice specifi co vengono prodotti i mem-bri privati della classe che conterranno i valori delle proprietà/attributi di ogni nodo.

For Each lAttribute In lComplex.Attributes

CreateClassesFromXMLSchema =

CreateClassesFromXMLSchema & vbCrLf & _

“ Private m_” & lAttribute.Name

Next

Stessa cosa, però sulla collection particles di contentModel del Complex.

Essa conterrà l’elenco eventuale dei nodi fi -gli del nodo corrente (ad es. il nodo vCRM ha come nodi fi glia Order e Customer).

For Each lAttribute In lComplex.contentModel.particles

CreateClassesFromXMLSchema =

CreateClassesFromXMLSchema & vbCrLf & _

“ Public “ & lAttribute.Name

Next

Un’altro interessante pezzo di codice da os-servare è quello che permette di gestire le con-straint a livello di property let di ogni attributo, cioè di gestire correttamente le assegnazioni di valori agli attributi in base alle caratteristi-che peculiari (tipo, lunghezza, ecc.) diretta-mente ereditate dalle informazioni di schema. Si tratta di un’implementazione molto parziale, ma rende l’idea della potenza del meccanismo che si va ad implementare: le classi che an-dremo a produrre non perderanno nulla delle caratteristiche dello schema XML di cui van-no a realizzare il wrapper (Listato 3).

L’altro metodo è CreateInstanceFromXML. Esso banalmente scorre il fi le XML passato in input (XMLIssue) Public Function CreateInstancesFromXML

(XMLIssue As Variant, Optional ParentArray As Variant, Op-

tional XMLSchemaRef As Variant, Optional SourceCode As String

= “”) As Object

Il parametro XMLIssue rappresenta il nome o il nodo XML che si vuole trasformare in un’istanza di una classe VBScript generata dallo schema a cui soggiace il nodo stesso.

Vale infatti lo stesso discorso di ambivalen-za visto per il parametro del metodo prece-dente.

Gli altri parametri, invece, sono apparente-mente poco signifi cativi ed infatti non verran-no mai adoperati (per questo sono opzionali) in modo diretto; servono invece all’interno del metodo stesso perché si tratta di un meto-do ricorsivo.

Infatti la logica del metodo è che, dal nodo XML appena passato (o dal nodo root del fi le XML se il parametro XMLIssue è di tipo nodo) viene popolato un’istanza.

Per ogni nodo fi glio del nodo corrente viene invocato il metodo nuovamente per popolare le istanze di questi nodi fi gli e così via.

I tre altri parametri servono proprio per co-municare informazioni alle altre istanze di ri-corsione del metodo in modo da velocizza-re alcune operazione e donare uno stato al metodo.

Dim lObject As Object

Dim lElement As MSXML2.IXMLDOMElement

Dim lDOM As MSXML2.DOMDocument40

Dim lXSDFile As String

Dim lAttribute As MSXML2.IXMLDOMAttribute

Dim lSchema As MSXML2.ISchema

Dim lSchemaItem As MSXML2.ISchemaItem

Dim lSchemaElement As MSXML2.ISchemaElement

Dim lSchemaComplex As MSXML2.ISchemaComplexType

I MITI

Questo meccanismo

si presta alla realizzazione

di generatori automatici

di codice

Page 60: v2005 04 vbj64

60 VBJ N. 64 - Luglio/Agosto 2005

I MITI

Dim lSchemaAttribute As MSXML2.ISchemaItem

Dim lNodeList As MSXML2.IXMLDOMNodeList

Dim lChildElement As MSXML2.IXMLDOMElement

If SourceCode = “” Then

‘ci troviamo al livello 0 della ricorsione

e il sorgente delle classi non è stato

‘ancora prodotto e quindi viene prodotto in

questa fase usando il metodo precedente

‘(codice omesso, per lo studio del codice di

questo metodo si rimanda ai sorgenti)

Else ‘è un livello di ricorsione >0 e quindi

il sorgente delle classi è stato passato

‘per referenza

Set lSchema = XMLSchemaRef

End If

‘nuova istanza della classe wrapper del nodo corrente

Set lObject = g_SC.Eval

(“new “ & lElement.nodeName)

Per ogni proprietà/attributo dell’istanza vie-ne invocata la property let per assegnare il va-lore preso dal nodo XML originale per quel-l’attributo.

La valorizzazione avviene per nome simboli-co della proprietà, grazie alla potente CallBy-Name di Visual Basic:

For Each lSchemaAttribute In lSchemaComplex.Attributes

CallByName lObject, lSchemaAttribute.Name,VbLet, _

lElement.Attributes.getNamedItem

(lSchemaAttribute.Name).nodeValue

lObject.Attributes.Add lSchemaAttribute.Name, _

lElement.Attributes.getNamedItem

(lSchemaAttribute.Name).nodeValue

Next

In questo brano di codice si può invece os-servare l’uso della ricorsione nel metodo: per ogni nodo fi glio di quello corrente viene invo-cato infatti lo stesso metodo.

For Each lSchemaAttribute In lSchemaComplex.

contentModel.particles

Set lNodeList = lElement.getElementsByTagName

(lSchemaAttribute.Name)

For Each lChildElement In lNodeList

CreateInstancesFromXML lChildElement,

CallByName(lObject, lChildElement.

baseName, VbGet), lSchema, SourceCode

Next

Next

Vediamo dunque il motore appena creato in azio-ne in un esempio molto semplice in Visual Basic:

‘codice VBA

Dim oProxy As Object

Dim oObject As Object

Set oProxy = CreateObject(“CPXMLProxies.XMLProxy”)

Set oObject = oProxy.CreateInstancesFromXML(“dati.xml”)

MsgBox oObject.Order(1).Row(2).Cover(1)

.CodQuest

Semplice e potente…

Conclusioni

Abbiamo visto come realizzare un sistema di classi VBScript di wrapper a partire da un fi le XML con schema, riciclando il concet-to di classe/istanza sull’equivalente XML di schema/fi le. Le istanze di queste classi pos-sono poi essere utilizzate a runtime dalle no-stre applicazioni Visual Basic che quindi ac-quistano la capacità di gestire i complicati fi le XML con schema senza conoscere nulla di questo formato, senza ricorrere al potente ma un po’ complicato object model di DOM e senza neppure mantenere una referenza a MSXML 4. Il tutto è stato generato facendo uso di un motore molto semplice scritto in Vi-sual Basic, che quindi ci mette in salvo dalla riscrittura di queste classi VBScript ogni vol-ta che cambia il formato dello schema e ci risparmia anche dal codice di popolamento delle istanze. Al di là della possibilità di map-pare classi IDispatch su XML, questo mec-canismo si presta alla realizzazione di gene-ratori automatici di codice, magari a partire da template XML che li descrivono o ad al-tre applicazioni simili o diverse che possono trarre giovamento da questo singolare utiliz-zo. Si accettano consigli a riguardo…

Bibliografi a

[1] Don Box, A. Skonnard, J. Lam, “Essential XML – Beyond markup”, Addison Wesley (2000)

[2] I tipi di dati di SOAP, http://www.w3.org/2001/XMLSchema

[3] XML Schema Part 0: Primer, http://www.w3.org/ TR/xmlschema-0/

[4] Vito Vessia, “Tecniche di « osmosi » nello scripting”, DEV n. 95

Page 61: v2005 04 vbj64

a cura di Davide [email protected]

.NET TO OLS

61N. 64 - Luglio/Agosto 2005 VBJ

Dopo più di due anni di esistenza la rubrica .NET Tools si concede un “strappo” alla regola, ospitando in questo numero una suite di prodotti che avrà un sicuro posto di rilievo nel prossimo futuro, e che, a differenza dei software recensiti normalmente, è a pagamento.

Ohibò! Perché questa decisione? Non c’è più software “free” disponibile?

Assolutamente no, anzi…ma, come ormai saprete, i tempi del rilascio del .NET Framework 2.0 e di Visual Studio 2005 (e di Sql Server 2005) sono sempre più vicini e, data l’importanza dell’evento, noi di VBJ non possiamo certo far fi nta di nulla!

È quindi importante farsi trovare pronti, magari andando a vedere cosa bolla in pentola sopratutto per quello che riguarda la programmazione free (o a bassissimo costo) di .NET 2.0.

La suite di prodotti “Express” è stata pensata proprio con questo target in mente: far avvicinare tutti (ma pro-prio tutti) gli sviluppatori a .NET, soprattutto quelli che non vogliono o non possono investire nell’intero Visual Studio, ma che non vogliono comunque rinunciare a fare il salto di qualità verso la piattaforma .NET 2.0.

Tralasciando però per un momento il discorso pura-mente professionale, la possibilità di avere piattaforme di sviluppo ottime ad un prezzo davvero basso, permette di far iniziare la formazione informatica sin dalle scuole medie, dando la possibilità ai ragazzi interessati di potersi avvicinare subito ad uno strumento reale di sviluppo, lasciando il buon GWBasic al suo posto, ovverosia… nell’armadio. A tal proposito ho recentemente avuto un’ottima esperienza a Milano, dove, insieme ad un collega ed a volenterosi professori e genitori abbiamo organizzato alcune giornate di formazione su Visual Basic 2005 per ragazzi delle scuole medie.

Il riscontro è stato molto positivo, soprattutto per i genitori degli appassionati fanciulli che possono così utilizzare Visual Studio senza doversi svenare.

Per due motivi:

1. Attualmente le versioni Express sono ancora in Beta (benché piuttosto stabile) e l’utilizzo ed il download sono completamente gratuiti.

2. Una volta giunto alla versione fi nale, il costo di ogni singolo prodotto sarà di ben (!) 49$!

Da questo ovviamente va escluso Sql Server Express che invece è – e rimarrà – completamente gratuito!

A questo punto nessuno ha più scuse; dal punto di vista professionale, chi ancora non ha provato .NET lo potrà fare senza spendere nulla, mentre dal punto di vista didattico, chi vorrà, potrà essere sicuro di imparare (od insegnare) ad utilizzare un prodotto che è in tutto e per tutto simile a quello che, magari, un giorno, si userà per lavorare.

Tenendo conto che nell’età scolastica (ed includo an-che il periodo universitario) chi sviluppa lo fa solamente per passione e per divertimento, cosa ci può essere di meglio che prepararsi al mondo professionale unendo l’utile al dilettevole?

Visual Studio Express Edition Products

A differenza dei fratelli maggiori (da Visual Studio stan-dard in su) che permettono di scrivere codice indifferen-temente in C#, VB.NET, J# e C++, e che permettono di sviluppare applicazioni Windows e Web, la suite Express è strutturata in modo da diversifi care gli ambienti di sviluppo a seconda del target e del linguaggio.

Per lo sviluppo di applicazioni Windows, a seconda del linguaggio che desideriamo utilizzare, possiamo scegliere tra:

� Visual Basic 2005 Express� Visual C# 2005 Express� Visual J# 2005 Express� Visual C++ 2005 Express

Se invece vogliamo sviluppare siti web, è suffi ciente utilizzare un solo prodotto, che permette di scegliere se sviluppare in VB.NET, C# o J#:

� Visual Web Developer Express

Tutti gli ambienti di sviluppo mettono a disposizione praticamente lo stesso set di funzionalità; set davvero molto ampio, tanto da farne prodotti quasi professionali. Per quanto riguarda la scrittura del codice troviamo le ormai indispensabili funzionalità di syntax highlighting e di Intellisense (anche conosciuto come auto-completion), che permettono di scrivere velocemente e possibilmente senza errori il codice.

Davide Mauri è un consulente freelance che si occupa di SQL Server 2000 e di sviluppo di soluzioni Web basate sul .NET Framework. All’attività di consulenza affianca una co-spicua attività di docenza e di formazione presso Mondadori Informatica. Il suo sito personale è www.davidemauri.it.

Page 62: v2005 04 vbj64

62

.NET TOOLS

VBJ N. 64 - Luglio/Agosto 2005

Tra le novità si trova l’implementazione del refactoring “Rename Method” (esteso di fatto ad ogni oggetto) che semplifi ca di molto la vita quando si vuole rinominare un oggetto.

Come si vede dalla Figura 1 (che mostra l’ambiente in tutta la sua bellezza), quando si rinomina un elemento, Visual Studio Express se ne accorge e ci chiede se vogliamo aggiornare tutti i riferimenti all’oggetto con il nuovo nome. Molto comodo!

Un altro nuovo strumento che aiuta a semplifi care ulteriormente la scrittura del codice è rappresentato dai Code Snippets.

Premendo la combinazione di tasti Ctrl+K e di seguito Ctrl+X appare un menù a tendina che ci chiede che cosa vogliamo fare.

Dobbiamo aggiungere un proprietà alla nostra classe? Nessun problema, scelta la voce adatta il codice verrà generato in automatico e noi dovremo solo preoccuparci di personalizzarlo!

Per quanto concerne l’esecuzione del codice, tutti gli ambienti permettono di fare del debugging offrendo praticamente tutte le funzionalità delle versioni più co-stose, fondamentali per poter sviluppare un prodotto di buona qualità, senza dover impazzire nel cercare

anche gli errori più semplici. Dall’ambiente di sviluppo è possibile anche gestire database (non necessariamente Sql Server) e nel caso della versione Web Developer navigare nel sito che si sta sviluppando usufruendo del web server integrato.

A differenza infatti del predecessore Web Matrix, questo ambiente di sviluppo è completamente autosuffi ciente e non necessita dell’installazione di Internet Information Server o di Cassini sul proprio computer.

L’installazione del prodotto è molto semplice: una volta scaricato l’eseguibile dal sito lo si lancia e lo si lascia lavorare. Penserà lui a scaricare tutto il necessario (.NET Framework 2.0 compreso) per far funzionare tutto correttamente.

Prima di concludere, una nota riguardante la docu-mentazione e gli esempi forniti.

La documentazione è – come da standard Microsoft – piuttosto ricca; se questo però non bastasse le ricerche fatte all’interno della stessa possono essere automati-camente estese al web, andando a cercare documenti ed esempi di codice nelle community registrate su Co-deZone e su MSDN online, per essere sicuri di avere sempre informazioni recenti. Per quanto riguarda esem-pi e tutorial, in ogni versione Express vengono forniti

Fi gu ra 1 Visual Web Developer

Page 63: v2005 04 vbj64

63

.NET TOOLS

N. 64 - Luglio/Agosto 2005 VBJ

Starter Kit a tema: per quanto riguarda la versione Web Developer lo starter kit fornito è quello per la creazione di un sito personale (Figura 2), mentre invece per la versione Visual Basic 2005 ne vengono forniti due: uno per la gestione della propria videoteca (Figura 3) ed uno per creazione di uno screen-saver.

Anche in questo caso è comunque possibile scaricarne di nuovi, accedendo alla voce “Download Additional Starter Kit” direttamente dalla schermata iniziale che viene presentata ad ogni avvio dell’ambiente di svi-luppo.

È da sottolineare che gli starter kit sono comunque rivolti ad utenti non proprio alle prime armi (per questi ultimi sono presenti dei Walkthrough nell’help) e, proprio per questo, c’è da fare un piccolo appunto alla qualità del codice fornito, che in diversi casi (ad esempio per la gestione della connessione ad un database che – come ormai sanno anche i sassi – deve essere aperta il più tardi possibile e chiusa il prima possibile) è ben lontano dalla sufficienza.

Occhio quindi a non prendere come oro colato quello che viene mostrato negli Starter Kit, cercando di utilizzarli come trampolino di lancio per entrare nel mondo .NET, non come punto di arrivo e di riferimento.

Fi gu ra 2 Il sito fornito come tutorial

ProdottoVisual Studio Express Edition Products

Url di riferimento http://lab.msdn.microsoft.com/express/

Sta to ReleaseBETA 2

Semplicità d’uso �����Semplicissimo, soprattutto grazie agli esempi ed ai tu-torial forniti.

Utilità �����Estrema! È come dare pennelli, colori e tela bianca ad un pittore… Cosa volere di più?

Qualità prodotto ����� Ottima.

Qualità documentazione �����−Il meno è dovuto al fatto che i tutorial in rari casi (ma purtroppo ci sono) non mostrano un esempio di buona programmazione… Per iniziare possono andare bene, ma se fossero stati fatti in modo migliore ne avremmo guadagnato tutti.

Page 64: v2005 04 vbj64

64

.NET TOOLS

VBJ N. 64 - Luglio/Agosto 2005

SQL Server 2005 Express

La versione Express di Sql 2005 prenderà il posto dell’attuale MSDN, senza però soffrire delle limitazioni circa il numero di connessioni contemporanee suppor-tate, dando quindi molto più spazio ad un uso non solamente personale ma anche per piccole/medie esigenze.

Oltre a questa grossa ed attesa novità, un’al-tra importante aggiunta è quella – finalmente – di uno strumento di amministrazione e di sviluppo, che prende il nome di Sql Server Express Manager.

La versione Express supporta tutte le novità introdotte da Sql Server 2005 senza escluderne alcuna.

È pertanto possibile sfruttare le potenzialità fornite da XML, Web Services, Service Broker, .NET, le nuove estensio-ni al linguaggio T-SQL e, interessantissimo, lo sviluppo di report trami-te i Reporting Services. Tutto ciò “limitato” ad un confi gurazione massima che supporta:

� 1 Processore� 1 GB di RAM� 4 GB per Database

che è una limitazione in senso lato in quan-to, tenendo presente il target del prodotto, ossia l’uso personale o per piccole/medio-piccole soluzioni, non dovrebbe mai andare stretta a nessuno.

Un’altra tipologia di utilizzo della versione Express è quella a sup-porto di una versione maggiore, in modo poter disporre di un supporto locale per tutte quelle esigenze in

cui l’applicazione deve funzionare senza la presenza di una connettività di rete, garantendo comunque un’autonomia di funzionamento.

Il tutto, come detto in precedenza, è supportato dalla presenza del Sql Server Express Manager, che mira

Fi gu ra 4 Sql Server 2005 Express Manager

Fi gu ra 3 L’applicazione “videoteca” fornita come tutorial

Page 65: v2005 04 vbj64

65

.NET TOOLS

N. 64 - Luglio/Agosto 2005 VBJ

a fornire una console evoluta per l’accesso ai dati e l’amministrazione degli stessi. Attualmente le funzioni fornite dell’interfaccia grafica sono molto limitate, e quindi ci si troverà a scriversi da soli il codice T-SQL per fare qualsiasi cosa: dal backup al restore, dalla creazione di login e user, all’amministrazione dei per-messi degli stessi.

Questo sicuramente comporta un buon esercizio per aumentare la propria familiarità con T-SQL (cosa ap-prezzabile e consigliabile sempre a tutti), ma dal punto di vista puramente “operativo” un po’ di funzionalità in più per aumentare la produttività ci sarebbero state sicuramente bene. Pazienza, vorrà dire che sopperi-remo a tale mancanza creandoci e salvandoci script e funzioni personalizzate ☺.

La documentazione non è ancora fornita con la versio-ne in circolazione (essendo praticamente una Beta), ma è possibile scaricarla successivamente a questo indirizzo: http://go.microsoft.com/fwlink/?LinkId=31046.

Se siete alle prime armi con Sql Server, prima di lanciarvi a provare il prodotto ricordatevi che la ver-sione Express viene installata come Named Instance e pertanto per potersi collegare è necessario specificare il seguente indirizzo del server: <nomemacchina>\SQLEXPRESS.

ProdottoSql Server 2005 Express

Url di riferimento http://lab.msdn.microsoft.com/express/

Sta to ReleaseCTP June (IDW 159

Semplicità d’uso �����Il Sql Server Express Manager è un po’ carente nelle funzioni, in quanto attraverso l’interfaccia sono disponili solo quelle più comuni (e neanche tutte!)

Utilità �����È uno dei migliori database in circolazione. C’è qualcosa di più utile di un database, visto che al suo interno ci andranno tutti i dati che vogliamo memorizzare?

Qualità prodotto ����� Sql Server Express 2005 è funzionalmente identico alla versione commerciale del prodotto, che, come detto sopra è un dei migliori in circolazione.

Qualità documentazione �����Più che buona.

Page 66: v2005 04 vbj64
Page 67: v2005 04 vbj64
Page 68: v2005 04 vbj64