Sei sulla pagina 1di 68

I N O F F E R TA V B J 6 7

Scrivi a
A First Look at SQL Ser- Applicare UML e i pattern
ver 2005 for Developers book@infomedia.it Analisi e progettazione
di B. Beauchemin et al. specificando orientata agli oggetti
3a Ed.
nell’oggetto della di C. Larman

Addison Wesley
e-mail: Addison Wesley Italia
ISBN 0321180593
736 pp - 46,95 €
IN OFFERTA ISBN 8871922700
768 pp - 43,00 €
VBJ n. 67
OPPURE
inviaci il coupon
Microsoft SQL Server 2005 Using UML:Software
Changing the Paradigm sottostante Engineering with Objects
(SQL Server 2005 Public al numero di fax and Compone, Edition 2
Beta Edition) di P. Stevens
0587/732232
Sams Potrai acquistare Addison Wesley
ISBN 0672327783 ISBN 0321269675
504 pp - 34,70 €
i libri qui riportati con 272 pp - 57,95 €
uno
SCONTO
ADO.NET and
ECCEZIONALE Cryptography in the
System.Xml v. 2.0-The Database
Beta Version, Edition 2 del 10% anche se The Last Line of Defense
di A. Homer et al. acquisti solo un di K. Kenan

libro
Addison Wesley Prentice Hall
ISBN 0321247124 OPPURE ISBN 0321320735
560 pp - 39,95 € 312 pp - 39,95 €
del 20% se acquisti
3 libri

VBJ 67
online.infomedia.it
n. 67 - gennaio/febbraio 2006
bimestrale - anno dodicesimo

Direttore Responsabile
Marialetizia Mari (mmari@infomedia.it)
Direttore Esecutivo
ED I T O R I A L E
Francesco Balena (fbalena@infomedia.it)
Managing Editor
Renzo Boni (rboni@infomedia.it)
Guerre informatiche
Collaboratori
Stefano Corti
Raffaele Di Natale
di religione
Andrea Ferendeles

U
Fabio Perrone na delle cose più belle della comunità degli sviluppatori
Scott Swigart
Lorenzo Vandoni nel mondo è di essere animata da passioni ferventi, come
Pietro Vite quelle che ti fanno passare la notte in bianco fino a quando
il problema non è stato definitivamente risolto. È bello vedere tante
persone di ogni età affrontare il proprio lavoro con l’entusiasmo
di teenager, o quasi.
D’altra parte, una delle cose più discutibili della comunità degli
sviluppatori è di essere animata da passioni troppo ferventi. Quando
cominciai a smanettare sui computer, il mondo si divideva tra gli
appassionati del Sinclair Spectrum e quelli del Commodore C64,
ciascuno dei quali poteva passare ore a discettare sulle virtù del proprio computer e sui
Direzione difetti del computer avversario. La disputa si spostò presto sul confronto Amiga-Atari-
Natale Fino (nfino@infomedia.it) Apple, e dopo ancora IBM PC vs Macintosh. Quest’ultima è probabilmente la querelle di
Marketing & Advertising più lunga durata, visto che è continuata immutata fino ad oggi, anche se ora la materia
Segreteria: 0587/736460 del contendere si è spostata sul sistema operativo.
marketing@infomedia.it Nel campo del software la discussione è stata persino più accesa. Prima erano i dialetti
Amministrazione del Basic, poi la contrapposizione C vs Pascal, poi C e C++, Visual Basic e Delphi, SQL
Sara Mattei Server e Oracle, Windows e Linux, e più recentemente le piattaforme .NET e Java. Se
(amministrazione@infomedia.it) volete avere dei dati che supportino le vostre convinzioni, fatevi un giro in Internet e
Grafica troverete sicuramente dei benchmark che vi garantiscono che il linguaggio-sistema
Manola Greco (mgreco@infomedia.it) operativo-database che preferite è assolutamente migliore di quello che invece detestate,
Technical Book con buona pace di tutti.
Lisa Vanni (book@infomedia.it) Forse è l’eta, forse il fatto di avere la sensazione di aver già visto questo film altre volte,
Segreteria ma non riesco più ad appassionarmi a queste battaglie. O comunque, cerco di non per-
Enrica Nassi dere mai di vista il fatto che, molto spesso, dietro le community di persone appassionate
(info@infomedia.it) e in buona fede, che dedicano davvero il loro tempo a curare blog e forum, si muovono
interessi commerciali enormi. In questo piccolo spazio a mia disposizione, posso solo
darvi un paio di consigli.
Stampa
Quando leggete i risultati di un case-study o unasurvey che mette a confronto due tec-
TIPOLITOGRAFIA PETRUZZI
Citta’ di Castello (PG) nologie, per prima cosa andate a vedere chi l’ha fatta e soprattutto chi l’ha commissio-
nata. A seconda di come si selezionano i criteri di valutazione e i benchmark, è possibile
Ufficio Abbonamenti dimostrare tutto e il contrario di tutto. Ad esempio, non è difficile provare che in alcune
Tel. 0587/736460 - Fax 0587/732232 configurazioni Access sia molto più veloce di SQL Server o Oracle, oppure che VB4 sia più
e-mail: abbonamenti@infomedia.it performante di C# o Java. Controllate sempre i dettagli di questo tipo prima di prendere
www.infomedia.it
le conclusioni come buone.
Gruppo Editoriale Infomedia srl E quando vi imbattete in dichiarazioni di IBM, Sun o Oracle che promuovono Java e Linux
Via Valdera P., 116 - 56038 Ponsacco (PI) Italia come espressioni del “software libero” contro lo strapotere Microsoft, andatevi a rileggere
Tel. 0587/736460 - Fax 0587/732232
red_vbj@infomedia.it le cronache degli anni ‘70, quando IBM aveva il monopolio assoluto in questo settore.
Sito Web www.infomedia.it Ovviamente non sto dicendo che non si ha il diritto di criticare Microsoft, ma solo che
sarebbe bene non farsi coinvolgere più di tanto nelle guerre tra i colossi del software.
A meno che ovviamente, non abbiate investito tutti i vostri risparmi nelle azioni di uno o
Manoscritti e foto originali anche se non pubblicati, dell’altro. Ma quello è un problema vostro...
non si restituiscono. È vietata la riproduzione
anche parziale di testi e immagini.
Francesco Balena
Si prega di inviare i comunicati stampa e gli inviti stampa per fbalena@codearchitects.com
la redazione all’indirizzo: comunicatistampa@infomedia.it
Visual Basic Journal è una rivista di
Gruppo Editoriale Infomedia S.r.l. Via Valdera P, 116 Ponsacco - Pisa.
Registrazione presso il Tribunale di Pisa n. 20/1999
N. 67 - Gennaio/Febbraio 2006 VBJ 5
SOMMARIO
GENNAIO/FEBBRAIO

N.67 Editoriale
RUBRICHE

5
.NET Tools 61

SPECIALE

Tdo - Typed Data Object 2.0 10


Tdo – Typed Data Object (oggetto dati tipizzato) è un insieme costituito da una libreria di classi base ed
un generatore di codice .NET 2.0 per database MS Sql Server 2005/2000/7 ed il .NET Framework 2.0.
di Andrea Ferendeles

TECNICHE
Multithreading semplice e robusto in applicazioni
Windows Forms 1.1 e 2.0 34
Chi sviluppa in .NET 2.0 può scrivere applicazioni Windows Forms multithreaded in modo semplice grazie al nuovo
componente BackgroundWorker. In questo articolo vedremo come implementare un identico controllo in .NET 1.1 e
come estenderlo per renderlo persino più potente del componente fornito con Visual Studio 2005.
di Francesco Balena

VB6
File compatti e illeggibili - Crittografia(prima puntata) 40
Crittografia e compressione di file in VB6 utilizzando il Microsoft .NET Framework.
di Scott Swigart, Swigart Consulting LLC.

SOFTWARE ENGINEERING
Executable UML 44
UML (xUML) è un formalismo che consente di applicare concretamente le linee guida fornite dalla Model Driven
Architecture (MDA).
di Lorenzo Vandoni

WEB
DotNetNuke: creazione di un modulo 50
DotNetNuke è un portale open source sviluppato in tecnologia Microsoft .Net. In questo articolo ne vedremo le
caratteristiche principali e le soluzioni adottate per costruire con esso un portale intranet.
di Pietro Vite
APPLICAZIONI
Controllo Remoto in Visual Basic .NET (quarta puntata) 55
Il pattern Adapter suggerisce come modificare una classe server in modo da adattarsi all’interfaccia richiesta da altre classi client.
di Stefano Corti

N. 67 - Gennaio/Febbraio 2006 VBJ 9


TDO 2.0

Tdo - Typed Data Object 2.0


Tdo – Typed Data Object (oggetto dati tipizzato) è un insieme costi-
tuito da una libreria di classi base ed un generatore di codice .NET 2.0
per database MS Sql Server 2005/2000/7 ed il .NET Framework 2.0.

TDO
di Andrea Ferendeles

G
ià nel numero 62 abbiamo avuto modo di Più avanti vedremo che tutte
parlare della prima versione di Tdo. Oggi le operazioni possibili che pos-
questo strumento si presenta rinnovato ed siamo eseguire sul nostro data-
in linea con le novità introdotte dal .NET framework base (select, insert, update, de-
2.0 ma soprattutto in versione open source (http:// lete, stored procedure, ecc…)
tdo.sourceforge.net). partiranno proprio dall’invoca-
zione di metodi presenti all’in-
Che cos’è Tdo terno degli oggetti della classe
Tdo è l’acronimo di Typed Data Object ovvero Og- TdoHelper.
getto Dati Tipizzato e si compone di un assembly
.NET (Tdo.dll) e di un generatore di codice sorgen- Tdo è Open Source
te (TdoCodeGenerator.exe). Tdo, a partire da questa ver-
Il generatore di codice produce un insieme di sione (2.0) è divenuto un pro-
classi derivate (VB.NET / C#.NET ) che estendo- getto open source e sia i sor-
no le classi base presenti all’interno dell’assem- genti che il pacchetto di in-
bly Tdo.dll, il tutto secondo un rigoroso modello stallazione sono scaricabili da
Object Oriented. sourceforge.net all’indirizzo
Ogni classe generata ha lo scopo di “rappresenta- web: http://tdo.sourceforge.net.
re” un oggetto del nostro database SQL, dove per Il pacchetto di installazione
oggetti si intendono Tabelle, Viste, Stored Proce- installa sia la libreria di base
dure, Funzioni. Proprio secondo questo modello, Tdo.dll che il generatore di co-
ogni tabella, vista, ecc… viene considerata un og- dice TdoCodeGenerator.exe.
getto che espone a sua volta attributi ed operazio- Per poterlo utilizzare è
ni (proprietà e metodi). consigliabile disporre di MS
Ciò significa che il singolo campo di una tabella Visual Studio .NET 2005 e Sql
o il singolo parametro di una stored procedure sa- Server 2005 (o 2000 o 7.0).
ranno a loro volta oggetti. Tutte queste classi sono
racchiuse gerarchicamente in un unico macro-og- Generazione del codice
getto a rappresentazione dell’intero database – la sorgente e del DataSet
classe TdoHelper. tipizzato
La prima operazione da com-
Andrea Ferendeles ha iniziato ad appassionarsi di informati- piere è sicuramente quella di in-
ca dai tempi del VIC20. Ha sviluppato in BASIC, Logo, Turbo stallare Tdo e quindi di genera-
Pascal, C, C++, Visual Basic, fino ad arrivare ai linguaggi
re il codice sorgente necessario
.NET, quali VB.NET e C#. È certificato MCP, MCAD, MSF,
MCSD (.NET), MCDBA, MCT. Può essere contattato via email: (occorre aver precedentemente
aferendeles@infomedia.it. installato il .NET Framework

10 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

basso a sinistra ave-


te a disposizione un
menu di navigazio-
ne tra i vari oggetti
in stile MS Outlook
(Tables, Views, Sto-
red Procedures, Ta-
ble-valued Func-
tions, Scalar-valued
Functions), nel-
la parte centrale in
alto, l’elenco degli
oggetti disponibili
nel database e nella
parte in basso l’elen-
co degli oggetti sele-
zionati per la gene-
razione.
Ora è sufficiente
Figura 1 Lanciamo TdoCodeGenerator salvare la Tdo Solu-
tion ed il Tdo Project
appena creati e clic-
2.0). Una volta installato, possiamo lanciare care sul pulsante Ge-
TdoCodeGenerator – Figura 1 – e dal riqua- nerate – Figura 3. Si aprirà dunque la fine-
dro dei progetti in alto a sinistra, aggiungere stra di generazione del codice sorgente che
un nuovo Tdo Project alla soluzione di default mostrerà lo stato di avanzamento dell’opera-
Tdo Solution 1 – Figura 2. zione – Figura 4 - e se il database è integro,
All’aggiunta di un nuovo progetto vi verrà cioè ad esempio non ci sono viste che pun-
chiesto di immettere le informazioni di con- tano a tabelle inesistenti, il codice generato
nessione al database Sql tra cui il nome del verrà scritto correttamente nella cartella di
progetto Tdo, il linguaggio in cui il codice output specificata.
sorgente dovrà essere generato, la modalità Nell’esempio mostrato in figura si è scelto
di selezione degli oggetti dal database (tutti di voler generare in un unico file C#.NET
o selezione custom), la directory in cui i sor- tutte le tabelle, viste, stored procedure e fun-
genti verranno scritti ed infine se creare un zioni del database Northwind. Il file è stato
file per ogni classe generata oppure un unico generato nella root del disco C: e si chiama
file sorgente contenente tutte le classi. TdoNorthwind.cs. Inoltre sulla macchina su
Questa stessa finestra vi chiederà inoltre un cui stiamo effettuando le prove (un pentium
namespace .NET in cui dichiarare le classi ed IV 3.40 GHz con 2 Gb di RAM) la generazione
un namespace web root da utilizzarsi per tut- del codice ha richiesto 0,6 sec. per un totale
te le operazioni di serializzazione XML (web di 420Kb di codice generato e 8.835 linee di
service che restituiscono oggetti Tdo). codice, quindi come vedete tempi molto esi-
Una volta creato il progetto è possibile sele- gui di generazione (nemmeno un secondo).
zionare per quali tabelle, viste, stored proce- Nella stessa cartella troverete lo schema
dure, funzioni, Tdo dovrà generare una rela- XML del DataSet tipizzato dell’intero data-
tiva classe wrapper, se avete scelto la modali- base Northwind TdoNorthwindDataSet.xsd
tà di Selection only, altrimenti tutto sarà au- con all’interno tabelle, viste, stored proce-
tomaticamente incluso nella generazione. In dure e relazioni.

N. 67 - Gennaio/Febbraio 2006 VBJ 11


TDO 2.0

Tips & Tricks: progetto Tdo in modo da far sovrascrive-


• Spesso durante la fase di sviluppo di un re di volta in volta il sorgente che Visual
software e maggiormente nelle fasi ini- Studio .NET andrà a compilare (VS.NET
ziali, il database subirà spesso modifiche si accorgerà che il file è stato modificato
che richiederanno una nuova generazione dall’esterno e vi chiederà di ricaricarlo).
del codice Tdo (ad esempio viene aggiunta
una colonna ad una tabella o viene creata Compilazione del codice generato
una nuova vista) . Il consiglio è quello di Una volta generato il codice andremo a crea-
aggiungere il file di progetto Tdo (quel- re un progetto .NET (C# o VB) di tipo Class
lo appena salvato e con estensione .tdo- Library che farà da Data Layer per le nostre
prj) direttamente alla soluzione di Visual prove sul database Northwind e lo chiame-
Studio .NET in modo da poterlo rigene- remo appunto TdoNorthwind. A questo pro-
rare on demand direttamente dall’IDE di getto dovremo aggiungere il file sorgen-
sviluppo facendovi doppio click (associa- te appena generato TdoNorthwind.cs, eli-
te, solo la prima volta, l’estensione .tdoprj minando prima il file Class1.cs automatica-
all’eseguibile TdoCodeGenerator.exe) mente aggiunto da VS.NET, ed infine il file
• Dopo aver creato il progetto in Visual TdoNorthwindDataSet.xsd. Proprio su que-
Studio .NET è bene inoltre cambiare la st’ultimo faremo doppio click per far generare
Output folder tra le proprietà del nostro a VS.NET il codice di wrapping del dataset ti-
pizzato. Come dicevamo prima
il codice che Tdo genera non è
nient’altro che un insieme di
classi derivate e tipizzate che
estendono un set di classi base
presenti all’interno dell’assem-
bly Tdo.dll, quindi un ulteriore
passo da compiere sarà quel-
lo di aggiungere il riferimento
a quest’assembly che dovreste
poter localizzare direttamen-
te nella finestra References
– .NET quando aggiungiamo
il riferimento. In caso contra-
rio troveremo l’assembly nel-
la directory di installazione di
default di Tdo: C:\programmi\
Tdo – Typed Data Object. Det-
to ciò compiliamo ed il gioco
è fatto. Ora siamo pronti a re-
ferenziare l’assembly appena
compilato TdoNorthwind.dll
nel nostro strato Presentation
(una applicazione ASP.NET
o Windows Smart Client).

Figura 2 Aggiunta di un nuovo Tdo Project


Tdo Object Model
Se andiamo a curiosare nel
codice sorgente che Tdo ha ge-

12 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

nerato per noi, ci accorgeremo che la struttura proprietà di tipo Tdo.Common.TdoTypes.Td


è estremamente semplice ed intuitiva. Inizia- oTypeBase. Questo tipo base ha il compito di
mo col dire che esisterà una classe per ogni formalizzare tutte le proprietà della colonna
tabella, vista, stored procedure e funzione. (tipo, dimensione, nullabilità, ecc…) e ovvia-
Prendiamo in esame ad esempio la tabella mente il valore del campo attraverso la pro-
Employees che nel codice generato corrispon- prietà Value , proprietà che poi verrà specia-
de alla classe EmployeesTable. lizzata a seconda dell’effettivo tipo (int, char,
La Figura 5 mostra il diagramma delle clas- binary, ecc…) grazie ai generics di .NET 2.0.–
si e la gerarchia degli oggetti alla quale ap- Figura 12.
partiene la classe:
Tips & Tricks:
System.Object Tdo.Common.TdoEntityBase • Tutte le classi generate sono estendibili
 Tdo.Common.Tables.TdoTableBase  tramite ereditarietà, sono CLS compliant,
Tdo.Northwind.Entities.EmployeesTable, serializzabili e marcate con il prefisso
dove la classe Tdo.Common.TdoEntityBase im- partial. Questo ci consente all’occorren-
plementa l’interfaccia Tdo.Common.ITdoEntity za di poter “completare” le classi gene-
e la classe Tdo.Common.Tables.TdoTableBas rate da Tdo con metodi/proprietà/eventi
e implementa l’interfaccia Tdo.Common.Tabl custom in file .cs/.vb diversi da quelli ge-
es.ITdoTable. nerati da Tdo (se rigenerassimo il codi-
In Figura 6 e 7 rispettivamente e analoga- ce sorgente, a fronte di cambiamenti nel-
mente i diagrammi delle classi della vista Al- lo schema del db, perderemmo le nostre
phabetical List Of Products e della stored pro- implementazioni).
cedure Cust Order Hist.
La classe TdoHelper
Tutte queste classi sono riesposte come pro- Facciamo ora alcuni esempi concreti di uti-
prietà della classe TdoHelper … la classe che lizzo della classe TdoHelper generata per il
rappresenta l’intero database. database Northwind.
In Figura 8 il suo diagramma ed in Figu- Prendiamo il database Northwind, presen-
ra 9 l’organizzazione dei namespace dell’as- te ormai quasi per tradizione anche nella
sembly Tdo.dll. versione Express di SQL 2005 e supponia-
mo di aver generato tramite TdoCodeGene-
Ora che abbiamo fatto le presentazioni for- rator tutte le classi necessarie per trattare
mali vediamo in concreto come utilizzare la l’intero database Northwind.
classe TdoHelper. Abbiamo detto che tutte le operazioni par-
Come si vede dalla Figura 8 tutte le proprie- tiranno dalla classe TdoHelper e allora co-
tà della classe TdoHelper che espongono ta- minciamo proprio col creare un’istanza di
belle iniziano per “t” (table). questa classe.
Allo stesso modo proprietà che espongono
viste, stored procedure e funzioni avranno ri- Listato 1:
spettivamente “v”, “p” ed “f” come prefisso. C#
Questo semplice meccanismo permette du- //Automatic connection string building
rante la stesura del codice, di rintracciare su- TdoHelper tdo = new TdoHelper(“(local)”, “Northwind”,
bito, grazie all’intellisense di VS.NET, gli og- “sqlusername”, “sqlpassword”);
getti sui quali vogliamo operare – Figura 10
e 11. Inoltre ogni singola classe che espone VB.NET
una tabella del db, (ma lo stesso discorso vale ‘Automatic connection string building
per viste, stored procedure e funzioni) ha tut- Dim tdo As New TdoHelper(“(local)”, “Northwind”,
ti i campi della tabella stessa esposti come “sqlusername”, “sqlpassword”)

N. 67 - Gennaio/Febbraio 2006 VBJ 13


TDO 2.0

Le uniche informazioni da fornire al costrut- false, //enlist


tore della classe sono i parametri di connes- 50, //max pool size
sione al database. 1, //min pool size
In questo caso stiamo dicendo a Tdo di vo- true, //pooling
ler utilizzare l’istanza di default di Sql Server true, //asynchronous processing
presente sulla nostra macchina e di voler ac- “PartnerSqlServerName”); //sql backup server name
cedere al database Northwind tramite sql-au-
thentication. La classe TdoHelper è fornita di VB.NET
ben 11 costruttori per consentirci di specifi- ‘default constructor - assign connection string properties
care tutte le possibili modalità di connessio- Dim tdo1 As New TdoHelper()
ne tra cui: Windows-authentication, asynchro- tdo1.ConnectionString = “data source=(local);Initial
nous processing, connection pooling, failover Catalog=Northwind;Intgrated Security=SSPI”
partner, ecc….
Ecco, nel Listato 2, alcuni altri modi per spe- ‘Windows Authentication
cificare le informazioni di connessione: Dim tdo2 As New TdoHelper(“(local)”, “Northwind”)

Listato 2: ‘Windows Authentication with asynchronous processing enabled.


C# Dim tdo2async As New TdoHelper(“(local)”, “Northwind”, True)
//default constructor - assign connection string properties
‘Sql Authentication
TdoHelper tdo1 = new TdoHelper(); Dim tdo3 As New TdoHelper(“(local)”, “Northwind”,
tdo1.ConnectionString = “data source=(local);Initial “sqlusername”, “sqlpassword”)
Catalog=Northwind;Integrated Security=SSPI”;
‘Sql Authentication with asynchronous processing enabled.
//Windows Authentication Dim tdo3async As New TdoHelper(“(local)”, “Northwind”,
TdoHelper tdo2 = new TdoHelper(“(local)”, “Northwind”); “sqlusername”, “sqlpassword”, True)

//Windows Authentication with asynchronous processing ‘Several parameters


enabled. Dim tdo4 As New TdoHelper(“SqlServerName”, “DataBaseName”,
TdoHelper tdo2async = new TdoHelper(“(local)”, “sqlusername”, “sqlpassword”,
“Northwind”,true); 30, True, False, 50, 1, True, True, “PartnerSql-
ServerName”)
//Sql Authentication
TdoHelper tdo3 = new TdoHelper(“(local)”, “Northwind”, La classe TdoHelper è fornita di una serie
“sqlusername”, “sqlpassword”); di proprietà che ne governano il comporta-
mento durante la fase di run-time, come ad
//Sql Authentication with asynchronous processing enabled. esempio la capacità di aprire e chiudere au-
TdoHelper tdo3async = new TdoHelper(“(local)”, tomaticamente la connessione al database
“Northwind”, “sqlusername”, “sqlpassword”,true); oppure far sì che tutte le operazioni CRUD
siano automaticamente racchiuse in una
//Several parameters transazione SQL.
TdoHelper tdo4 = new TdoHelper(
“SqlServerName”, //sql server name Listato 3:
“DataBaseName”, //database name C#
“sqlusername”, //sql username //Automatic connection open before any operation
“sqlpassword”, //sql password //... and close automatically after execution
30, //connection life time tdo.AutomaticOpenCloseConnection = true; //default is true
true, //connection reset

14 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

Figura 3 Si comanda la generazione del codice sorgente

//Automatic BEGIN TRANSACTION on every CRUD operation Operazioni di SELECT


(and SPs) Vediamo ora come con Tdo sia possibile
// ... and commit after execution eseguire operazioni di SELECT con una
tdo.AutomaticTransaction = false; semplice riga di codice. Ci sono diversi
metodi per eseguire tale operazione in base
//Command timeout al tipo di result-set desiderato; iniziamo con
tdo.CommandTimeOut = 60; //default is 60 il metodo SelectDataTable che restituisce
appunto un oggetto System.Data.DataTable
VB.NET di ADO.NET 2.0 (in commento l’equivalente
‘Automatic connection open before any operation statement T-SQL).
‘... and close automatically after execution
tdo.AutomaticOpenCloseConnection = True ‘default is true Listato 4:
C#
‘Automatic BEGIN TRANSACTION on every CRUD operation //SELECT * FROM dbo.Employees
(and SPs) DataTable dtEmployees = tdo.tEmployees.SelectDataTable();
‘ ... and commit after execution
tdo.AutomaticTransaction = False VB.NET
‘SELECT * FROM dbo.Employees
‘Command timeout Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
tdo.CommandTimeOut = 60 ‘default is 60 Table()

N. 67 - Gennaio/Febbraio 2006 VBJ 15


TDO 2.0

Come osserviamo, il me-


todo SelectDataTable, uti-
lizzato senza parametri in
input, equivale ad una se-
lect * from Employees.

Ovviamente è possibile,
con lo stesso metodo, inve-
ce specificare quali campi
includere nel result-set.

Listato 5:
C#
//SELECT EmployeeId, LastName,
FirstName FROM dbo.Employees
Figura 4 Stato di avanzamento della generazione

DataTable dtEmployees =
tdo.tEmployees.SelectDataTable
( VB.NET
tdo.tEmployees.Employeeid, ‘SELECT EmployeeId, LastName, FirstName
tdo.tEmployees.Lastname, ‘ FROM(dbo.Employees)
tdo.tEmployees.Firstname ‘ WHERE LastName=’Davolio’ AND FirstName=’Nancy’
); Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table( _
VB.NET Clause.Where(tdo.tEmployees.Lastname = “Davolio” And
‘SELECT EmployeeId, LastName, FirstName FROM dbo.Employees tdo.tEmployees.Firstname = “Nancy”), _
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData tdo.tEmployees.Employeeid, _
Table( _ tdo.tEmployees.Lastname, _
tdo.tEmployees.Employeeid, _ tdo.tEmployees.Firstname)
tdo.tEmployees.Lastname, _
tdo.tEmployees.Firstname) … il tutto ancora con una sola riga di codice.

… ed eventuali clausole Where/Order By/ Tdo Sql Expression DOM


Group by … Vediamo alcune espressioni più complesse e
come sia possibile scriverle facendo uso del-
Listato 6: l’overloading degli operatori introdotto dal mo-
C# dello Tdo Sql Expression DOM (Tdo.Common.
//SELECT EmployeeId, LastName, FirstName TdoSqlExpressionDOM).
// FROM dbo.Employees
// WHERE LastName=’Davolio’ AND FirstName=’Nancy’ Listato 7:
DataTable dtEmployees = tdo.tEmployees.SelectDataTable C#
( //SELECT EmployeeId, LastName, FirstName
Clause.Where(tdo.tEmployees.Lastname==”Davolio” & // FROM dbo.Employees
tdo.tEmployees.Firstname==”Nancy”), // WHERE EmployeeId>=5
tdo.tEmployees.Employeeid, // OR
tdo.tEmployees.Lastname, // (TitleOfCourtesy LIKE ‘Ms.’ AND LastName<>’Davolio’)
tdo.tEmployees.Firstname // AND
); // (REGION IS NOT NULL)
DataTable dtEmployees = tdo.tEmployees.SelectDataTable

16 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

( DataTable dtEmployees = tdo.tEmployees.SelectDataTable


Clause.Where (
( Clause.Where
tdo.tEmployees.Employeeid>=5 (
| tdo.tEmployees.Titleofcourtesy==new TdoWhere-
(tdo.tEmployees.Titleofcourtesy % “Ms.” setOfValues(“Mrs.”,”Mr.”,”Ms.”)
& tdo.tEmployees.Lastname!=”Davolio”) )
& );
(tdo.tEmployees.Region!=DBNull.Value)
), VB.NET
tdo.tEmployees.Employeeid, ‘SELECT *
tdo.tEmployees.Lastname, ‘ FROM dbo.Employees
tdo.tEmployees.Firstname ‘ WHERE Titleofcourtesy IN (‘Mrs.’,’Mr.’, ‘Ms.’)
); Dim dtEmployees As DataTable = tdo.tEmployees.SelectData-
Table( _
VB.NET Clause.Where _
‘SELECT EmployeeId, LastName, FirstName ( _
‘ FROM dbo.Employees tdo.tEmployees.Titleofcourtesy = New TdoWhere-
‘ WHERE EmployeeId>=5 setOfValues(“Mrs.”, “Mr.”, “Ms.”) _
‘ OR ) _
‘ (TitleOfCourtesy LIKE ‘Ms.’ AND LastName<>’Davolio’) )
‘ AND
‘ (REGION IS NOT NULL) Listato 9:
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData C#
Table( _ //SELECT *
Clause.Where _ // FROM dbo.Employees
( _ // WHERE (BirthDate BETWEEN ‘1950-01-01’ AND ‘1960-12-31’)
tdo.tEmployees.Employeeid >= 5 _ DataTable dtEmployees = tdo.tEmployees.SelectDataTable
Or _ (
(tdo.tEmployees.Titleofcourtesy Mod “Ms.” And Clause.Where
tdo.tEmployees.Lastname <> “Davolio”) _ (
And _ tdo.tEmployees.Birthdate==new TdoWhereRange
(tdo.tEmployees.Region <> DBNull.Value) _ (
), _ new DateTime(1950,1,1),new DateTi-
tdo.tEmployees.Employeeid, _ me(1960,12,31)
tdo.tEmployees.Lastname, _ )
tdo.tEmployees.Firstname) )
);
Nei Listati 8 e 9 vediamo l’utilizzo delle clas-
si TdoWheresetofValues e TdoWhereRange per VB.NET
costruire espressioni SQL basate sugli opera- ‘SELECT *
tori T-SQL IN e BETWEEN. ‘ FROM dbo.Employees
‘ WHERE (BirthDate BETWEEN ‘1950-01-01’ AND ‘1960-12-31’)
Listato 8: Dim dtEmployees As DataTable = tdo.tEmployees.SelectDataTable( _
C# Clause.Where _
//SELECT * ( _
// FROM dbo.Employees tdo.tEmployees.Birthdate = New TdoWhereRange _
// WHERE Titleofcourtesy IN (‘Mrs.’,’Mr.’, ‘Ms.’) ( _

N. 67 - Gennaio/Febbraio 2006 VBJ 17


TDO 2.0

Figura 5 Diagramma delle classi e gerarchia degli oggetti alla quale appartiene la classe EmployeesTable

New DateTime(1950, 1, 1), New DateTi- // ORDER BY TitleOfCourtesy DESC, LastName, FirstName
me(1960, 12, 31) _ DataTable dtEmployees = tdo.tEmployees.SelectDataTable
) _ (
) _ Clause.OrderBy
) (
tdo.tEmployees.Titleofcourtesy,
Un esempio di Order by e Group By nei Li- OrderByOperator.Desc,
stati 10 e 11: tdo.tEmployees.Lastname, OrderByOperator.Asc,
tdo.tEmployees.Firstname, OrderByOperator.Asc
Listato 10: )
C# );
//SELECT *
// FROM dbo.Employees VB.NET

18 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

‘SELECT * con vari overload analogamente al metodo Se-


‘ FROM dbo.Employees lectDataTable.
‘ ORDER BY TitleOfCourtesy DESC, LastName, FirstName
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData Listato 12:
Table( _ C#
Clause.OrderBy _ //SELECT LastName, FirstName FROM Employees
( _ // WHERE LastName = ‘Davolio’
tdo.tEmployees.Titleofcourtesy, SqlDataReader dataReader = tdo.tEmployees.SelectDataReader(
OrderByOperator.Asc, _ Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
tdo.tEmployees.Lastname, OrderByOperator.Asc, _ CommandBehavior.SingleRow,
tdo.tEmployees.Firstname, OrderByOperator.Asc _ tdo.tEmployees.Lastname, tdo.tEmployees.Firstname);
) _
) dataReader.Close(); //must be closed before another
select operation
Listato 11:
C# //SELECT Birthdate FROM Employees
//SELECT CategoryID AS [Category ID], COUNT(ProductID) AS // WHERE LastName = ‘Davolio’
Total DateTime birthDate = (DateTime)tdo.tEmployees.SelectScalar(
// FROM Products Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
// GROUP BY CategoryID tdo.tEmployees.Birthdate);
DataTable dtProductsCount = tdo.tProducts.SelectDataTable
( //SELECT EmployeeId FROM Employees
Clause.GroupBy(tdo.tProducts.Categoryid), // WHERE LastName = ‘Davolio’
tdo.tProducts.Categoryid.Alias(“Category ID”), int davolioEmpId = tdo.tEmployees.SelectIdentity(
Functions.Count<TdoInt32>(tdo.tProducts. Clause.Where(tdo.tEmployees.Lastname ==
Productid,”Total”) “Davolio”)).Value;
);
//SELECT LastName, FirstName FROM Employees
VB.NET // WHERE LastName = ‘Davolio’ FOR XML AUTO, ELEMENTS
‘SELECT CategoryID AS [Category ID], COUNT(ProductID) AS XmlReader xmlEmployees = tdo.tEmployees.SelectXmlReader(
Total Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
‘ FROM Products “FOR XML AUTO, ELEMENTS”,
‘ GROUP BY CategoryID tdo.tEmployees.Lastname, tdo.tEmployees.Firstname);
Dim dtProductsCount As DataTable = tdo.tProducts.
SelectDataTable _ VB.NET
( _ ‘SELECT LastName, FirstName FROM Employees
Clause.GroupBy(tdo.tProducts.Categoryid), _ ‘ WHERE LastName = ‘Davolio’
tdo.tProducts.Categoryid.Alias(“Category ID”), _ Dim dataReader As SqlDataReader = tdo.tEmployees.
Functions.Count(Of TdoInt32)(tdo.tProducts. SelectDataReader( _
Productid, “Total”) _ Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _
) CommandBehavior.SingleRow, _
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname)
Oltre al metodo SelectDataTable esistono al-
tri metodi fra cui SelectDataReader, SelectSca- dataReader.Close() ‘must be closed before another
lar, SelectIdentity, SelectXmlReader che re- select operation
stituiscono rispettivamente oggetti di tipo
System.Data.SqlDataReader, System.Object, ‘SELECT Birthdate FROM Employees
System.Int32, System.Xml.XmlReader, tutti ‘ WHERE LastName = ‘Davolio’

N. 67 - Gennaio/Febbraio 2006 VBJ 19


TDO 2.0

Dim birthDate As DateTime = DirectCast(tdo.tEmployees. TdoString, TdoDateTime, TdoBinary. All’in-


SelectScalar( _ terno di ogni tipo, oltre alle varie informazioni
Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _ che caratterizzano il tipo del campo (AllowDB-
tdo.tEmployees.Birthdate), DateTime) Null, AutoIncrement, MaxLenght, ecc…), tro-
viamo la proprietà Value; per i tipi suddetti la
‘SELECT EmployeeId FROM Employees proprietà sarà rispettivamente di tipo SqlInt32,
‘ WHERE LastName = ‘Davolio’ SqlString, SqlDateTime, SqlBinary ovvero Tdo
Dim davolioEmpId As Integer = tdo.tEmployees.SelectIdentity( _ fa uso delle strutture presenti nel namespa-
Clause.Where(tdo.tEmployees.Lastname = “Davolio”)).Value ce System.Data.SqlTypes di ADO.NET 2.0 per
esporre i valori di ciascun campo. Infine non
‘SELECT LastName, FirstName FROM Employees dimentichiamo che la classe Tdo.Northwind.
‘ WHERE LastName = ‘Davolio’ FOR XML AUTO, ELEMENTS Entities.EmployeesTable viene esposta come
Dim xmlEmployees As XmlReader = tdo.tEmployees. proprietà della classe TdoHelper con il nome
SelectXmlReader( _ tEmployees (prefisso “t” in quanto è una tabel-
Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _ la). Fatte queste considerazioni vediamo come
“FOR XML AUTO, ELEMENTS”, _ eseguire operazioni di inserimento, modifica e
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname) cancellazione dalla tabella employees nei Li-
stati 13, 14, 15 e 16.
Operazioni di INSERT, UPDATE, DELE- Nel Listato 15 c’è un esempio di transazio-
TE e supporto alle Transazioni ne con Tdo. Tutti gli oggetti racchiusi nella
classe TdoHelper infatti condividono la stes-
Le operazioni di INSERT, UPDATE e DELE- sa connessione al database e partecipano au-
TE su tabelle sono altrettanto semplici e poco tomaticamente alla stessa transazione (se ri-
costose in termini di righe di codice necessa- chiesta).
rio. Prendiamo in considerazione la tabella Em- Per eseguire una serie di comandi in transa-
ployees e la relativa classe Tdo Tdo.Northwind. zione (è possibile specificare il livello di isola-
Entities.EmployeesTable. mento) è sufficiente racchiudere tutte le ope-
Al suo interno troviamo mappati tutti i cam- razioni Tdo tra due chiamate ai metodi Tdo
pi della tabella tra cui Employeeid, Lastname, Helper.BeginTransaction e TdoHelper.Com-
Birthdate, Photo, ecc…, riesposti con altrettan- mitTransaction (o TdoHelper.RollBackTran-
ti tipi Tdo e rispettivamente come TdoInt32, saction).

Riquadro 1 Elenco degli operatori .NET in overloading per espressioni T-SQL secondo il modello Tdo Sql
Expression DOM

T-SQL C#.NET VB.NET

= == =
<> != <>
> > >
>= >= >=
< < <
<= <= <=
NOT ! NOT
AND & And
OR | Or
LIKE % Mod
NOT LIKE - -

20 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

Figura 6 Diagramma delle classi della vista Alphabetical List Of Products

Listato 15: //---------------------


C# //exec sp_executesql N’update dbo.Employees
// SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED // SET LastName=@LastName,FirstName=@FirstName
// BEGIN TRANSACTION // WHERE (Employees.LastName = @_p14705386011) ‘,
// UPDATE Employees // N’@_p14705386011 nvarchar(10),@LastName nvarchar
// SET Lastname = ‘Ferendeles mod’, Firstname = ‘Andrea mod’ (14), @FirstName nvarchar(10)’,
// WHERE Lastname = ‘Ferendeles’ // @_p14705386011 = N’Ferendeles’,
// COMMIT TRANSACTION // @LastName = N’Ferendeles mod’, @FirstName = N’Andrea mod’
tdo.OpenConnection();
tdo.BeginTransaction(IsolationLevel.ReadUncommitted); VB.NET
tdo.tEmployees.Lastname.Value=”Ferendeles mod”; ‘ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
tdo.tEmployees.Firstname.Value = “Andrea mod”; ‘ BEGIN TRANSACTION
int affectedRecords = ‘ UPDATE Employees
tdo.tEmployees.Update(Clause.Where(tdo.tEmployees. ‘ SET Lastname = ‘Ferendeles mod’, Firstname = ‘Andrea mod’
Lastname == “Ferendeles”)); ‘ WHERE Lastname = ‘Ferendeles’
tdo.CommitTransaction(); ‘ COMMIT TRANSACTION
tdo.CloseConnection(); tdo.OpenConnection()
tdo.BeginTransaction(IsolationLevel.ReadUncommitted)
//[SQL Profiler Trace]: tdo.tEmployees.Lastname.Value = “Ferendeles mod”

N. 67 - Gennaio/Febbraio 2006 VBJ 21


TDO 2.0

Figura 7 Diagramma delle classi della stored procedure Cust Order Hist

tdo.tEmployees.Firstname.Value = “Andrea mod” ‘ @_p14705386011 = N’Ferendeles’,


Dim affectedRecords As Integer = _ ‘ @LastName = N’Ferendeles mod’, @FirstName = N’Andrea mod’
tdo.tEmployees.Update(Clause.Where(tdo.tEmployees.
Lastname = “Ferendeles”)) Listato 16:
tdo.CommitTransaction() C#
tdo.CloseConnection() //DELETE FROM Employees
// WHERE Lastname LIKE ‘%Ferendeles%’
‘[SQL Profiler Trace]: int affectedRecords =
‘--------------------- tdo.tEmployees.Delete(Clause.Where(tdo.tEmployees.
‘exec sp_executesql N’update dbo.Employees Lastname % “%Ferendeles%”));
‘ SET LastName=@LastName,FirstName=@FirstName
‘ WHERE (Employees.LastName = @_p14705386011) ‘, //[SQL Profiler Trace]:
‘ N’@_p14705386011 nvarchar(10),@LastName nvarchar //---------------------
(14), @FirstName nvarchar(10)’, //exec sp_executesql N’delete from dbo.Employees

22 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

// WHERE (Employees.LastName LIKE @_p631522191) ‘, Listato 19:


N’@_p631522191 nvarchar(12)’, C#
// @_p631522191 = N’%Ferendeles%’ this.tdo.pCustorderhist.pCustomerid = “ALFKI”;
SqlParameterCollection outputParameters;
VB.NET SqlDataReader custReader = his.tdo.pCustorderhist.Execute-
‘DELETE FROM Employees Reader(CommandBehavior.SingleResult, out outputParame-
‘ WHERE Lastname LIKE ‘%Ferendeles%’ ters);
Dim affectedRecords As Integer = _ //do something with custReader
tdo.tEmployees.Delete(Clause.Where(tdo.tEmployees.Last-
name Mod “%Ferendeles%”)) custReader.Close(); //close before retrieve output para-
meters
‘[SQL Profiler Trace]:
‘--------------------- this.tdo.pCustorderhist.AssignOutputParameterValues
‘exec sp_executesql N’delete from dbo.Employees (outputParameters);
‘ WHERE (Employees.LastName LIKE @_p631522191) ‘, //object somevalue = this.tdo.pCustorderhist.pSomeOutput-
N’@_p631522191 nvarchar(12)’, Parameter.Value;
‘ @_p631522191 = N’%Ferendeles%’
VB.NET
Comandi Asincroni MyClass.tdo.pCustorderhist.pCustomerid = “ALFKI”
ADO.NET 2.0 è stato dotato della possibili- Dim outputParameters As SqlParameterCollection
tà di eseguire comandi in modalità asincro- Dim custReader As SqlDataReader =
na, in particolar modo sfruttando le coppie di MyClass.tdo.pCustorderhist.ExecuteReader
metodi BeginXXX/EndXXX dell’oggetto Sql- (CommandBehavior.SingleResult, outputParameters)
Command. ‘do something with custReader
Allo stesso modo Tdo fornisce con equiva-
lente sintassi coppie di metodi BeginXXX/ custReader.Close() ‘close before retrieve output parameters
EndXXX per le tutte le operazioni finora vi- MyClass.tdo.pCustorderhist.AssignOutputParameterValues
ste. Vediamo nel Listato 17 un esempio di (outputParameters)
esecuzione asincrona per una operazione di ‘object somevalue = this.tdo.pCustorderhist.pSomeOutput-
INSERT. Parameter.Value

Stored Procedure e User-Function Nel Listato 20 la stessa stored procedure in-


Analogamente ad operazioni CRUD, le Stored vocata in modo asincrono più un esempio di
Procedure sono rappresentate da Tdo tramite come la classe TdoHelper possa essere uti-
classi derivate dalla classe base Tdo.Common. lizzata in un costrutto using (TdoHelper è
Programmability.TdoStoredProcedureBase ed System.IDisposable).
esposte come proprietà dalla classe TdoHel- Purtroppo nel database Northwind non è pre-
per. Prendiamo in esame la stored procedure sente nessuna user-function, pertanto invito
CustOrderHist, definita con un parametro in tutti a creare la vostra user-function, a gene-
ingresso @CustomerID di tipo nchar(5) e nes- rare il codice sorgente con TdoCodeGenera-
sun parametro di output. Nel Listato 18 la sto- tor e ad effettuare le dovute prove.
red procedure viene invocata passando diretta-
mente il parametro CustomerId al metodo Fill- Listato 20:
DataTable in una sola riga di codice. C#
Nel Listato 19 invece la stessa invocazione ma using (TdoHelper tdo = new TdoHelper(“(local)”,
assegnando prima, singolarmente i valori di cia- “Northwind”, true))
scun parametro e successivamente ricavando gli {
eventuali valori dei parametri di output. tdo.pCustorderhist.AssignParameterValues(“ALFKI”);

N. 67 - Gennaio/Febbraio 2006 VBJ 23


TDO 2.0

Figura 8 Diagramma di TdoHelper

//assign all in one row EndExecuteReader(asyncResult);


SqlParameterCollection outputParameters; custReader.Read();
IAsyncResult asyncResult = tdo.pCustorderhist. //do something with results
BeginExecuteReader( custReader.Close(); //close reader before retrieve
null, null, out outputParameters); output parameters
//do something else tdo.pCustorderhist.AssignOutputParameterValues
SqlDataReader custReader = tdo.pCustorderhist. (outputParameters);

24 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

int retValue = tdo.pCustorderhist.pReturnValue.Value; mento della DataTable Orders del DataSet


} tipizzato NorthwindDataSet e nel Listato 22
vediamo come sia possibile aggiornare con
VB.NET Tdo una riga già modificata nel dataste; in-
Using tdo As New TdoHelper(“(local)”, “Northwind”, True) fine nel Listato 23 come inserire nel dataset
tdo.pCustorderhist.AssignParameterValues(“ALFKI”) una riga già inserita da Tdo nel database.
‘assign all in one row
Dim outputParameters As SqlParameterCollection = Nothing
Dim asyncResult As IAsyncResult = tdo.pCustorderhist.
Tdo è l’acronimo di
BeginExecuteReader( _
Nothing, Nothing, outputParameters) Typed Data Object
‘do something else ovvero
Dim custReader As SqlDataReader = tdo.pCustorderhist.
EndExecuteReader(asyncResult)
Oggetto Dati Tipizzato
custReader.Read()
‘do something with results
custReader.Close() ‘close reader before retrieve TdoHelper … Select Join,
output parameters SqlDependecy, SqlNotificationRequest,
tdo.pCustorderhist.AssignOutputParameterValues Properties & Events
(outputParameters) La classe TdoHelper è fornita di una serie
Dim retValue As Integer = tdo.pCustorderhist.pReturn di metodi per operazioni generiche o che non
Value.Value riguardano nello specifico un solo database
End Using object (una sola tabella/vista/SP/funzione).
Consideriamo una operazione di INNER
Tdo e DataSet tipizzati JOIN tra due tabelle: la tabella Orders e
Il generatore di codice TdoCodeGene- la tabella Order Details. Secondo la logica
rator, oltre a generare tutto il codice sor- Object Oriented questa è un'operazione al
gente necessario alla trattazione dell’inte- livello di database e dunque esposta attra-
ro database, produce un DataSet tipizza- verso un metodo della classe TdoHelper. La
to (System.Data.DataSet – Figura 13) di filosofia di Tdo è quella di spingere lo svi-
ADO.NET 2.0, per ad esempio operazioni luppatore ed il DBA ad operare più possibile
di data-binding, operazioni in modalità di- tramite Viste e Stored Procedure per ragioni
sconnessa o xml web service. Prima di po- di performance e manutenibilità ormai arci-
ter utilizzare il dataset è necessario fare dop- note (Tdo recita: “crea gli oggetti nel db e
pio click (o tasto destro del mouse – run cu- rigenera il codice al volo”); detto ciò, nella
stom tool) sul file .xsd dall’interno di Visual classe TdoHelper troviamo comunque tutto
Studio .NET. il supporto per operazioni di Join on-the-fly
Tale operazione fa si che l’IDE generi cor- in quanto non è sempre possibile interve-
rettamente il code-behind (classi tipizzate) nire, per diverse ragioni, sul database. Nel
a partire dallo schema XSD del dataset. Da Listato 24 un esempio di INNER JOIN con
questo momento in poi il DataSet sarà visibi- il metodo TdoHelper.SelectJoin tra le due
le come componente dalle applicazioni con- tabelle sopra citate.
sumatrici e trascinabile sul designer.
Tdo ovviamente fornisce tutto il supporto Listato 24:
necessario alla trattazione dei DataSet tipiz- C#
zati fornendo metodi per il caricamento e per DataTable dtOrder_OrderDetails = tdo.SelectJoin(
la manipolazione sino al livello di singola Da- new JoinHelper(Join.InnerJoin, tdo.tOrders.Orderid,
taRow. Nel Listato 21 un esempio di carica- JoinOperator.Equal, tdo.tOrderDetails.Orderid),

N. 67 - Gennaio/Febbraio 2006 VBJ 25


TDO 2.0

‘ Orders.OrderDate as [OrderDate] ,[Order


Details].ProductID as [ProductID] ,
‘ [Order Details].Quantity as [Quantity] ,[Order
Details].UnitPrice as [UnitPrice]
‘ from Orders INNER JOIN dbo.[Order Details] ON
Orders.OrderID = [Order Details].OrderID
‘ WHERE (Orders.CustomerID = @_p10513825641) ‘, N’@_
p10513825641 nvarchar(5)’, @_p10513825641 = N’ALFKI’

La classe Tdo.Common.TdoSqlExpression
DOM.JoinInfo è dotata di ben 5 costruttori
Figura 9 Organizzazione di namespace dell'assembly
Tdo.dll per ammettere operazioni di JOIN che
coinvolgano al più 6 tabelle/viste. Altri metodi
interessanti della classe TdoHelper sono:
Clause.Where(tdo.tOrders.Customerid == “ALFKI”),
tdo.tOrders.Orderid, tdo.tOrders.Customerid, • CreateCommand per creare rapidamente og-
tdo.tOrders.Orderdate, gettiSqlCommand,conlapossibilitàdipassare
tdo.tOrderDetails.Productid, tdo.tOrderDetails. un parametro di tipo SqlNotificationRequest
Quantity, tdo.tOrderDetails.Unitprice); (rif.: http://msdn.microsoft.com/library/
d e f a u l t . a s p ? u r l = / l i b r a r y / e n - u s/
//[SQL Profiler Trace]: dnvs05/html/querynotification.asp).
//--------------------- Nel Listato 25 un esempio di utilizzo del
//exec sp_executesql N’select Orders.OrderID as [OrderID] , metodo CreateCommand con l’oggetto Sys
Orders.CustomerID as [CustomerID] , tem.Data.Sql.SqlDependency per SQL Ser-
// Orders.OrderDate as [OrderDate] ,[Order ver 2005.
Details].ProductID as [ProductID] , • CreateDataAdapter per creare rapidamen-
// [Order Details].Quantity as [Quantity] ,[Order te oggetti SqlDataAdapter.
Details].UnitPrice as [UnitPrice] • ExecuteNonQuery, ExecuteReader, Execu-
// from Orders INNER JOIN dbo.[Order Details] ON teScalar, ExecuteXmlReader.
Orders.OrderID = [Order Details].OrderID • Fill, FillSchema.
// WHERE (Orders.CustomerID = @_p10513825641) ‘, N’@_
p10513825641 nvarchar(5)’, @_p10513825641 = N’ALFKI’ Listato 25:
C#
VB.NET public void example_025()
Dim dtOrder_OrderDetails As DataTable = tdo.SelectJoin( _ {
New JoinHelper(Join.InnerJoin, this.tdo = new TdoHelper(@”.\SQL2005”,”Northwind”);
tdo.tOrders.Orderid, JoinOperator.Equal, SqlDependency.Start(tdo.Connection.ConnectionString);
tdo.tOrderDetails.Orderid), _ SqlCommand empCmd = tdo.CreateCommand(
Clause.Where(tdo.tOrders.Customerid = “ALFKI”), _ String.Format(“select * from {0}.{1}”,tdo.tEmploy-
tdo.tOrders.Orderid, tdo.tOrders.Customerid, ees.SchemaName ,tdo.tEmployees.EntityName),
tdo.tOrders.Orderdate, _ CommandType.Text);
tdo.tOrderDetails.Productid, tdo.tOrderDetails. SqlDependency dep = new SqlDependency(empCmd);
Quantity, tdo.tOrderDetails.Unitprice) dep.OnChange += new OnChangeEventHandler(dep_OnChange);
tdo.OpenConnection();
‘[SQL Profiler Trace]: SqlDataReader dr = empCmd.ExecuteReader();
‘--------------------- //do something with dr
‘exec sp_executesql N’select Orders.OrderID as [OrderID] dr.Close();
,Orders.CustomerID as [CustomerID] , }

26 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

tutti i nomi delle colonne di tutte le tabel-


public void dep_OnChange(object sender, SqlNotification- le del db.
EventArgs e)
{ Listato 26:
System.Diagnostics.Debug.WriteLine( C#
String.Format(@”Source: {0}\r\nInfo: {1}, Type: foreach (Tdo.Common.Entities.
{2}”,e.Source,e.Info,e.Type)); Tables.ITdoTable table in
} tdo.TdoTables)
VB.NET {
Dim WithEvents dep As SqlDependency System.Diagnostics.Debug.WriteLine(String.Format
(“Table: {0}”,table.EntityName));
Public Sub example_025() foreach (Tdo.Common.TdoTypes.ITdoColumn column in
MyClass.tdo = New TdoHelper(“.\SQL2005”, “Northwind”) table.TdoColumns)
SqlDependency.Start(tdo.Connection.ConnectionString) {
Dim empCmd As SqlCommand = tdo.CreateCommand( _ System.Diagnostics.Debug.WriteLine(String.Format
String.Format(“select * from {0}.{1}”, (“\tColumn: {0}”,column.ColumnName));
tdo.tEmployees.SchemaName, tdo.tEmployees.EntityName), _ }
CommandType.Text) }
dep = New SqlDependency(empCmd) //Output Window sample:
tdo.OpenConnection() //---------------------
Dim dr As SqlDataReader = empCmd.ExecuteReader() //Table: [Order Details]
‘do something with dr // Column: OrderID
dr.Close() // Column: ProductID
End Sub // Column: UnitPrice
// Column: Quantity
Private Sub dep_OnChange(ByVal sender As Object, ByVal e // Column: Discount
As System.Data.SqlClient.SqlNotificationEventArgs) _ //Table: Categories
Handles dep.OnChange // Column: CategoryID
System.Diagnostics.Debug.WriteLine( _ // Column: CategoryName
String.Format(“Source: {0}, Info: {1}\r\n, Type: // Column: Description
{2}”, e.Source, e.Info, e.Type))
End Sub

Ecco alcune altre pro-


prietà interessanti per
“navigare” nelle collection
degli oggetti del database
in fase di run-time:

• TdoTables
• TdoViews
• TdoStoredProcedures
• TdoTableFunctions

Nel Listato 26 un esem-


pio che fa uso della proprie- Figura 10 Il prefisso permette di rintracciare facilmente gli oggetti
tà TdoHelper.TdoTables
per ottenere a run-time

N. 67 - Gennaio/Febbraio 2006 VBJ 27


TDO 2.0

Figura 11 Anche l'Intellisense aiuta nel lavoro

// Column: Picture che nelle specifiche classi db-objects; nel Lista-


// ... to 27 un esempio di come utilizzare tali eventi
per realizzare un componente custom di logging
VB.NET per tutte le operazioni svolte tramite Tdo:
For Each table As Tdo.Common.Entities.Tables.ITdoTable In
tdo.TdoTables Listato 27:
System.Diagnostics.Debug.WriteLine(String.Format C#
(“Table: {0}”, table.EntityName)) public void example_028()
For Each column As Tdo.Common.TdoTypes.ITdoColumn In {
table.TdoColumns tdo.StatementCompleted += new StatementCompletedEvent-
System.Diagnostics.Debug.WriteLine(String.Format Handler(tdo_StatementCompleted);
(“ Column: {0}”, column.ColumnName)) DataTable dtEmployees = tdo.tEmployees.SelectDataTable();
Next //tdo_StatementCompleted raised
Next }
‘Output Window sample:
‘--------------------- public void tdo_StatementCompleted(object sender,
‘Table: [Order Details] StatementCompletedEventArgs e)
‘ Column: OrderID {
‘ Column: ProductID SqlCommand cmd = (SqlCommand)sender;
‘ Column: UnitPrice int rc = e.RecordCount;
‘ Column: Quantity // Write Log information somewhere
‘ Column: Discount System.Diagnostics.Debug.WriteLine(String.Format
‘Table: Categories (“Text: {0} - Type: {1} - RecordCount: {2}”,
‘ Column: CategoryID cmd.CommandText, cmd.CommandType, rc));
‘ Column: CategoryName }
‘ Column: Description
‘ Column: Picture VB.NET
‘ ... Public Sub example_028()
Dim dtEmployees As DataTable = tdo.tEmployees.
Diamo un rapido sguardo agli eventi - Tabella SelectDataTable()
1 - che Tdo supporta sia nella classe TdoHelper ‘tdo_StatementCompleted raised

28 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

Figura 12 Ogni classe che espone una tabella ha tutti i campi della tabella stessa esposti come proprietà

N. 67 - Gennaio/Febbraio 2006 VBJ 29


TDO 2.0

Figura 13 Il generatore di codice TdoCodeGenerator produce un Dataset tipizzato di ADO.NET 2.0

End Sub • tutte le classi sono CLS-Compliant (in al-


cuni casi viene fatta eccezione per il Da-
Private Sub tdo_StatementCompleted(ByVal sender As taSet tipizzato);
Object, ByVal e As System.Data.StatementCompletedEventArgs) • tutto è tipizzato – a fronte di cambiamen-
Handles tdo.StatementCompleted ti nel database, per esempio si modifica il
Dim cmd As SqlCommand = DirectCast(sender, SqlCommand) nome o il tipo di un campo di una tabella,
Dim rc As Integer = e.RecordCount il nuovo codice Tdo generato, segnala even-
‘ Write Log information somewhere tuali anomalie in fase di compile-time;
System.Diagnostics.Debug.WriteLine(String.Format • tutti gli statement T-SQL che Tdo genera a
(“Text: {0} - Type: {1} - RecordCount: {2}”, run-time sono parametrici (riuso dell’execu-
cmd.CommandText, cmd.CommandType, rc)) tion plan di Sql, no sql-injection, ecc…);
End Sub • un solo linguaggio (C# o VB.NET) per
scrivere statement T-SQL e quindi clau-
Conclusioni sole where ad esempio scritte in .NET (Tdo
Tdo è dotato di altre caratteristiche che sa- Sql Expression DOM);
rebbe impossibile, per ragioni di spazio, citare • documentazione Xml: tutto il codice ge-
tutte insieme; ecco allora un breve elenco di nerato da Tdo è già documentato trami-
altre importanti feature offerte da Tdo: te .NET Xml Documentation ed il codice
è organizzato in #region, dandoci così un
• le classi Tdo ed il codice generato da Tdo- maggior senso di “pulizia”;
CodeGenerator sono serializzabili sia in • performance: Tdo è più veloce del 45% ri-
formato binario che XML; spetto a equivalenti operazioni compiute

30 VBJ N. 67 - Gennaio/Febbraio 2006


TDO 2.0

con l’oggetto SqlDataAdapter e del 7% ri- stituirsi ad ADO.NET ma anzi ne costituisce


spetto a statement non parametrici ese- un complemento ed esso stesso ne fa uso,
guiti tramite l’oggetto SqlCommand di sfruttando però al massimo tutte le carat-
ADO.NET 2.0; teristiche offerte da MS Sql Server che in
• può essere utilizzato per operazioni di data- ADO.NET, per ragioni di “generalità” del co-
bindings; dice, non sono state implementate (almeno
• è interoperabile con COM/COM+; per il nostro specifico database).
• nel namespace Tdo.Common.Programma-
bility troviamo la classe Functions che ri-
espone la maggior parte delle funzioni pre-
senti in Sql Server (Count, Max, ecc…);
Tdo, a partire da questa
tali funzioni possono essere utilizzate in versione (2.0)
espressioni Tdo Sql Expression DOM.
è divenuto un progetto
• nel namespace Tdo.Common esiste una
classe Utility per scrivere su disco ad open source
esempio un intero oggetto Tdo in for-
mato Xml ed eventualmente rileggerne
il contenuto in un secondo momento (un La caratteristica più importante e signifi-
po’ come i metodi DataSet.WriteXml e cativa di Tdo risiede proprio nella tipizza-
DataSet.ReadXml). zione delle classi (Typed data object) rispet-
to al “nostro” database e non ad un DB ge-
Arrivati a conclusione di quest’articolo ci nerico come giustamente ADO.NET, in fase
poniamo dunque la fatidica domanda: “Per- di progettazione ha dovuto fare (tutto è un
chè usare Tdo anziché ADO.NET 2.0 ?” “object”).
Fatto salvo che la risposta a tale domanda Personalmente ho sempre avuto gli “in-
è assolutamente personale, possiamo però cubi” a cablare il nome di una tabella o il
individuare alcuni punti di forza di Tdo ri- nome di un campo direttamente nel codice
spetto al modello offerto da ADO.NET 2.0. o a fare continuamente operazioni di casting
Innanzitutto Tdo non ha la pretesa di so- da object al mio tipo di dato e col passare

Tabella 1 Eventi supportati da Tdo

Tdo Class Handler Event

TdoHelperBase StateChangeEventHandler ConnectionStateChange


EventHandler Disposed
FillErrorEventHandler FillError
SqlInfoMessageEventHandler InfoMessage
SqlRowUpdatedEventHandler RowUpdated
SqlRowUpdatingEventHandler RowUpdating
StatementCompletedEventHandler StatementCompleted

TdoEntityBase FillErrorEventHandler FillError


SqlRowUpdatedEventHandler RowUpdated
SqlRowUpdatingEventHandler RowUpdating
StatementCompletedEventHandler StatementCompleted

TdoStoredProcedureBase StatementCompletedEventHandler StatementCompleted

N. 67 - Gennaio/Febbraio 2006 VBJ 31


TDO 2.0

degli anni… ho visto molti sviluppatori im- con clausola where? Saremmo costretti, an-
pazzire con database di configurazione (con cora una volta, a scrivere la clausola where di-
dentro i nomi delle tabelle e dei campi), file rettamente nel testo della query. L’altro pun-
xml chilometrici o peggio ancora sviluppa- to di maggior rilievo è dato dal fatto che Tdo
tori tesserati ai fun-club “tutto con Stored è molto più “spinto” per operazioni in moda-
Procedure” ☺ mascherandosi dietro al fin- lità connessa, delegando ad ADO.NET 2.0 la
to motivo delle prestazioni. parte disconnessa.
Anche qui non so quanti di voi ad esem-
pio utilizzano DataSet e DataAdapter per
operazioni di aggiornamento in applicazioni
Il pacchetto installa ASP.NET; personalmente lo evito, non tanto
sia la libreria per questioni di performance, ma perchè si fi-
nisce sempre ad usarlo allo vecchia maniera
di base Tdo.dll che stile ADO VB6: l’utente modifica un record in
il generatore di codice una schermata… e subito corriamo ad aggior-
TdoCodeGenerator.exe nare il database con DataSet e DataAdapter.
Questa è paranoia!!! Discorso diverso invece
se l’applicazione è progettata per funzionare
in modalità disconnessa… ma non se ne ve-
Proprio su questo argomento qualcuno dono molte in giro.
di voi potrebbe obiettare dicendo: “ma ci Un altro punto a favore di Tdo è la possibili-
sono i DataSet tipizzati”; giusto, ma van- tà di scrivere espressioni T-SQL direttamente
no bene solo per operazioni disconnesse. in C# o VB.NET, quindi un solo linguaggio e
In ADO.NET 2.0 c’è stato un tentativo da soprattutto controllo sui tipi in fase di compi-
parte di Microsoft (con i TableAdapters) di lazione. Ultimo punto ma non il meno impor-
compensare questa mancanza, ma in realtà tante è che Tdo è open source: sia i sorgenti
il gap è ancora grande. della libreria base (Tdo.dll) sia del generato-
re di codice (TdoCodeGenerator) sono dispo-
nibili per il download su tdo.sourceforge.net
e questo vuol dire customizzazione del codi-
Le classi Tdo ed il codice ce e maggior controllo. Chiunque fosse inte-
generato da TdoCodeGene- ressato a partecipare al continuo sviluppo di
Tdo o semplicemente per maggior chiarimen-
rator sono serializzabili sia in
ti può mettersi in contatto con me.
formato binario che XML
Bibliografia
[1] Andrea Ferendeles - “Performance con
I TableAdapters, che poi non sono nient’al- GAC e NGEN in ASP.NET”, VBJ n.64/
tro che SqlDataAdapter uno per ogni tabella, Infomedia, 2005.
ed i comandi Insert/Update/Delete e Select [2] Andrea Ferendeles - “Tdo Typed Data Ob-
che il dataset tipizzato genera, sono ancora ject 1.0”, VBJ n.62/Infomedia, 2005.
troppo generici e poco adattabili alle diver-
se tipologie di query di cui si ha bisogno a Riferimenti
run-time. Ad esempio il metodo Insert pre- [3] Tdo home site: http://tdo.sourceforge.net
vede che gli vengano passati TUTTI i valori (documentazione e quickstart).
dei campi; e se volessimo inserirne solo al- [4] Tdo sourgeforge site: http://sourceforge.net/
cuni e settare gli altri a Null?! projects/tdo (download, news, forum, mail-
Oppure se volessimo eseguire una select ing list).

32 VBJ N. 67 - Gennaio/Febbraio 2006


TECNICHE

Multithreading
semplice e robusto in
applicazioni Windows
Forms 1.1 e 2.0
Chi sviluppa in .NET 2.0 può scrivere applicazioni Win- Multithreading
dows Forms multithreaded in modo semplice grazie al
nuovo componente BackgroundWorker. In questo articolo
vedremo come implementare un identico controllo in .NET
1.1 e come estenderlo per renderlo persino più potente del
componente fornito con Visual Studio 2005.

di Francesco Balena

S
crivere applicazioni Windows Form mul- A questi problemi si aggiunge
ti-threaded non è mai stato un compi- però un’altra limitazione, che
to semplice. Ad esempio, occorre pren- è peculiare delle sole applica-
dere in considerazione tutti i possibili fattori zioni Windows Form: l’impos-
che possono portare a deadlock oppure a erro- sibilità di invocare un metodo
ri derivanti da race condition oppure ancora a o accedere alle proprietà di un
dati inconsistenti causati dall’accesso non sin- controllo di un form (o un altro
cronizzato a variabili o altre risorse condivise elemento dell'interfaccia uten-
tra i vari thread dell'applicazione. I problemi di te) dal codice in esecuzione da
questo tipo sono in realtà comuni a tutte le ap- un thread secondario.
plicazioni che eseguono codice su più thread e Ad esempio, se il thread se-
si possono risolvere solo mediante una attenta condario sta eseguendo una
analisi del codice e un uso accorto delle primi- azione in background, non è
tive di sincronizzazione offerte dal linguaggio permesso aggiornare un con-
(lock in C#, SyncLock in VB.NET) e dal .NET trollo ProgressBar per infor-
Framework (gli oggetti Monitor, Mutex, Reade- mare l’utente sullo stato di
rWriterLock, Interlocked, ManualResetEvents e avanzamento dell'azione.
AutoResetEvents), o ancora le API messe a di- Per essere più precisi, ac-
sposizione da Windows (ad esempio i semafo- cedere a un controllo da un
ri, che però sono disponibili come oggetti ma- thread secondario è un'azione
naged nella versione 2.0 di .NET). fortemente sconsigliata perchè

34 VBJ N. 67 - Gennaio/Febbraio 2006


TECNICHE

Figura 1 Il controllo BackgroundWorker in Visual Studio 2003 a design time

può portare a malfunzionamenti o errori fa- controllo che è stato creato nel thread di in-
tali dell'applicazione: in alcuni casi un bug terfaccia utente solo per mezzo di un com-
di questo tipo può rimanere “in sonno” per plicato sistema di delegate.
giorni o settimane, ma nella maggioranza In pratica, il codice che gira nel thread se-
dei casi prima o poi un'applicazione che con- condario deve creare un delegate che pun-
tiene questo errore di programmazione an- ta alla procedura che vuole eseguire nel
drà rovinosamente in crash, per giunta la- thread UI e deve passare questo delegate
sciandovi ignari di cosa sia realmente avve- al metodo Invoke del form (vedremo i det-
nuto. Questo è un errore talmente comune tagli tra breve).
che in .NET 2.0 il comportamento delle ap- Poichè il meccanismo non è proprio sem-
plicazioni Windows Forms è stato modifica- plice, molti programmatori lo implementano
to e ogni tentativo di accedere a un controllo male o non lo implementano affatto.
creato in un altro thread genera un'eccezio- Per semplificare la vita agli sviluppatori,
ne se il programma esegue in modalità De- .NET 2.0 contiene un nuovo controllo chia-
bug: in modo che il programmatore si possa mato BackgroundWorker. L’uso di questo
rendere conto del problema sin dalla fase controllo è davvero banale. Supponiamo ad
di test (l’errore non avviene in modalità Re- esempio che il thread secondario debba ese-
lease perchè avrebbe reso inutilizzabili le guire un countdown, contando da N a 0 e
poche applicazioni esistenti in cui l’errore mostrando su un controllo Label il numero
esiste ma non ha effetti rovinosi). dei secondi che mancano alla fine del pro-
cesso. Ecco nel Listato 1 come si può fare
Il controllo BackgroundWorker tutto ciò in VB 2005.
di .NET 2.0 Per maggiori informazioni sul control-
In .NET 1.1 un thread secondario – detto lo BackgroundWorker si consulti la docu-
anche thread non-UI – può accedere a un mentazione della versione 2.0 del .NET Fra-

N. 67 - Gennaio/Febbraio 2006 VBJ 35


TECNICHE

mework. Nonostante le sue indubbie qualità, thread di .NET o creato mediante un og-
il controllo BackgroundWorker non è esente getto System.Threading.Thread.
da difetti e limiti. Ad esempio:
Per tutti questi motivi, ma anche per dare
• Non permette di impostare la priorità del la possibilità di utilizzare il controllo Back-
thread secondario. groundWorker nelle applicazioni .NET 1.1,
• Non permette di impostare il nome del ho deciso di creare una versione custom del
thread secondario, il che può rendere controllo (Figura 1). Questa versione è scrit-
difficoltoso il debug dell'applicazione (il ta in C# per Visual Studio 2003 ed è qua-
nome del thread serve proprio per iden- si perfettamente compatibile con la versio-
tificare in modo univoco quale thread sta ne per 2.0. Questo significa che se utilizzate
eseguendo il codice su cui abbiamo posto questo controllo nelle applicazioni .NET 1.1
un breakpoint). potrete poi migrare il codice senza proble-
• Non offre alcun altro tipo di controllo sul mi su .NET 2.0 (utilizzando il “vero” control-
thread, ad esempio la possibilità di sospen- lo BackgroundWorker). A meno che, ovvia-
derlo o terminarlo prima del termine na- mente, non abbiate deciso di usare una delle
turale dell'esecuzione. feature extra sopra descritte, che il mio con-
• Non permette di decidere se il thread se- trollo BackgroundWorker aggiunge al con-
condario debba essere preso dal pool dei trollo di .NET 2.0.

Listato 1 Il countdown del thread secondario in VB 2005

Private Sub btnStart_Click(ByVal sender As Object, ByVal e As EventArgs) _


Handles btnStart.Click
‘ Conta da 10 a 0 in un thread separato
BackgroundWorker1.RunWorkerAsync(10)
End Sub

‘ Questo codice esegue nel thread secondario

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _


Handles BackgroundWorker1.DoWork
Dim start As Integer = CInt(e.Argument)
For i As Integer = start To 0 Step -1
BackgroundWorker1.ReportProgress(i)
System.Threading.Thread.Sleep(1000)
Next
End Sub

‘ Questo codice esegue nel thread UI quando si invoca il metodo ReportProgress

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _


Handles BackgroundWorker1.ProgressChanged
‘ Mostra in un controllo Label il numero dei secondi rimasti
lblSecondsLeft.Text = e.ProgressPercentage.ToString()
End Sub

‘ Questo codice esegue nel thread UI quando la procedura DoWork termina.

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _


Handles BackgroundWorker1.RunWorkerCompleted
‘ Indica che il processo asincrono è terminato
lblSecondsLeft.Text = “Completed!”
End Sub

36 VBJ N. 67 - Gennaio/Febbraio 2006


TECNICHE

Il BackgroundWorker control custom al thread secondario gli argomenti passati


Il sorgente completo del controllo, allega- a RunWorkerAsync.
to all’articolo insieme a un semplice pro- Il codice in OnDoWork memorizza questi
getto demo, contiene circa 600 righe di C#. argomenti nella variabile privata evArgs e
Eliminando però i commenti XML e qual- poi chiama la procedura ThreadEntryPoint,
che porzione meno interessante, il sorgen- la quale recupera tali valori e li passa final-
te del controllo può essere condensato come mente al gestore dell’evento DoWork.
nel Listato 2.
Il sorgente contiene numerose parti di in-
teresse. In questo articolo mi limito a illu-
strare le principali, lasciando le altre come Scrivere applicazioni
“esercizio per il lettore”. Windows Form multi-
La proprietà booleana WorkerUsesThrea-
dPool - che è assente nel control Back- threaded non è mai stato
groundWorker di .NET 2.0 - indica se il un compito semplice
controllo usa il pool di thread di .NET op-
pure l’oggetto Thread in System.Threading.
Il primo meccanismo è più scalabile e do-
vrebbe essere usato se possibile, mentre il Intercettare il termine dell’esecuzione del
secondo offre un maggiore controllo sul thread non pone particolari difficoltà: se si
thread. Ad esempio, le proprietà Thred- usano i delegate asincroni basta registrare
Priority e ThreadName sono utilizzate solo come completion method del delegate il me-
nel secondo caso. todo DoWorkCompleted, il quale a sua volta
Se WorkerUsesThreadPool è True, allora chiama OnRunWorkerCompleted: quest’ul-
l’applicazione può utilizzare la proprietà timo metodo scatena l’evento RunWorker-
Thread (a sola lettura), che restituisce l’og- Completed. Se invece stiamo usando un og-
getto System.Threading.Thread corrispon- getto Thread, al ritorno dalla chiamata asin-
dente al thread secondario in cui viene ese- crona il metodo ThreadEntryPoint chiama
guito il codice nell’evento DoWork. Grazie a direttamente OnRunWorkerCompleted. Il
questa proprietà l’applicazione può sospen- passaggio tra i due thread - il thread se-
dere il thread, abortirlo, cambiare la priori- condario usato dal controllo e il thread di UI
tà mentre è in esecuzione, e così via. Tut- - è codificato nel metodo privato InvokeDe-
te queste feature non sono disponibili se il legate, che è in grado di attivare gli eventi
controllo utilizza un thread preso dal pool ReportProgress e RunWorkerCompleted (ri-
(e non sono disponibili nel controllo stan- cordo che questi eventi devono essere ese-
dard fornito con .NET 2.0). guiti nel thread UI). Il metodo InvokeDele-
Quando l’applicazione chiama il metodo gate deve eseguire il dispatch della chiamata
RunWorkerAsync, il controllo invoca il me- al thread IU mediante il metodo Invoke di
todo protected OnDoWork, dove si fa partire un oggetto che supporta l’interfaccia ISyn-
il thread in uno dei due modi supportati. Se chronizeInvoke. I casi che si presentano a
WorkerUsesThreadPool è True, la procedu- questo punto sono i seguenti:
ra che richiama il thread secondario viene
invocata per mezzo di delegate asincroni: a) la proprietà SynchronizeObject è imposta-
questa tecnica permette, tra le altre cose, di ta a un valore non-Nothing. In questo caso
passare argomenti al thread senza bisogno abbiamo l’oggetto su cui chiamare il me-
di adottare un particolare accorgimento. todo Invoke.
Se invece WorkerUsesThreadPool è False, b) la proprietà SynchronizeObject non è stata
si pone il problema di rendere disponibile impostata. In questo caso il metodo Invoke-

N. 67 - Gennaio/Febbraio 2006 VBJ 37


TECNICHE

Delegate visita tutti gli oggetti che hanno questa proprietà è True. Ad esempio, il codice
registrato l’evento da scatenare (per mezzo che esegue il countdown dovrebbe essere mo-
del metodo GetInvocationList) e utilizza il dificato in questo modo:
primo che supporta l’interfaccia ISynchro-
nizeInvoke. Private Sub BackgroundWorker1_DoWork(ByVal sender As O
c) Se nessun oggetto supporta questa inter- bject, ByVal e As DoWorkEventArgs) _
faccia, l’evento viene sollevato nel thread Handles BackgroundWorker1.DoWork
secondario (ovviamente, in questo caso non Dim start As Integer = CInt(e.Argument)
si dovrebbe accedere ai controlli di inter- For i As Integer = start To 0 Step -1
faccia utente). BackgroundWorker1.ReportProgress(i)
System.Threading.Thread.Sleep(1000)
L’ultimo punto di interesse è nel fatto che il If BackgroundWorker1.CancellationPending Then Exit Sub
controllo BackgroundWorker supporta l’inter- Next
faccia IExtenderProvider e quindi implementa End Sub
il suo metodo CanExtend. Come è noto, que-
sta interfaccia è normalmente usata per im- Per quanto questo meccanismo possa sembra-
plementare i provider control, come HelpPro- re poco raffinato a prima vista, si consideri che
vider e ToolTip, ma in questo caso il controllo questa tecnica è semplice da implementare e
non espone alcuna proprietà extender, quindi mette comunque al riparo dall'eventualità che
l’implementazione di questa interfaccia sem- una cancellazione “brusca” del thread provochi
brerebbe inutile a prima vista. una perdita dei dati. Se però il thread secon-
dario richiama altri componenti o esegue una
query molto lunga, questa tecnica potrebbe non
essere sufficiente, perchè in tali casi il thread
Per semplificare la vita agli
deve davvero essere interrotto a tutti i costi. In
sviluppatori, .NET 2.0 contie- questo caso può essere conveniente impostare
ne un nuovo controllo chia- la proprietà WorkerUsesThreadPool a False e
usare il metodo Abort dell’oggetto Thread espo-
mato BackgroundWorker sto dal controllo:

BackgroundWorker1.Thread.Abort()
L’unico motivo per implementare questa in-
terfaccia è la possibilità di impostare automati- Quando l’esecuzione del thread secondario è
camente la proprietà SynchronizingObject pari interrotta in uno dei due modi descritti, la pro-
al valore del form al momento della creazione prietà Canceled dell’oggetto RunWorkerComple-
del controllo. Questo è infatti il comportamen- tedEventArgs ricevuto dall’evento RunWorker-
to di default del controllo BackgroundWorker Completed è impostata al valore True, e il codi-
in .NET 2.0, come pure di altri componenti che ce può determinare il motivo della cancellazio-
devono sollevare eventi in thread secondari, ad ne testando la proprietà Error.
esempio FileSystemWatcher.
Conclusioni
Il metodo CancelAsync Il controllo BackgroundWorker descritto nel-
Il metodo CancelAsync richiede una spiega- l’articolo può essere usato nelle applicazioni
zione ulteriore. In realtà, questo metodo non fa .NET 1.1 per prepararsi alla migrazione ver-
null’altro che impostare a True il valore della so .NET 2.0, ma può anche essere usato in
proprietà CancellationPending. È compito del VisualStudio 2005 per sfruttare alcune fea-
codice nell’evento DoWork di terminare “dol- ture che Microsoft ha “dimenticato” di inse-
cemente” la sua esecuzione quando scopre che rire nel controllo.

38 VBJ N. 67 - Gennaio/Febbraio 2006


VB6

File compatti Prima puntata

e illeggibili:
Crittografia Crittografia

Crittografia e compressione di file in VB6 utilizzando il


Microsoft .NET Framework

di Scott Swigart, Swigart Consulting LLC

Introduzione

I
n questa serie di due articoli, si vedrà come
si possono aggiungere facilmente funziona- Ho un segreto, e non voglio
lità di crittografia e di compressione dati che lo conosciate. In effetti, ho
(ZIP) alle applicazioni VB6 esistenti utilizzando una applicazione, e voglio es-
il .NET Framework. Benché la crittografia e la sere in grado di memorizzare
compressione possano non sembrare tecnologie le informazioni in un formato
correlate, se si pensa ad esse, ciascuna prende crittografato in modo che non
un insieme di dati ed esegue una trasformazio- possano essere lette da utenti
ne su di esso. Nel caso della crittografia, i dati non autorizzati. Visual Basic 6
vengono resi illeggibili, mentre con la compres- non ha alcuna funzionalità na-
sione i dati vengono resi più piccoli. Si vedrà an- tiva per eseguire la crittografia
che che entrambe utilizzano molte delle mede- o la decrittazione dei dati, ma
sime tecnologie sottostanti. il Microsoft .NET Framework
dispone in modo nativo di crit-
tografia forte. In questo arti-
© 2006 Microsoft Corporation. All right reserved
colo descrivo un componente
VB.NET che fornisce le funzio-
Scott Swigart è consulente, autore e speaker su tecnologie nalità di crittografia e di decrit-
emergenti e convergenti. Nella sua professione ha lavorato con tazione, e si può utilizzarlo mol-
una vasta gamma di tecnologie, iniziando con la programmazio- to facilmente da VB6, ASP o da
ne del Commodore 64 all’età di 12 anni, scrivendo diagnostica
dell’hardware per sistemi UNIX in C++, e realizzando applica-
qualsiasi altro ambiente COM.
zioni Windows desktop e Web. Negli anni, Scott ha lavorato Prima di tutto, è bene esamina-
allo sviluppo di componenti, con tecnologie XML, .NET, Web re come Visual Basic 6 permet-
service e altri linguaggi, piattaforme e paradigmi. Grazie a que-
te di operare con i file. VB6 for-
sta esperienza, Scott ha visto come la tecnologia evolve nel
tempo, e si è concentrato nell’aiutare le organizzazioni a trarre nisce diverse keyword del lin-
il massimo vantaggio dalla tecnologia odierna, pur preparandosi guaggio per l’I/O su file:
alla tecnologia di domani. Scott è anche un Microsoft MVP,
ed è coautore di numerosi libri e articoli. Scott può essere
‘ Lettura da file con Visual Basic 6
contattato all’indirizzo scott@swigartconsulting.com.
Dim dataIn As String

40 VBJ N. 67 - Gennaio/Febbraio 2006


VB6

Open someFileName For Input As #1 Fondamenti sull’I/O di file con VB.NET


Do Until Eof(1) Prima di affrontare la crittografia, è importan-
Line Input #1, dataIn te comprendere come Visual Basic .NET gesti-
Debug.Print dataIn sce l’I/O dei file. Il Microsoft .NET Framework
Loop utilizza qualcosa noto come “stream” quando
Close #1 opera con i file, come è mostrato qui:

Con VB6, si eseguono tutte le proprie opera- ‘ I/O di file con Visual Basic .NET
zioni su un handle di file (#1 in questo caso), Dim sw As New StreamWriter(“C:\log.txt”)
e sta a voi gestire questo handle di file. Non vi sw.WriteLine(“This is a test”)
è un unico oggetto che incapsula le operazioni sw.Close()
su file, esistono invece diverse keyword (Open,
Close, Input, ecc). Se si vuole eseguire la crit- Si può vedere che Visual Basic .NET fornisce
tografia e la decrittazione con Visual Basic 6, specificamente delle classi per la lettura e la scrit-
si dovrebbero leggere tutti i dati in memoria, tura da file (e da altri dispositivi di I/O). In que-
crittografarli utilizzando un componente di ter- sto caso, il codice utilizza la classe StreamWri-
ze parti (generando ciò che è noto come “testo ter per scrivere righe di testo in un file. L’og-
cifrato”), e poi memorizzare il testo cifrato su getto StreamWriter è molto comodo, poiché non
un file di output. Sarebbe auspicabile poter far si deve tener traccia di un handle di file, e tut-
fluire le informazioni attraverso il proprio pro- te le operazioni per operare con un file vengo-
gramma, ma ciò non sarebbe facile da fare. Per no evidenziate da Intellisense. Oltre alla classe
facilitare realmente una facile crittografia da StreamWriter, ci sono anche le seguenti classi:
VB6, ho creato un componente di crittografia/
decrittazione. In seguito si vedrà come questo FileStream Lettura e scrittura da un fi-
componente è stato implementato, ma è bene le binario.
menzionare che non è necessario comprende- NetworkStream Lettura e scrittura da una
re i dettagli di implementazione per utilizza- rete.
re effettivamente il componente. Se si vuole MemoryStream Lettura e scrittura dalla me-
semplicemente utilizzare il componente sen- moria come se fosse un file.
za addentrarsi in come funziona, basta legge-
re il paragrafo “Crittografia VB6”. In realtà, la classe StreamWriter del codice
di esempio utilizza in-
ternamente un oggetto
FileStream per scrivere
informazioni su disco.
FileStream permette di
creare, aprire e accoda-
re dati in file su disco,
e di aprirli in lettura e
scrittura. Tuttavia, File-
Stream vuole leggere e
scrivere blocchi di dati
(array di byte) da e ver-
so file. Quando si opera
con il testo, tipicamen-
Figura 1 Tentativo di visualizzare i dati crittografati con Notepad
te si vuole lavorare con
righe, non con blocchi
di 1024 byte, pertanto

N. 67 - Gennaio/Febbraio 2006 VBJ 41


VB6

StreamWriter si basa sull’oggetto


FileStream per fornire la funziona-
lità WriteLine. Si potrebbe anche
basare StreamWriter o StreamRea-
der su un oggetto NetworkStream
o MemoryStream in modo che si
possa eseguire WriteLine o ReadLi-
ne anche da questi dispositivi. È
questa realmente la potenza degli
stream. Si può basare uno stream
su un altro per collegare ulterio-
ri funzionalità. Infatti, è così che
funziona la crittografia in VB.NET.
Viene aggiunto allo stack il “Cryp-
toStream”, e tutti i dati che lo at-
traversano vengono crittografati o Figura 2 Il form dell’applicazione d’esempio

decodificati:

‘ Pila di stream per abilitare la crittografia in VB.NET


fs = New FileStream(fileName, FileMode.Create, Dim pdb As New PasswordDeriveBytes(password, Nothing)
FileAccess.Write) rc2.Key = pdb.CryptDeriveKey(“RC2”, “SHA1”, 128, IV)
cs = New CryptoStream(fs, rc2.CreateEncryptor(),
CryptoStreamMode.Write) La crittografia simmetrica si basa sull’utilizzo
sw = New StreamWriter(cs) di una chiave segreta per crittografare le infor-
mazioni. In questo caso, la chiave sarà fornita a
Qui, viene creata una classe FileStream per questo algoritmo sotto forma di password. Dalla
abilitare la scrittura su uno specifico file su di- password, viene derivata una chiave crittogra-
sco. Un CryptoStream si basa sul FileStream fica utilizzando la classe PasswordDeriveBytes.
in modo che le informazioni siano crittografa- Per ulteriore sicurezza, può essere utilizzato un
te prima che vengano inviate al FileStream. Lo vettore di inizializzazione (IV). Un IV viene uti-
StreamWriter viene quindi basato sul Crypto- lizzato per assicurare che se un documento con-
Stream in modo da avere una comoda funzio- tiene sezioni identiche di testo in chiaro, non
nalità di WriteLine, piuttosto che dover opera- vengano crittografate come testo cifrato identi-
re con blocchi di dati a lunghezza fissa. Il Cryp- co. Ciò rende anche la chiave più sicura se più
toStream è ciò che crea la magia della critto- file vengono crittografati utilizzando la stessa
grafia. Supporta molti algoritmi di crittografia, chiave. Quando si utilizza questo componente,
e pertanto deve essere configurato con alcune specificare un IV è opzionale.
informazioni iniziali prima di essere utilizzato.
Per questo componente, è stato utilizzato l’al- Crittografia in VB6
goritmo di crittografia RC2 [1], poiché fornisce Nuovamente, benché qui vengano trattate le
una crittografia robusta e veloce. Tuttavia, se si basi della crittografia, non è necessario conosce-
vuole utilizzare la crittografia DES, Triple-DES re nulla su .NET o sulla crittografia per utiliz-
o Rijndael, questo codice può essere modificato zare questo componente. Da VB6, l’utilizzo di
per utilizzare questi altri algoritmi: questo componente è semplice, come si vede
nel seguente frammento di codice:
‘ Configurazione della crittografia RC2 con VB.NET
Dim rc2 As New RC2CryptoServiceProvider ‘ Utilizzando del componente di crittografia da VB6
rc2.IV = IV Dim e As wrappers.EncryptedFileWriterWrapper

42 VBJ N. 67 - Gennaio/Febbraio 2006


VB6

sual Studio .NET, si può saltare questo


passo).
2. Scaricare il codice allegato a questo arti-
colo.
3. Eseguire il file “Build and Register.bat”
incluso nel codice, ottenendo il build del
componente di crittografia e registrarlo
in modo da poterlo utilizzare dalla pro-
Figura 3 Se si cambia password, in modo che pria applicazione VB6.
non corrisponda alla password con cui 4. Nel codice dell’articolo, aprire il progetto
è stato salvato il testo, si vedrà che il
file non viene decodificato
VB6 nella cartella “Encryption”.
5. Premere il tasto F5 per eseguire l’appli-
cazione di esempio (Figura 2).
Set e = New wrappers.EncryptedFileWriterWrapper 6. Immettere qualsiasi stringa come pas-
sword, e premere Save. Il testo viene
e.Open “c:\file.bin”, txtPassword, False, “” salvato in “c:\file.bin”. Se si apre questo
e.WriteLine (txtMessage) file in Notepad, si vedrà che è crittogra-
e.Close fato. La textbox della password viene an-
che azzerata.
Come si vede, ora che l’EncryptedFileWri- 7. Se si clicca su Load, l’informazione ver-
terWrapper è stato creato, è banale da utiliz- rà recuperata, decodificata e visualizzata
zare. Basta crearne una istanza, passargli un nella textbox.
nome di file e una password, e iniziare a scri- Se si cambia password, in modo che non
vere dati. I dati verranno crittografati e scritti corrisponda alla password con cui è sta-
su file (Figura 1). La lettura e la decrittazione to salvato il testo, si vedrà che il file non
dei dati è ugualmente semplice: viene decodificato (Figura 3)

‘ Lettura e decrittazione dei dati utilizzando VB6 Conclusioni


Dim e As wrappers.EncryptedFileReaderWrapper Visual Basic 6 non fornisce una funzionalità
Set e = New EncryptedFileReaderWrapper nativa di crittografia/decrittazione, ma Micro-
soft .NET Framework sì.
e.Open “c:\file.bin”, txtPassword, “” Come accade in tutto il .NET Framework, è
While Not e.EOF relativamente semplice esporre questa funzio-
txtMessage = txtMessage & e.ReadLine & vbCrLf nalità in modo che possa essere utilizzata da
Wend un'applicazione VB6.
e.Close Questo articolo fornisce una soluzione pre-
confezionata per aggiungere la funzionalità di
Nuovamente, basta specificare un nome di crittografia e decrittazione alla propria appli-
file e una password e iniziare a leggere. I dati cazione VB6.
vengono decodificati dal componente e resti- E bastano solo poche righe di codice!
tuiti come semplice testo. Per vostra comodità,
ho realizzato un’applicazione VB6 che utilizza Riferimenti
il componente per eseguire la crittografia e la [1] http://en.wikipedia.org/wiki/RC2
decrittazione. Per utilizzare questo componen- [2] http://en.wikipedia.org/wiki/Data_
te, può essere necessario fare alcune cose: Encryption_Standard
[3] http://en.wikipedia.org/wiki/Triple_DES
1. Scaricare e installare il Microsoft .NET [4] http://en.wikipedia.org/wiki/Advanced_
Framework SDK 1.1 (se si è installato Vi- Encryption_Standard

N. 67 - Gennaio/Febbraio 2006 VBJ 43


SOFTWARE ENGINEERING

Executable UML
Executable UML (xUML) è un formalismo che consente di
applicare concretamente le linee guida fornite dalla Model
Driven Architecture (MDA)

di Lorenzo Vandoni

N
el precedente numero di questa rubrica che abbia sostituito la babele
abbiamo parlato della Model Driven Ar- di linguaggi e modelli propo-
chitecture (MDA) proposta dall’Object sti ed utilizzati nelle scorse de-
Management Group (OMG), che si pone l’obiet- cadi. Executable UML (xUML)
tivo di separare completamente la logica appli- estende UML con l’introduzio-
cativa dalla piattaforma sulla quale l’applicazio- ne di aspetti dinamici che con-
ne dovrà essere implementata. sentono ai diagrammi di essere
In particolare, abbiamo visto come questa archi- eseguibili, verificabili, e quindi
tettura preveda la realizzazione di un modello di traducibili in codice applicativo
alto livello denominato Platform Independent Mo- da speciali compilatori. Di fat-
del (PIM), che descriva il comportamento dell’ap- to, in questo caso alcuni auto-
plicazione. Questo modello verrà successivamen- ri parlano di “Executable and
te convertito, più o meno automaticamente, in un Translatable” UML, indicato
modello specifico per una o più piattaforme, come con l’acronimo xtUML.
CORBA, COM, Java o .NET (Platform Specific Mo- xUML è quindi un sottoinsie-
del, o PSM), e quindi in un programma funzionante me di UML (nel senso che ne
(Platform Specific Implementation, o PSI). utilizza solo alcuni costrutti,
Executable UML, come vedremo, non è altro che come Package, diagrammi di
uno strumento per mettere in pratica le linee gui- classe e diagrammi di stato), e
da di MDA. una sua estensione, nel senso
che adotta strumenti aggiuntivi
Da UML a xUML per specificare dettagli e aspet-
UML è un formalismo di alto livello, che or- ti dinamici dei sistemi model-
mai si è fortunatamente imposto come la nota- lati. Inoltre xUML è una meto-
zione di riferimento per la stesura di diagrammi dologia di lavoro automatizza-
di analisi e di progettazione. Al di là del fatto di ta, basata sulla notazione UML,
apprezzare o meno questo tipo di notazione, in- che formalizza alcuni concetti
fatti, non si può non considerare positivo il fat- chiave di MDA. xUML non è
to che questa sia universalmente riconosciuta, e ancora uno standard, ma pro-
babilmente lo diventerà pre-
sto, perché OMG ha già avvia-
Lorenzo Vandoni è laureato in Informatica, ed è uno spe- to il processo di standardizza-
cialista di progettazione e sviluppo con tecniche e linguaggi
object-oriented. Ha collaborato alla realizzazione di framework,
zione con una richiesta di pro-
strumenti di sviluppo e software commerciali in C++, Java e poste in questo senso. In ogni
Visual Basic. Può essere contattato tramite e-mail all’indirizzo caso xUML è basato su questi
vandoni@infomedia.it. concetti:

44 VBJ N. 67 - Gennaio/Febbraio 2006


SOFTWARE ENGINEERING

• il modello dell’applicazione, descritto con do in alcuni casi duplicazioni e ridondanze.


UML, è indipendente da qualsiasi partico- Con xUML, in un certo senso Mellor si pren-
lare piattaforma applicativa de una tardiva rivincita, proponendo una no-
tazione semplificata e reintroducendo un si-
• tale modello può essere mandato in esecu- stema di generazione automatico.
zione, o simulato, il che costituisce un ot-
timo modo per verificare la correttezza dei Il processo di sviluppo
requisiti e del funzionamento del sistema La più singolare particolarità di xUML è la
modellato completa separazione tra la stesura del mo-
dello dell’applicazione e la definizione delle
• le caratteristiche di ogni specifica piatta- regole di conversione, che guidano la tradu-
forma vengono incapsulate all’interno di zione di elementi del modello in codice sor-
un opportuno “traduttore”, in grado di ge- gente. In pratica, chi si preoccupa di definire
nerare codice a partire dal modello UML quale dovrà essere il comportamento dell’ap-
plicazione è virtualmente indipendente dal-
Uno degli autori più attivi in questo campo è l’esperto della piattaforma di riferimento, che
Stephen Mellor, coautore, molti anni fa, di un invece ha il compito di definire la corrispon-
metodo di sviluppo del software object-orien- denza tra gli elementi dei diagrammi UML e
ted, denominato Shlaer-Mellor, che si basava il codice sorgente.
su una specifica notazione (UML non era an- Al modello xUML vengono aggiunti tutti i
cora stato introdotto), ma introduceva già al- dettagli necessari sia per l’esecuzione e il test
cuni meccanismi di generazione automatica del modello, sia per la successiva conversione
del codice. In seguito UML è nato come fusio- in codice. L’esistenza di questi dettagli con-
ne delle notazioni proposte da Booch, Rum- sente anche di eseguire dei test di tipo forma-
baugh e Jacobsen. Fin dalla sua introduzione, le sul modello, per verificarne la consistenza
una delle critiche rivolte a UML è quello di es- e la completezza.
sere eccessivamente complesso, e di derivare Gli aspetti dinamici del modello vengo-
questa sua complessità da motivi più politici no specificati tramite un “action language”
che scientifici. In UML sono infatti confluite come Action Specification Language (ASL),
diverse caratteristiche delle notazioni origina- che consente di specificare il comportamento
riamente proposte dai tre autori, introducen- runtime, mentre i vincoli sull’esecuzione ven-

Figura 1 Il processo di traduzione

N. 67 - Gennaio/Febbraio 2006 VBJ 45


SOFTWARE ENGINEERING

Figura 2 Il processo di sviluppo con xUML

gono espressi tramite clausole del linguaggio evitare la generazione di codice ripetitivo,
Object Constraint Language (OCL). ed incapsula un insieme di classi e funzio-
Questo particolare permette anche di defi- ni di utilità
nire chiaramente delle linee guida per capi-
re quando un modello è veramente comple- Quando il progettista richiede la generazio-
to. Normalmente, infatti, il livello di detta- ne del codice, il sistema di traduzione estrae
glio e il numero di informazioni inserite nel le informazioni necessarie dal modello xUML,
modello sono lasciati alla sensibilità del pro- e utilizza le regole per selezionare i pattern
gettista. Con xUML, invece, un modello sarà da applicare.
completo solo quando potrà essere mandato
in esecuzione.

Dal modello al codice xUML utilizza solo alcu-


Il processo di traduzione è basato su tre ele- ni costrutti di UML, come
menti fondamentali:
Package, diagrammi di clas-
• anzitutto vi è un insieme di pattern e re- se e diagrammi di stato
gole di traduzione. Un pattern descrive la
traduzione di un elemento xUML in codi-
ce, mentre una regola indica quali pattern Questi pattern possono essere visti come
debbano essere utilizzati per tradurre un porzioni di codice generalizzate, in cui alcuni
diagramma “spazi” siano stati lasciati vuoti, per poter es-
sere riempiti con le informazioni provenienti
• un sistema di traduzione, che applica re- dal modello.
gole e pattern al modello, per generare il Tipicamente, l’invocazione di un pattern pro-
codice corrispondente vocherà la chiamata ad altri sotto-pattern, a
loro volta selezionati applicando le regole di
• una libreria runtime che contiene funzio- traduzione.
nalità comuni di supporto al codice gene- Chiaramente tutto questo non può essere fatto
rato. Questa libreria serve, in pratica, per su carta, ma richiede il supporto di un tool di

46 VBJ N. 67 - Gennaio/Febbraio 2006


SOFTWARE ENGINEERING

sviluppo. Esempi di strumenti del genere sono • possibilità di riutilizzo dei modelli appli-
BridgePoint di Project Technology, che può es- cativi, o parte di essi, indipendentemente
sere trovato su www.projtech.com, e la MDA dalla piattaforma di riferimento: un buon
Product Suite di Kennedy Carter, che può es- modello di gestione del magazzino scritto
sere trovata su www.kc.com/products.php. per un’applicazione Java client-server può
tranquillamente essere riutilizzato per rea-
Benefici di xUML lizzare un sistema per Pocket PC
I benefici legati all’utilizzo di xUML sono
più o meno gli stessi già enunciati nel prece- • facilità di manutenzione, legata al fatto
dente articolo, relativamente ad MDA. Oltre a che modifiche al modello, o al processo
quelli più ovvi, legati al fatto di poter descri- di traduzione, possono essere facilmen-
vere l’applicazione ad alto livello, poter scri- te trasferite all’applicazione finale. Ad
vere meno codice, e poter agevolmente con- esempio, l’ottimizzazione di un partico-
vertire un’applicazione da una piattaforma (es. lare algoritmo, codificato nel processo
Java su Linux) ad un’altra (es. Microsoft.NET), di traduzione ed utilizzato in molti pun-
vale la pena di citare i seguenti: ti dell’applicazione, potrà essere facil-
mente riportata sull’intera applicazione
• anzitutto, il codice generato può essere ot- semplicemente generando nuovamente
timizzato per la piattaforma di riferimen- il codice sorgente
to. Tale ottimizzazione può essere codifi-
cata una volta per tutte nel processo di • il codice ottenuto è tipicamente ben do-
traduzione, senza richiedere particolari cumentato. Lo sforzo di documentare il
competenze da parte degli sviluppatori codice può essere fatto una volta sola ap-
plicandolo ai pattern utilizzati per la ge-
• la possibilità di mandare in esecuzione nerazione
i modelli UML permette ai clienti e agli
utilizzatori finali di verificare la correttez- Alcune perplessità
za del modello applicativo fin dalle prime La visione di xUML come meccanismo che
fasi dello sviluppo permetterà di abbandonare, in un futuro più

Figura 3 Il processo di traduzione può essere applicato a più modelli

N. 67 - Gennaio/Febbraio 2006 VBJ 47


SOFTWARE ENGINEERING

o meno lontano, linguaggi come Java e C++, risultare positivo per applicazioni commerciali
esattamente come da tempo è ormai stato ab- da introdurre sul mercato, ma di solito è ne-
bandonato l’assembler, è certamente affasci- gativo nel caso di applicazioni sviluppate su
nante, ma personalmente non nascondo al- commissione, in quanto quasi sempre ogni
cune perplessità. cliente avrà esigenze particolari.
L’idea di tradurre descrizioni di alto livel- L’esperienza inoltre ha mostrato che rara-
lo in codice non è certamente nuova. Tra la mente si riuscirà a codificare all’interno dei
fine degli anni ‘80 e la prima metà degli anni pattern tutte le particolari esigenze di un pro-
’90 erano abbastanza diffusi i cosidetti stru- gramma, e che per gestire le innumerevoli ec-
menti CASE “di basso livello”, ovvero orien- cezioni sarà necessario intervenire manual-
tati alla progettazione e alla generazione di mente, modificando il codice generato. Oc-
codice. Con questi strumenti, di solito rivol- correrà quindi acquisire le necessarie com-
ti ad applicazioni per la gestione di databa- petenze relative alla struttura di questo codi-
se, lo sviluppatore doveva disegnare un dia- ce e della libreria di runtime, nonché adotta-
gramma dello schema della base dati, e poi re qualche meccanismo che permetta di pre-
aveva a disposizione degli editor grafici per servare gli interventi manuali a fronte di una
disegnare le maschere e i report del program- nuova generazione del codice.
ma. Contrariamente a quanto accade con am- Non è detto, infine, che UML possa essere
adatto per specificare tutti gli aspetti di un’ap-
plicazione. I dettagli dell’interfaccia grafica,
ad esempio, o la progettazione fisica di una
Il sistema di traduzione base dati relazionale, sono difficilmente cat-
applica regole e pattern turabili con questo tipo di notazione.
al modello per generare il
Conclusioni
codice corrispondente Il tentativo di realizzare modelli applicativi
di alto livello che siano eseguibili e consen-
tano quindi di verificare la correttezza delle
bienti di sviluppo visuali come Delphi o Vi- specifiche non è nuova, ed era anzi tra i prin-
sual Studio, queste maschere e report veniva- cipali temi di ricerca nella comunità scien-
no automaticamente dotate di molte funzio- tifica agli inizi degli anni ’90. Nel frattem-
nalità di alto livello. L’architettura complessi- po, però, UML si è affermato come notazio-
va del sistema era sorprendentemente molto ne standard per l’analisi e la progettazione
simile a quella ora proposta da xUML: un in- di applicazioni object-oriented, e quindi que-
sieme di regole di traduzione, un motore in ste idee hanno trovato nuovi stimoli.
grado di interpretarle, e una libreria di run- Come già evidenziato nello scorso numero,
time di supporto. non si può non notare come MDA e xUML
Questi sistemi soffrivano di varie limitazio- vadano quasi in direzione opposta rispetto
ni: alcune di queste erano legate al periodo alle metodologie agili, che stanno ultima-
storico in cui erano nati, prima tra tutte la mente avendo un grande successo. Da una
mancanza di standard come UML, ma altre parte abbiamo un’attenzione quasi ossessiva
potrebbero rimanere valide anche per gli at- per le fasi di analisi, dall’altra un atteggia-
tuali tool basati su xUML. Non bisogna sot- mento più pragmatico, e una maggiore en-
tovalutare le lezioni del passato. fasi per il ruolo dei programmatori. Sicura-
Tra queste limitazioni, una è data dalla ri- mente MDA e xUML trovano una giustifica-
gidità del processo di traduzione, che porta zione nell’ambito di progetti molto grossi,
ad ottenere applicazioni con un look-and-feel dove le metodologie agili possono mostra-
piuttosto uniforme. Questo aspetto potrebbe re alcuni limiti.

48 VBJ N. 67 - Gennaio/Febbraio 2006


WEB

DotNetNuke:
creazione di
un modulo DNN

DotNetNuke è un portale open source sviluppato in tecno-


logia Microsoft .NET. In questo articolo ne vedremo le ca-
ratteristiche principali e le soluzioni adottate per costruire
con esso un portale intranet

di Pietro Vite

D
otNetNuke [5] è un portale scritto in sato a portali Web in tecnologia
VB.NET da Shaun Walker ed altri, Microsoft .NET perchè anche i
usando come base l’applicazione IBuy- capitoli più tecnici, per esempio
Spy WorkShop, sviluppata da Microsoft come quello sulle modalità di installa-
uno dei Source Project che accompagnano il ri- zione e sulle pratiche di ammini-
lascio di .NET Framework 1.0 Beta. strazione, sono molto chiari, ma
Nel primo capitolo del libro della Wrox dal titolo soprattutto perché è stato scritto
“Professional DotNetNuke ASP.NET Portals” [1], dal creatore di DotNetNuke. Nel
Shaun Walker racconta la genesi di DotNekNuke, sito www.dotnetnuke.org è an-
fino alla attuale versione 3.0. La necessità di svi- che citato un altro libro interes-
luppare e gestire portali per piccole e medie azien- sante, edito da PACKT, dal tito-
de e a basso costo spinge Shaun a rimaneggiare il lo “Building Websites with VB:
codice VB.NET di IBuySpy WorkShop, applicativo NET and DotNetNuke 3.0” [2].
sviluppato da Microsoft per illustrare, con un’appli-
cazione web completa, le potenzialità di ASP.NET. La storia del portale Dot-
Nel libro, con molta sincerità, l’autore racconta gli NetNuke di AWD Sim
errori commessi, le difficoltà superate e il gruppo Nel 2004 AWD Sim [3] inizia a
di persone che si raccolsero intorno al progetto, in- valutare la possibilità di dotarsi di
sieme al concreto supporto di Microsoft stessa, in- un portale aziendale, molto utile
teressata allo sviluppo di questa iniziativa, normal- per la gestione delle informazioni
mente appannaggio di comunità Perl, Pyton, Php da erogare alla Rete di Vendita,
o, al più, Java. Shaun Walker ha scritto un libro ma gli impegni di business piani-
che mi sento di consigliare a chi può essere interes- ficati impediscono di trasforma-
rere i requisiti di business in un
progetto software. Nel 2005, dopo
Pietro Vite è IT Manager di AWD Sim Italia. Ha lavorato per
diverse SIM e istituti di credito nell’area Finance e IT. Può essere una profonda riorganizzazione, il
contattato tramite e-mail all’indirizzo pvite@infomedia.it. management decide di soddisfa-

50 VBJ N. 67 - Gennaio/Febbraio 2006


WEB

Figura 1 Il file .ascx del modulo

re le pressanti richieste della Rete di Vendita. I contenuti dell’articolo


L’architettura IT di AWD Sim è fortemente le- Dopo l’installazione dell’applicativo, che
gata ai prodotti Microsoft. È naturale, quindi, non descrivo perché ben documentata sul
orientare la scelta verso prodotti che valorizzino sito del prodotto, nei due libri citati prima
le competenze del personale IT cresciuti all’in- e in alcuni articoli molto dettagliati recupe-
terno di AWD Sim. Nella primavera del 2005, rarabili da Google su Internet, illustro bre-
l’Amministratore Delegato di Debug Software vemente il concetto di modulo, l’insieme
& Tailoring S.p.A. [4], la società software pro- dei componenti che permettono la perso-
duttrice del prodotto di amministrazione della nalizzazione del portale e proseguo quindi
Rete di Vendita utilizzato da AWD Sim, illustra con la descrizione dello sviluppo di un mo-
la soluzione DNN adottata per lo sviluppo del dulo che estrae da una tabella SQL Server
proprio portale aziendale. La scelta di adotta- l’ultimo messaggio da mostrare nel com-
re DotNetNuke coniuga robustezza e sempli- ponente.
cità d’uso, qualità dei componenti disponibili
su Internet e, ora, anche la possibilità di ap- I moduli di DotNetNuke
poggiarsi ad un fornitore competente e pronto I moduli sono componenti software che ren-
a collaborare ai progetti di AWD Sim. Nell’au- dono disponibili funzionalità di base per la
tunno del 2005 AWD Sim rende disponibile alla gestione amministrativa di un portale. Di se-
Rete di Vendita i servizi principali richiesti dal guito vedremo alcuni dei moduli disponibili
Management. con l’applicazione.

N. 67 - Gennaio/Febbraio 2006 VBJ 51


WEB

• Account Login permette l’accesso profilato lità delle soluzioni disponibili per soddisfare
• Announcement rende disponibile la pre- esigenze molto diverse.
sentazione di annunci su una pagina
• Banners permette di mostrare banner agli Struttura di un modulo DotNetNuke
utenti che si sono loggati al sito. È possi- e tutorial
bile selezionare il numero e il tipo di ban- Il modulo è un componente software (assem-
ners da presentare bly dll). L’interfaccia WYSYWYG del modulo
• Contacts mette a disposizione le informa- offre una discreta autonomia ai redattori di te-
zioni anagrafiche per un gruppo di lavoro sti e di contenuti. Nei libri precedentemente
• Discussions rende disponibile la tipica la- citati lo sviluppo di un nuovo modulo è sem-
vagna di discussione (threaded discussion pre correlato alla possibilità di usare Visual
board) Studio 2003. Per sottolineare la relativa sem-
• Documents mette a disposizione una lista plicità di sviluppo di nuovi moduli mi limi-
di documenti, compresi i classici link e to ad usare NotePad e Visual Basic.NET, lin-
download guaggio con il quale è stato sviluppato tutto
• Events rende disponibili item che possono il codice del portale. È necessario possedere
scadere automaticamente una installazione funzionante di DotNetNuke.
• FAQ permette di gestire una lista di Fre- Il tutorial che segue descrive la costruzione
quented Asked Question e le risposte cor- dello sviluppo di un modulo che estrae da una
rispondenti tabella SQL Server l’ultimo messaggio da mo-
• FeedBack permette ai visitatori di inviare strare nel componente. Iniziamo a costruire
messaggi all’amministratore del portale la tabella all’interno, per esempio, del classi-
• IFrame, particolare per Microsoft Internet co database NorthWind. Di seguito è riporta-
Explorer, rende disponibili contenuti ap- to il codice T-SQL per la creazione della ta-
partenenti ad un altro sito web all’interno bella e della vista.
di un frame
• Image permette di mostrare immagini at- T-SQL
traverso il classico HTML IMG Tag // esempio di codice
• Links presenta una lista di link ad altri siti CREATE TABLE [dbo].[Messages] (
web [no_message] [int] NULL ,
• News Feeds (RSS) permette la distribuzione [message] [nvarchar] (100) COLLATE SQL_Latin1_General_
di syndicated news feed nel formato Rich CP1_CI_AS NULL
Site Summary ) ON [PRIMARY]
• Search Input abilita le ricerche nel sito web
e il corrispondente modulo Search Results CREATE VIEW dbo.vw_last_Message
permette di mostrare i risultati della ricerca AS
• Text/HTML permette di esporre porzioni SELECT message
di testo o HTML FROM dbo.Messages
• User Accounts abilita utenti profilati WHERE (no_message = (SELECT MAX(no_message) AS max_no_
• User Defined Table rende disponibili sul message FROM dbo.Messages))
sito dati tabellari
• XML/XLS permette di mostrare informa- Non mi soffermo, qui e in altre parti del-
zioni a partire da contenuti XML median- l’articolo, a valutare la necessità di imposta-
te trasformazioni XLS re indici e vincoli sulle tabelle, come a gesti-
re tutte le possibili eccezioni nel codice, per
Esistono moltissimi altri moduli addizionali. non appesantire la lettura. L’esecuzione de-
La carrellata permette di valutare la comples- gli script T-SQL con Query Analyzer produ-
sità della costruzione del portale e la flessibi- ce la tabella e la vista collega ad essa. Sem-

52 VBJ N. 67 - Gennaio/Febbraio 2006


WEB

pre con SQL Server Analyzer si possono in- Public ReadOnly Property ModuleActions() As ModuleAction
serire alcuni messaggi e analizzare l’output Collection Implements IActionable.ModuleActions
della vista. Per costruire l’interfaccia uten- Get
te è necessario preparare un file .ascx con- Dim actions As New ModuleActionCollection
tenente la label che mostrerà l’ultimo mes- actions.Add(GetNextActionID(), Localization.GetString
saggio della tabella. Il codice è riportato nel (ModuleActionType.AddContent, LocalResourceFile),
file ShowMessage.ascx visibile in Figura 1 e ModuleActionType.AddContent, “”, “”, EditUrl(), False,
nel file ShowMessage.ascx.resx ed entrambi DotNetNuke.Security.SecurityAccessLevel.Edit, True, False)
i file non presentano caratteristiche partico- Return actions
larmente interessanti. Passiamo al codice che End Get
estrae dalla tabella Messages il messaggio da End Property
mostrare nel modulo. Il file LastMessage.vb
è allegato all’articolo e di seguito illustriamo Il codice dell’estrazione del messaggio dalla
alcuni elementi interessanti. tabella è ‘standard’ MSDN per quanto riguar-
da l’utilizzo di ADO.NET.
Visual Basic .NET
// esempio di codice Installazione del modulo DotNetNuke
‘ Code adapted from DAL Builder Pro from www.dotnetnuke.dk e tutorial
Passiamo ora a documentare l’installazione.
Imports System È necessario creare il nuovo folder LastMes-
Imports System.Data sage sotto la directory DesktopModules del-
Imports System.Data.SqlClient l’installazione di DotNetNuke e aggiungere
Imports System.Drawing il file .ascx e .vb descritti sopra. All’interno
Imports System.Web.UI.WebControls di LastMessage si è creato il folder App_Lo-
Imports DotNetNuke calResources contenente il file .resx discusso
Imports DotNetNuke.Common prima. Possiamo ora loggarci a DotNetNuke
Imports DotNetNuke.Services.Search come Amministratori Host (user Host, per in-
Imports DotNetNuke.Entities.Modules tenderci) ed accedere alla maschera Module
Imports DotNetNuke.Entities.Modules.Actions Definitions del menu Host. Editiamo le de-
Imports DotNetNuke.Services.Localization finizioni del nuovo modulo avendo cura di
Imports DotNetNuke.Services.Exceptions compilare il text Folder Name con il nome
del folder costruito in precedenza. Digitiamo
Namespace LastMessage Update e l’applicazione ci presenta la masche-
ra delle Definitions della quale compiliamo il
Come si può vedere dal codice qui sopra è text New Definitions con il nome del modulo.
necessario importare molti package dell’appli- Digitiamo Add Definitions e l’applicativo ci
cativo DotNetNuke. Come si vede dalla porzio- presenta la maschera del Default Cache Time
ne di codice qui sotto è necessario definire la che impostiamo a zero (0) e digitiamo Upda-
classe ShowMessage che eredita dalla classe te Cache Properties. Finalmente giungiamo
di DotNetNuke PortalModuleBase ed imple- alla maschera del Control, che deve risultare
menta l’interfaccia IActionableImports vuota. Digitiamo Add Control e l’applicativo
presenta la maschera di Edit Control Module
Visual Basic .NET nella quale troviamo già impostati e text Mo-
// esempio di codice dule e Definition ed impostiamo: il text Title
Public Class ShowMessage con il nome del modulo; il list Source, sele-
Inherits PortalModuleBase zionando la posizione del nostro file .ascx; il
Implements IActionableImports System.Data list Type selezionando View. A questo punto
(….) il modulo è caricato nell’anagrafica dei mo-

N. 67 - Gennaio/Febbraio 2006 VBJ 53


WEB

duli di DotNetNuke e disponibile per essere Conclusioni


posizionato in una pagina di prova. Ritengo DotNetNuke un ottimo prodot-
Chiediamo all’applicativo di creare una nuo- to soprattutto perché è possibile imparare/
va pagina; digitiamo nella maschera del Pa- documentare dal codice sorgente rilasciato
ging Management i text Page Name, Page con l’applicativo e per quanto riguarda la se-
Title e Page Description secondo il proprio curity audit oltre che, ovviamemte, per quanto
gusto e impostiamo, dalla stessa maschera, riguarda il rapporto costo/rendimento dell’in-
in lato al centro, dalla view Module il nostro vestimento. Inoltre, come descritto preceden-
modulo LastMessage, avendo cura di aver se- temente, poiché il fornitore di software princi-
lezionato l’option Add New Module e il view pale di AWD Sim utilizza lo stesso applicativo
Pane a Content. Il processo di installazio- per il proprio portale non si prevedono proble-
ne è finito e DotNetNuke compila al volo il mi connessi a richieste improvvise di sviluppo
componente .ascx recuperando il file risorse o di produzione di workaround per la gestione
.resx e il codice .vb, il bin creato è eseguito di incompatibilità o bachi del softwre di base.
da ASP.NET che esegue il codice del Page_
Load, permettendo l’accesso a SQL Server Bibliografia
ed estraendo l’ultimo messaggio che è pas- [1] Shaun Walker ed altri – “Professional Dot-
sato alla label del modulo. In caso di errori NetNuke ASP.NET Portals”, Wrox, 2005
DotNetNuke imposta il content della pagina [2] Daniel N. Egan – “Building Websites with
di prova con la trace dell’errore, che ritengo VB.NET and DotNetNuke 3.0”, Packt, 2005
però quasi illeggibile. In ogni caso è possi-
bile zippare il folder per decomprimerlo nel- Riferimenti
l’ambiente di produzione ed effettuare l’in- [3] www.awd-italia.com
stallazione come sopra. [4] www.debugswt.it
[5] www.dotnetnuke.com

54 VBJ N. 67 - Gennaio/Febbraio 2006


APPLICAZIONI

Controllo Remoto
in Visual Basic .NET
Quarta puntata

di Stefano Corti

I
n questa quarta parte del nostro progetto ana- PRNTreport = “OK>>>”
lizzeremo un importante metodo della classe End If
RemoteProcesses, la cui trattazione è già stata Return PRNTreport
introdotta nella precedente puntata. Abbiamo vi- End Function
sto che questa classe fondamentale ci consente di
implementare metodi e proprietà per controllare e Definiamo un semplicissi-
monitorare processi in esecuzione sulla macchina mo metodo pubblico chiama-
remota. Riteniamo possa essere assai utile defini- to printPrintableFile() che
re anche un semplice metodo che permetta l’uti- restituisce un semplice valore
lizzo di un’eventuale stampante collegata alla po- di flag di tipo String se l’opera-
stazione di cui abbiamo il controllo mediante il no- zione di stampa è stata eseguita
stro programma. Come sempre il .NET Framework correttamente. Prima di tutto
ci viene incontro mediante proprietà appartenenti definiamo l’oggetto myp di tipo
alla propria Class Library. Diamo uno sguardo alla Process, dopo di ché non ci re-
seguente porzione di codice. sta che assegnare alla proprie-
tà FileName il valore di ppff
Public Function printPrintableFile(ByVal ppff As String) As String passato come argomento del-
Dim PRNTreport As String = Nothing la funzione medesima. Tale va-
Dim myp As New Process() lore dovrà contenere il percor-
Try so assoluto all’interno del file-
myp.StartInfo.FileName = ppff system della macchina remota
myp.StartInfo.CreateNoWindow = True che punta al file che si desidera
myp.StartInfo.Verb = “print” stampare. Impostiamo quindi a
myp.Start() True la proprietà CreateNoWin-
Catch pp As Exception dow, al fine di non determina-
PRNTreport = “Errore: “ & pp.Message re la comparsa di alcuna GUI
End Try sullo schermo del PC Target.
If PRNTreport = Nothing Then Molto importante è invece la
proprietà Verb che ci consente
di impostare il canale da utiliz-
Stefano Corti si occupa di programmazione PHP e JSP zare nell’apertura dell’applica-
lato server, della piattaforma .NET e di integrazione di
sistemi legacy con le nuove realtà del web, soprattutto in
zione o del documento specifi-
ambito gestionale, bancario e finanziario. È attualmente alle cato dalla proprietà FileName.
dipendenze di un primario gruppo bancario italiano. Può Se il percorso specificato ricon-
essere contattato via email: scorti@infomedia.it. duce ad un file di cui è possi-

N. 67 - Gennaio/Febbraio 2006 VBJ 55


APPLICAZIONI

bile la stampa, il proces- Listato 1 Struttura Select Case per la gestione del sistema di chat-line
so ha inizio e la stampan-
te predefinita verrà attiva-
ta, altrimenti sarà solleva- Case Is = “TEXT”
ta un’eccezione che il no- inMessages.Clear()
inMessages.Text &= job(1)
stro codice intercette-
If CheckBox1.Checked Then
rà mediante un generico Dim objPop As MessagePopUp = New MessagePopUp()
blocco Try Catch. objPop.Comingfrom = ControllerID.Text
objPop.Poptext = job(1)
Ora che abbiamo esami- objPop.PopUp()
nato gli aspetti più im- writer.Write(vbCrLf & “Messaggio inviato”)
End If
portanti della nostra clas-
se RemoteProcesses, pas- Case Is = “TEXTWITHREPLY”
siamo ad illustrare una inMessages.Clear()
inMessages.Text &= job(1)
semplice classe del no- Dim objPop As MessagePopUp = New MessagePopUp()
stro progetto che ci con- objPop.Comingfrom = ControllerID.Text
objPop.Poptext = job(1)
sente di instaurare com- objPop.PopReply()
plete sessioni di chat te- writer.Write(“REPTEXT>” & objPop.Replytext)
stuali tra Controller e Case Is = “TEXTERRORMESSAGE”
Target. Riteniamo infatti inMessages.Clear()
importante poter effettua- inMessages.Text &= job(1)
Dim objPop As MessagePopUp = New MessagePopUp()
re uno scambio di messag- objPop.Comingfrom = ControllerID.Text
gi tra gli utenti delle due objPop.Poptext = job(1)
objPop.PopError()
macchine, al fine di ren- writer.Write(vbCrLf & “Messaggio inviato”)
dere le operazioni di assi-
stenza maggiormente in-
terattive e senza spreco di ulteriori risorse End Sub
tecniche, come ad esempio l’utilizzo del tele-
fono cellulare laddove la connettività di uno Public Sub PopError()
dei due utenti sia del tipo dial-up. In detta- fPrompt &= oPromptTitle
glio, il Controller sarà in grado di inviare su MessageBox.Show(oMessage, fPrompt,
Target ogni genere di messaggio, che com- MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
parirà sotto forma di una finestra di pop-up, End Sub
anche qualora la GUI Target sia stata ridotta
ad icona nella Systray. Tali pop-up potranno Come possiamo notare si tratta di tre proce-
essere di tipo meramente informativo, di er- dure Sub (quindi non restituiscono alcun valo-
rore oppure con un campo di testo vuoto per re) che si occupano semplicemente di visualiz-
permettere l’inserimento di una risposta di ri- zare altrettante tipologie di Pop-Up contenen-
torno. Vediamo i tre metodi principali. ti i relativi messaggi testuali. Questi metodi
provvedono alla visualizzazione del messag-
Public Sub PopUp() gio leggendo la stringa testuale dalla variabile
fPrompt &= oPromptTitle privata oMessage, di tipo String. La scritta che
MessageBox.Show(oMessage, fPrompt, comparirà nella barra del titolo della finestra
MessageBoxButtons.OK, MessageBoxIcon.Information) viene invece ricavata dalla variabile oPrompt-
End Sub Title. La variabile oMessage viene imposta-
ta per mezzo della proprietà Poptext, mentre
Public Sub PopReply() la proprietà di sola lettura Replytext consen-
fPrompt &= oPromptTitle te al ciclo principale del server di leggere la
oReply = InputBox(oMessage, fPrompt) stringa testuale memorizzata nella variabile

56 VBJ N. 67 - Gennaio/Febbraio 2006


APPLICAZIONI

Listato 2 Procedura Sub di implementazione server per trasferimento file

Public Sub runReceiveFile()


Try
Dim f_listener As TcpListener
f_listener = New TcpListener(13010)
While True
f_listener.Start()
inMessages.Text &= vbCrLf & “PC pronto per accettare UPLOAD da Controller...” & vbCrLf
f_connection = f_listener.AcceptSocket()
f_socketStream = New NetworkStream(f_connection)
f_writer = New BinaryWriter(f_socketStream)
f_reader = New BinaryReader(f_socketStream)
Dim buffer(1024) As Byte
Dim count As Integer
Dim finalFile As String = objTrans.DirectoryDeposit & “\” & objTrans.FileToUpLoad
Dim outfile As FileStream = File.OpenWrite(finalFile)
Try
count = f_reader.Read(buffer, 0, 1024)
While (count > 0)
outfile.Write(buffer, 0, count)
count = f_reader.Read(buffer, 0, 1024)
End While
Catch ef As Exception
sentText.Clear()
writer.Write(“Errore durante l’UpLoad del file: “ & _
ef.Message)
sentText.Text = vbCrLf & “Errore nella ricezione del file.”
Finally
outfile.Close()
f_reader.Close()
f_writer.Close()
f_socketStream.Close()
f_connection.Close()
End Try
objTrans.TransStatus = “OFF”
writer.Write(vbCrLf & “File trasferito con successo!”)
sentText.Text &= vbCrLf & “File trasferito con successo!”
End While
Catch en As Exception
inCommands.Text = vbCrLf & “Errore: “ & en.Message & “ !!!”
End Try
End Sub

privata oReply, quale valore stringa restitui- Get


to dalla funzione InputBox() all’interno del- Return oReply
la procedura Sub PopReply(). End Get
End Property
Public Property Poptext() As String
Get Occupiamoci ora di gestire queste funziona-
Return oMessage lità all’interno del ciclo principale del server.
End Get Esaminiamo la porzione di codice visibile nel
Set(ByVal Value As String) Listato 1. Abbiamo previsto tre possibili co-
oMessage = Value mandi del nostro protocollo applicativo che
End Set consentono alle due macchine di dialogare. In
End Property tutti e tre i casi viene creato l’oggetto objPop,
istanziando la classe MessagePopUp. In que-
Public ReadOnly Property Replytext() As String sto contesto, il valore stringa di job(1) contiene

N. 67 - Gennaio/Febbraio 2006 VBJ 57


APPLICAZIONI

il messaggio testuale inviato dall’applicativo mento che l’analoga operazione risulterà ov-
Controller, che viene immediatamente pas- viamente molto simile ed intuitiva su Con-
sato alla proprietà Poptext. Successivamen- troller. Passiamo subito all’esame del Listato
te viene invocato il metodo che determina la 2. Come possiamo facilmente notare, questo
comparsa della relativa finestra. Osserviamo nuovo server è in ascolto sulla porta 13010.
anche che, nel caso in cui il Controller tra- Abbiamo scelto infatti un numero di porta
smetta un messaggio con risposta di ritorno differente, in quanto vogliamo mantenere
obbligatoria, tale risposta possa essere indi- completamente separato il flusso di coman-
viduata leggendo la proprietà di sola lettura di del nostro protocollo applicativo, dai flus-
Replytext, come abbiamo visto precedente- si binari che rappresentano i byte trasmessi
mente. Il valore stringa ritornato da questa del nostro file. Per il resto la progettazione
proprietà viene quindi immesso nel socket del codice non differisce molto da ciò che ab-
di comunicazione preceduto dal comando ap- biamo già implementato nelle prime puntate.
plicativo <REPTEXT>. Come abbiamo det- Ricordiamo che anche l’esecuzione di questo
to, si tratta di una classe estremamente sem- server viene inserita in un apposito Thread
plice, la cui trattazione non merita ulteriori il cui ciclo di vita viene inizializzato nel co-
approfondimenti. struttore della form dopo la chiamata a Ini-
Incominciamo ora a
parlare di una funziona-
Listato 3 Procedura Sub di realizzazione del client per il trasferimento file
lità molto importante del
nostro progetto, ovvero
la possibilità di sposta-
Public Sub beginToUpLoad(ByVal fileUp As String)
re qualunque tipo di file Dim t_client As TcpClient
tra i due end-point. An- Dim ip As String = inIP.Text
Dim port As Int32 = 13010
che in questo caso do- t_client = New TcpClient()
vremo provvedere a vei- t_client.Connect(ip, port)
colare i flussi binari di t_output = t_client.GetStream()
t_writer = New BinaryWriter(t_output)
byte, utilizzando come t_reader = New BinaryReader(t_output)
sempre TCP/IP e preve- ProgressBar1.Visible = True
ProgressBar1.Minimum = 1
dendo un’architettura di ProgressBar1.Maximum = CInt(FileLen(fileUp) / 120)
tipo client-server. Imple- ProgressBar1.Value = 1
Dim buffer(1024) As Byte
menteremo quindi appo-
Dim count As Integer
site procedure Sub che Dim infile As FileStream = _
implementeranno tutto File.Open(fileUp, FileMode.Open, FileAccess.Read)
Try
il codice necessario per count = infile.Read(buffer, 0, 1024)
porre in ascolto su de- While count > 0
t_writer.Write(buffer, 0, count)
terminate porte due ele- count = infile.Read(buffer, 0, 1024)
mentari server, sia su ProgressBar1.PerformStep()
Controller, sia su Target, End While
Catch ef As Exception
dal momento che deside- inMessages.Text &= “Errore durante l’UpLoad del File: “ & _
riamo progettare un si- vbCrLf & ef.Message
Finally
stema che ci consenta di infile.Close()
trasferire file in entram- t_writer.Close()
t_reader.Close()
be le direzioni. In questa t_output.Close()
puntata ci concentrere- t_client.Close()
mo particolarmente sul- inMessages.Text &= vbCrLf & “FILE TRASFERITO A PC REMOTO.”
End Try
l’implementazione del End Sub
server su Target, dal mo-

58 VBJ N. 67 - Gennaio/Febbraio 2006


APPLICAZIONI

tializeComponent() in modo del tutto analo- ByVal buffer() As Byte, _


go al server principale. ByVal indexAs Integer, _
ByVal countAs Integer_
Public Sub New() ) As Integer
MyBase.New()
Il metodo restituisce quindi un valore di
InitializeComponent() tipo Integer e accetta tre parametri: un array
‘Aggiungere le eventuali istruzioni di inizializzazione di tipo Byte che rappresenta il buffer in cui
dopo la chiamata a InitializeComponent() leggere i dati; un valore Integer, quale punto
di partenza nel buffer da cui avviare la lettu-
rThread = New Thread(AddressOf runServerTarget) ra nel buffer stesso ed infine ancora un valo-
fThread = New Thread(AddressOf runReceiveFile) re Integer contenente il numero di caratteri
rThread.Start() da leggere. Il ciclo While più interno control-
fThread.Start() la costantemente che il valore di count sia
End Sub maggiore di zero, poiché in caso contrario è
stata certamente raggiunta la fine dell’ultimo
Quando la form viene visualizzata sullo blocco di byte del nostro file. All’interno del
schermo Target, questi due Thread sono già ciclo andremo quindi a porre il codice che ci
attivi e in ascolto sulle rispettive porte. Ab- serve per scrivere il file che stiamo estraendo
biamo inoltre previsto che i byte che compon- dal socket di flusso e assegnare un nuovo va-
gono il file da copiare non vengano trasmessi lore alla variabile count, affinché il ciclo pos-
uno alla volta, ma raggruppati in blocchi da sa ripetersi con la lettura del nuovo segmento
1024 byte ciascuno. Questo consente una più bufferizzato, oppure, in caso contrario, passa-
razionale segmentazione del flusso binario e re l’esecuzione al blocco Finally che si occupa
una più corretta bufferizzazione del procedi- di rilasciare tutte le risorse attivate.
mento stesso. Creiamo quindi l’array buffer,
di tipo byte, impostando la dimensione a 1024. Try
Il passo successivo consiste del creare un og- count = f_reader.Read(buffer, 0, 1024)
getto di tipo FileStream che riceverà il valo- While (count > 0)
re restituito dal metodo statico OpenWrite() outfile.Write(buffer, 0, count)
della classe File. Questo metodo, che restitui- count = f_reader.Read(buffer, 0, 1024)
sce ovviamente un valore di tipo FileStream, End While
apre un file esistente inizializzandolo per la Catch ef As Exception
relativa scrittura e accetta come parametro un sentText.Clear()
valore stringa che può contenere il percorso writer.Write(“Errore durante l’UpLoad del file: “ &
completo del file da scrivere su disco, inclu- ef.Message)
so il nome stesso del file e l’eventuale esten- sentText.Text = vbCrLf & “Errore nella ricezione del file”
sione. La classe FileStream viene utilizzata Finally
per leggere, scrivere, aprire e chiudere file outfile.Close()
su un file-system. Nel successivo blocco Try f_reader.Close()
passiamo alla variabile Integer count il valore f_writer.Close()
restituito dal metodo sovraccarico Read() del- f_socketStream.Close()
la classe BinaryReader() di cui abbiamo già f_connection.Close()
parlato in precedenza. In questo contesto però End Try
utilizziamo tre parametri previsti da un caso
di overload, di seguito esemplificato: Molto importante è ovviamente il meto-
do Write() per l’oggetto outfile di tipo File-
Overloads Public Overridable Function Read( _ Stream. Questo metodo di overload scrive ef-

N. 67 - Gennaio/Febbraio 2006 VBJ 59


APPLICAZIONI

fettivamente i dati su disco, utilizzando il pri- anche in questo caso un array di tipo Byte,
mo parametro come matrice in cui vengono impostando la dimensione massima a 1024
scritti i byte. Il valore 0 è in pratica la posi- e apriamo il file con il metodo Open() della
zione di offset dei byte da cui iniziare la scrit- classe File, che ancora una volta restituisce
tura e count rappresenta il numero massimo un valore di tipo FileStream. Quindi in un ci-
dei byte da scrivere, ovvero il valore Integer clo While del tutto analogo a quello previsto
letto precedentemente dallo stream binario per il server, scriviamo all’interno del socket
con il metodo Read(), già discusso. Nel Lista- di flusso precedentemente stabilito i blocchi
to 3 è invece visibile il codice di implemen- di byte che, ad opera dei servizi esposti dai
tazione del client relativo al processo appe- livelli più bassi del modello ISO/OSI, di cui
na descritto. A parte la gestione di una Pro- non ci occupiamo, dovranno raggiungere at-
gressBar che mostra lo stato di avanzamento traverso la rete, qualunque essa sia, la mac-
del trasferimento in corso, il codice non pre- china Target per essere nuovamente estratti
senta particolari aspetti degni di attenzione. e scritti su disco per ottenere una copia esat-
In pratica la funzione accetta un parametro ta del file di partenza. Nella prossima pun-
stringa che contiene il percorso assoluto al- tata concluderemo la trattazione delle funzio-
l’interno del file system del Controller dove ni e dei metodi per il trasferimento dei file e
si trova il file che desideriamo inviare alla vedremo come implementare un elementare
macchina Target. Successivamente creiamo sistema di autenticazione.

60 VBJ N. 67 - Gennaio/Febbraio 2006


.NET TOOLS
.NETMon eseguite e il code coverage, vale a dire analizzare
Il flow tracing senza sforzo se effettivamente tutte le righe di un determi-
di Fabio Perrone nato metodo vengono eseguite. Per eseguire il
flow tracing, il .NET Framework mette a nostra
Uno strumento gratuito per gestire il flow disposizione le Profiling API, uno strumento po-
tracing di applicazioni .NET tente ma alquanto complesso che richiede una
buona conoscenza di C++ e di tecniche avanza-
Alzi la mano chi, durante lo sviluppo di un pro- te di debugging (per chi fosse interessato con-
getto particolarmente complesso, non si è mai siglio l’ottimo “Debugging Applications for Mi-
posto il seguente problema: “Ho modificato il crosoft .NET and Microsoft Windows” di John
metodo pubblico X della classe Y, chissà quali Robbins). Per chi non avesse le conoscenze ne-
altri metodi del mio progetto lo utilizzano?”. cessarie o desiderasse uno strumento già “pron-
A causa di problematiche legate ai tempi di con- to all’uso”, .NETMon della Foundstone costitui-
segna dei progetti software, i test di regressio- sce un’ottima soluzione, poiché permette di ef-
ne sovente vengono trascurati, provocando poi fettuare il flow tracing sia a livello di classe che
gravi danni quando un applicativo viene messo a livello di metodo.
in produzione. Una semplice ricerca in tutta la Il primo aspetto importante da evidenziare è
soluzione non è quasi mai sufficiente, quindi è che .NETMon lavora sugli eseguibili, il che si-
necessario utilizzare tecniche di flow tracing. Il gnifica da un lato un inevitabile rallentamento
flow tracing ha una doppia funzionalità: scopri- delle prestazioni durante l’esecuzione della no-
re tutte le righe di un’applicazione che vengono stra applicazione all’interno di .NETMon, ma dal-
l’altro lato l’enorme van-
taggio che non devono es-
sere fatte modifiche al co-
dice sorgente della nostra
applicazione. Il suo utiliz-
zo sia su applicazioni Win-
dows Forms che Web For-
ms (anche se in queste ul-
time sono necessari alcu-
ni successivi accorgimen-
ti quali l’impostazione di
variabili d’ambiente) ren-
de questo tool molto appe-
tibile qualunque sia la ti-
pologia di progetto su cui
si sta lavorando.
Per poter fornire un con-
trollo maggiore su ciò
che deve essere tracciato

N. 67 - Gennaio/Febbraio 2006 VBJ 61


.NET TOOLS

e quando deve essere tracciato, .NETMon per- popoli eventualmente qualche tabella di data-
mette di impostare una serie di valori che aiu- base per poter effettuare ricerche più avanzate,
tano a scegliere se mostrare o nascondere de- come conoscere da quali assembly viene richia-
terminati assembly, namespace o metodi; que- mato un metodo, quante volte un metodo viene
sta funzionalità è assolutamente indispensabile richiamato e così via.
perché, ovviamente, il programma tiene traccia Per chi è particolarmente curioso, potrebbe es-
di tutti i metodi che vengono eseguiti, compre- sere interessante dare un’occhiata a quanti e qua-
si i metodi del CLR. li metodi del CLR vengono richiamati durante
È importante ricordare che all’avvio di una l’esecuzione di una nostra applicazione mana-
nostra applicazione il CLR esegue una grande ged, permettendo di capire un po’ più a fondo
quantità di metodi d’inizializzazione, che pos- quel bellissimo ma assai ampio mondo che è il
sono anche non essere tracciati grazie appun- .NET Framework.
to alla proprietà “Initial Delay”. Tale proprietà A corredo dell’applicazione vengono forniti alcu-
permette di impostare il ritardo iniziale di tra- ni esempi per familiarizzare con le impostazioni
cing dei metodi, cioè è un contatore che indica dell’applicazione, nonché una buona documen-
a .NETMon quando iniziare ad effettuare il tra- tazione in formato pdf.
cing dell’applicazione. Per impostare un valore Nonostante un leggero sforzo iniziale e l’inter-
corretto di questa proprietà sono necessarie un faccia grafica non proprio accattivante, il tool
po’ di prove, ma il grande vantaggio è che, una della Foundstone ripaga ampiamente fornendo
volta trovato un valore ottimale, è possibile ini- uno strumento gratuito di fondamentale impor-
ziare a seguire la nostra applicazione senza at- tanza per qualsiasi tester che desideri controlla-
tendere troppo tempo mentre il CLR esegue tut- re un’applicazione in modo più approfondito uti-
te le sue inizializzazioni. lizzando il flow tracing o sviluppatore che voglia
Per applicazioni che utilizzano pesantemente conoscere più a fondo le parti intrinseche di qual-
il multithreading, è possibile ordinare i meto- siasi applicazione. È necessario ricordare che pur-
di per thread, in modo da creare un tracing se- troppo la versione attuale prevede la piena com-
parato per ogni thread. Com’è lecito aspettarsi, patibilità solo con il .NET Framework 1.1.
è possibile interrompere l’analisi del flusso in
qualsiasi momento premendo sull’apposito but-
ton “STOP”. Prodotto
.NETMon
Il vero valore di questi strumenti senza dubbio
non è soltanto la possibilità di eseguire il flow Url di riferimento
http://www.foundstone.com/index.htm?subnav=resources/navigat
tracing, quanto fornire al tester i dati ottenuti ion.htm&subcontent=/resources/proddesc/dotnetmon.htm
in modo da poterli manipolare in qualche modo,
Stato Release
mettendo a disposizione dei valori su cui è pos- Stabile
sibile eseguire ulteriori studi, quali per esem-
Semplicità d’uso 
pio capire da chi è stato chiamato un determi- Per poter ottimizzare le impostazioni dell’applicazione è necessa-
nato metodo. rio conoscerla un po’ a fondo
I dati ci vengono quindi forniti o in una Text- Utilità 
box nell’ordine in cui i metodi vengono esegui- Strumento molto importante per chi desidera utilizzare il flow
ti o in un TreeView che organizza le funzioni in tracing di applicazioni
base alle chiamate annidate che vengono effet- Qualità prodotto 
tuate, escludendo eventualmente il tipo di ritor- Affidabile, nei test sul prodotto eseguiti non ha mai mostrato par-
no d’ogni metodo e il nome del namespace: que- ticolari problemi

sto risulta uno strumento fondamentale grazie Qualità documentazione 


al fatto che è possibile esportare i dati del Tree- Il file di documentazione allegato permette di comprendere tutte
le funzionalità del programma (in lingua inglese).
View in un file XML e quindi preparare un pic-
colo programma che legga questo file XML e

62 VBJ N. 67 - Gennaio/Febbraio 2006


.NET TOOLS

 InputFile, un control custom con caratteri-


NeatUpload stiche avanzate rispetto all’HtmlInputFile in
di Raffaele Di Natale quanto ci permette di accedere al file tempo-
raneo in fase di upload, così come al nome ori-
Un tool open source per eseguire l’upload di ginario del file e al suo tipo;
un file e di monitorarne l’avanzamento  ProgressBar, un control custom che ha il com-
pito di mostrare lo stato di avanzamento del-
Il numero degli utilizzatori di internet ad alta ve- l’upload oltre a comandarne l’interruzione. Può
locità cresce giorno dopo giorno e sul web si mol- essere inserito all’interno di una pagina oppu-
tiplicano i servizi orientati alla manipolazione di re richiamato in una finestra popup. Inoltre,
file di dimensioni sempre crescenti. Sebbene la nel caso in cui JavaScript non risulti abilitato
posta elettronica risulti ancora uno dei metodi più oppure il browser non supporti gli IFrame e la
immediati per inviare simili file (si veda G-mail, ProgressBar è inserita inline nel documento,
Alice…), qualche piccolo inconveniente potrebbe essa apparirà come un semplice link tramite il
presentarsi se si volesse mettere a disposizione dei quale potrà essere visualizzata la finestra po-
visitatori di un dato sito web, un servizio di reposi- pup contenente la barra di avanzamento;
tory di file con annessa funzionalità di upload. In-  Progress.aspx: la presenza di una ProgressBar
fatti, quando si pensa di inviare file di centinaia di richiama la pagina progress.aspx che ne effet-
MByte, se non di GByte, il primo problema da af- tuerà l’aggiornamento, in base alle indicazioni
frontare è quello di monitorare tale operazione di provenienti dall’UpLoadHttpModule;
trasferimento. Quante volte, ad esempio, provando  UpLoadHttpModule, è il modulo principale
ad effettuare l’attach di un file in un messaggio di dell’applicazione ed ha il compito di intercet-
posta elettronica web-based, abbiamo l’impressio- tare le richieste, gestire l’upload ed effettuare
ne che il trasferimento stia fallendo perché col pas- i controlli sulle dimensioni dei file.
sare del tempo non riceviamo alcun messaggio di
‘conforto’ dall’applicazione? NeatUpload, mediante Prerequisiti e Installazione
un opportuno control inserito in una pagina web – NeatUpload è un’applicazione ASP.Net multi-
ovvero in una finestra popup – restituisce un fee- piattaforma in quanto supporta sia il framework
dback continuo all’utente e ne verifica tranquilla- .NET che Mono. È stata testata utilizzando Win-
mente l’attività di streaming anche e soprattutto dows XP SP2 con Internet Information Services
relativamente a file di elevate dimensioni, per i [IIS] e .NET Framework 2.0.
quali il tempo di trasferimento potrebbe risultare Dopo aver scaricato da [2] ed estratti i file rela-
particolarmente lungo. tivi all’applicazione, gli scenari possibili sono es-
senzialmente due: il primo è rappresentato dal-
L’applicazione la possibilità di utilizzare direttamente l’appli-
NeatUpload è una Web Application sviluppata cazione mediante una pagina dimostrativa ap-
in C# che fa uso dei seguenti moduli: positamente sviluppata; la seconda è quella di
integrare NeatUpload all’interno di un’applica-
 Brettle.Web.NeatUpload.dll, il modulo princi- zione già esistente.
pale dell’applicazione; Nel primo caso è sufficiente configurare Nea-
 Log4Net.dll, per il log delle informazioni di de- tUpload come WebApplication nell’ambiente IIS.
bug [1]. Successivamente, per testarne le funzionalità ba-
sterà accedere alla pagina dimostrativa denomi-
Le dll sono distribuite sia in modalità debug sia nata appunto demo.aspx.
release. Alla base di NeatUpload vi sono quattro Tramite questa pagina, dopo aver selezionato
elementi che possono essere utilizzati ed organiz- un file e cliccato sul pulsante Submit, il file se-
zati a nostro piacimento, oltre ad essere facilmen- lezionato verrà copiato all’interno della cartel-
te modificabili e quindi personalizzabili: la temporanea di sistema (normalmente in C:

N. 67 - Gennaio/Febbraio 2006 VBJ 63


.NET TOOLS

\Documents and Settings\NOMEPC\ASPNET\


Impostazioni locali\Temp). [assembly: log4net.Config.XmlConfigurator(ConfigFile=”log4net.
Completato l’upload il file sarà immediatamente config”, Watch=true)]
eliminato: ovviamente ciò non accadrà in un’ap-
plicazione reale. In Figura1 è illustrata la pagina Se eseguiamo la nostra applicazione in moda-
demo.aspx ed evidenziata la ProgressBar conte- lità debug o release dovremo copiare Breattle.
nuta all’interno della finestra popup. Una demo Web.NeatUpload.dll, in versione debug o relea-
online dell’applicazione è inoltre disponibile al- se, rispettivamente nella cartella bin di debug
l’indirizzo [3]. o release dell’applicazione finale. Copiamo infi-
ne l’intero contenuto della cartella NeatUpload
Integrare NeatUpload (NeatUpload-1.0/NeatUpload/) all’interno di una
ad un’applicazione esistente sottodirectory omonima, opportunamente creata
La pagina dimostrativa serve solo a testare le nella cartella relativa all’applicazione principale.
funzionalità di base dell’applicazione, ma è chia- Infine, per poter utilizzare il modulo UpLoadHt-
ro che NeatUpload è pensata per essere integra- tpModule si dovrà aggiungere al Web.Config, sot-
ta ad un’applicazione già esistente o in fase di to la sezione configuration/system.web/httpmodu-
sviluppo. Per cominciare osserviamo che anche les, la seguente riga:
NeatUpload, così come altri tool .NET descritti in
questa rubrica, utilizza Log4Net [1] che può es- <add name=”UploadHttpModule” type=”Brettle.Web.NeatUpload.Uploa
sere configurato per ottenere interessanti infor- dHttpModule, Brettle.Web.NeatUpload” />
mazioni di debug, specialmente nella prima fase
di integrazione e sviluppo dell’applicazione. Una Prima di passare a sviluppare una pagina ad hoc
copia di Log4Net.dll è presente nella cartella …/ per la nostra applicazione è conveniente testare
bin di NeatUpload in formato release e debug, op- l’installazione copiando ed eseguendo la pagina
pure può essere ottenuta seguendo le indicazioni dimostrativa demo.aspx.
date in [1]. Dopo aver copiato Log4Net.dll all’in-
terno della cartella …/bin della nostra applicazio- Personalizzazione
ne non resta che modificare il file Global.asax.cs Se non si hanno particolari esigenze, le uni-
inserendo la seguente riga di codice C#: che modifiche necessarie per utilizzare NeatU-

64 VBJ N. 67 - Gennaio/Febbraio 2006


.NET TOOLS

pload in maniera ottimale riguardano l’imposta-


zione della cartella temporanea dei file caricati, <Upload:ProgressBar id=”progressBar” runat=”server” >
l’impostazione della dimensione massima dei file <asp:Label id=”label” runat=”server” Text=”Your Text Here”/>
in upload, lo spostamento del file caricato ver- </Upload:ProgressBar>
so una directory definitiva (il repository vero e
proprio) ed il testo da visualizzare come link nel sarà sufficiente modificare il campo Text inse-
caso in cui il browser impedisca la corretta vi- rendo il testo desiderato che costituirà il link in
sualizzazione della ProgressBar all’interno del- caso di mancata visualizzazione della Progres-
la pagina. Il codice della pagina demo.aspx si sBar.
presta ad essere modificato facilmente e in ogni Conclusioni
caso è sempre possibile seguire i suggerimenti NeatUpload è una WebApplication efficace e
presenti in manual.html della cartella docs del- senza fronzoli. Bene si adatta, grazie alla modu-
l’applicazione. Le prime due modifiche avvengo- larità che la contraddistingue, alle svariate esi-
no inserendo due righe all’interno della sezione genze delle applicazioni web che puntano a ge-
configuration/appSettings di Web.Config. Per im- stire streaming significativi di file. Sicuramente
postare una directory temporanea che ospiterà i l’interfaccia è migliorabile, ma se si esclude que-
file caricati si inserirà sto aspetto bisogna riconoscere che le aspettati-
ve non restano deluse.
<add key=”NeatUpload.DefaultTempDirectory” value=”TempDir” />
Riferimenti
Per limitare la dimensione massima di un file [1] D. Mauri - “. Net Tools”, Visual Basic Journal,
basta inserire n.52, Lug/Ago 2003
[2] http://www.brettle.com/neatupload
<add key=”NeatUpload.MaxRequestLength” value=”DimKbytes” /> [3] http://www.brettle.com/Demo.aspx

Nel caso in cui il file superi la dimensione mas-


sima consentita, NeatUpload genererà un’ecce-
zione HttpException(413, “Request Entity too
large”) che deve essere intercettata e gestita dal-
Prodotto
NeatUplaod
l’applicazione chiamante, altrimenti il file sarà
semplicemente ignorato senza alcun avvertimen- Url di riferimento
http://sourceforge.net/projects/mail2rss
to. Facendo riferimento al file demo.aspx, se vo-
gliamo spostare definitivamente il file caricato Stato Release
v. 1.0.16
verso una data cartella, ad esempio la cartella
principale dell’applicazione, è necessario modi- Semplicità d’uso 
Decisamente ottima la versione dimostrativa dell’applicazione,
ficare il blocco di codice relativo alla funzione buona la sua integrazione con applicazioni esistenti.
membro Page_PreRender come di seguito:
Utilità 
Chi ha in mente di sviluppare una funzionalità di streaming per la
if (this.IsPostBack) propria applicazione web trova in NeatUpload senz’altro la solu-
{ zione a tutti i suoi problemi presenti e futuri grazie alla sua ver-
satilità
if (inputFile.TmpFile != null)
{ Qualità prodotto 
È un prodotto di ben fatto e nei test effettuati non sono stati ri-
inputFile.TmpFile.MoveTo(Path.Combine(Request.
scontrati problemi.
PhysicalApplicationPath, Path.GetFileName(inputFile.FileName)));
}
Qualità documentazione 
La documentazione è in inglese ma è molto chiara e dettagliata,
ma soprattutto disporre di una pagina dimostrativa efficace rap-
Infine, facendo considerando il codice relativo presenta la documentazione più eloquente.
alla ProgressBar

N. 67 - Gennaio/Febbraio 2006 VBJ 65