v2006 01 vbj67
TRANSCRIPT
VBJ 67
IN OFFERTA VBJ 67
Scrivi a
specificando
nell’oggetto della
e-mail:
IN OFFERTAVBJ n. 67
OPPURE
inviaci il coupon
sottostante
al numero di fax
0587/732232
Potrai acquistare
i libri qui riportati con
uno
SCONTOECCEZIONALE
del 10% anche se
acquisti solo un libro
OPPURE
del 20% se acquisti
3 libri
ADO.NET and System.Xml v. 2.0-The Beta Version, Edition 2
di A. Homer et al.
Addison WesleyISBN 0321247124 560 pp - 39,95 €
Cryptography in the Database
The Last Line of Defensedi K. Kenan
Prentice HallISBN 0321320735
312 pp - 39,95 €
Applicare UML e i pattern Analisi e progettazione orientata agli oggetti
3a Ed.di C. Larman
Addison Wesley ItaliaISBN 8871922700768 pp - 43,00 €
A First Look at SQL Ser-ver 2005 for Developersdi B. Beauchemin et al.
Addison WesleyISBN 0321180593736 pp - 46,95 €
Microsoft SQL Server 2005Changing the Paradigm (SQL Server 2005 Public
Beta Edition)
SamsISBN 0672327783 504 pp - 34,70 €
Using UML:Software Engineering with Objects and Compone, Edition 2
di P. Stevens
Addison WesleyISBN 0321269675 272 pp - 57,95 €
E D I T O R I A L E
N. 67 - Gennaio/Febbraio 2006 VBJ 5
Guerre informatichedi religione
Una delle cose più belle della comunità degli sviluppatori nel mondo è di essere animata da passioni ferventi, come 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 difetti del computer avversario. La disputa si spostò presto sul confronto Amiga-Atari-Apple, e dopo ancora IBM PC vs Macintosh. Quest’ultima è probabilmente la querelle di più lunga durata, visto che è continuata immutata fino ad oggi, anche se ora la materia del contendere si è spostata sul sistema operativo.Nel campo del software la discussione è stata persino più accesa. Prima erano i dialetti del Basic, poi la contrapposizione C vs Pascal, poi C e C++, Visual Basic e Delphi, SQL Server e Oracle, Windows e Linux, e più recentemente le piattaforme .NET e Java. Se volete avere dei dati che supportino le vostre convinzioni, fatevi un giro in Internet e troverete sicuramente dei benchmark che vi garantiscono che il linguaggio-sistema operativo-database che preferite è assolutamente migliore di quello che invece detestate, con buona pace di tutti. Forse è l’eta, forse il fatto di avere la sensazione di aver già visto questo film altre volte, ma non riesco più ad appassionarmi a queste battaglie. O comunque, cerco di non per-dere mai di vista il fatto che, molto spesso, dietro le community di persone appassionate 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.Quando leggete i risultati di un case-study o una survey che mette a confronto due tec-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 dimostrare tutto e il contrario di tutto. Ad esempio, non è difficile provare che in alcune configurazioni Access sia molto più veloce di SQL Server o Oracle, oppure che VB4 sia più performante di C# o Java. Controllate sempre i dettagli di questo tipo prima di prendere le conclusioni come buone.E quando vi imbattete in dichiarazioni di IBM, Sun o Oracle che promuovono Java e Linux come espressioni del “software libero” contro lo strapotere Microsoft, andatevi a rileggere le cronache degli anni ‘70, quando IBM aveva il monopolio assoluto in questo settore. 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 dell’altro. Ma quello è un problema vostro...
Francesco [email protected]
o n l i n e . i n f o m e d i a . i tn. 67 - gennaio/febbraio 2006bimestrale - anno dodicesimo
Direttore Re spon sa bi leMarialetizia Mari ([email protected])
Direttore EsecutivoFran ce sco Balena ([email protected])
Managing EditorRenzo Boni ([email protected])
CollaboratoriStefano Corti
Raffaele Di Natale Andrea Ferendeles
Fabio Perrone Scott Swigart
Lorenzo Vandoni Pietro Vite
Direzione Natale Fino ([email protected])
Marketing & Advertising Segreteria: 0587/736460
AmministrazioneSara Mattei
GraficaManola Greco ([email protected])
Technical BookLisa Vanni ([email protected])
Segreteria Enrica Nassi
StampaTIPOLITOGRAFIA PETRUZZI
Citta’ di Castello (PG)
Ufficio AbbonamentiTel. 0587/736460 - Fax 0587/732232e-mail: [email protected]
www.infomedia.it
Gruppo Editoriale Infomedia srlVia Valdera P., 116 - 56038 Ponsacco (PI) Italia
Tel. 0587/736460 - Fax 0587/[email protected]
Sito Web www.infomedia.it
Manoscritti e foto originali anche se non pub bli ca ti, non si restituiscono. È vietata la ri pro du zio ne
anche parziale di te sti e immagini.
Si prega di inviare i comunicati stampa e gli inviti stampa per la redazione all’indirizzo: [email protected]
Visual Basic Journal è una rivista diGruppo Editoriale Infomedia S.r.l. Via Valdera P, 116 Ponsacco - Pisa.
Registrazione presso il Tribunale di Pisa n. 20/1999
9
SOMMARIO
N. 67 - Gennaio/Febbraio 2006 VBJ
N.67G E N N A I O / F E B B R A I O
10Tdo - Typed Data Object 2.0Tdo – 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
SPECIALE
Editoriale 5
.NET Tools 61
RUBRICHE
34Multithreading semplice e robusto in applicazioni Windows Forms 1.1 e 2.0Chi 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
File compatti e illeggibili - Crittografia (prima puntata)Crittografia e compressione di file in VB6 utilizzando il Microsoft .NET Framework.
di Scott Swigart, Swigart Consulting LLC.
Executable UMLUML (xUML) è un formalismo che consente di applicare concretamente le linee guida fornite dalla Model Driven Architecture (MDA).
di Lorenzo Vandoni
TECNICHE
VB6
40
SOFTWARE ENGINEERING
44
DotNetNuke: creazione di un moduloDotNetNuke è 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
Controllo Remoto in Visual Basic .NET (quarta puntata)Il pattern Adapter suggerisce come modificare una classe server in modo da adattarsi all’interfaccia richiesta da altre classi client.
di Stefano Corti
WEB
50
55APPLICAZIONI
10 VBJ N. 67 - Gennaio/Febbraio 2006
Tdo - Typed Data Object 2.0
di Andrea Ferendeles
Già nel numero 62 abbiamo avuto modo di
parlare della prima versione di Tdo. Oggi
questo strumento si presenta rinnovato ed
in linea con le novità introdotte dal .NET framework
2.0 ma soprattutto in versione open source (http://
tdo.sourceforge.net).
Che cos’è TdoTdo è l’acronimo di Typed Data Object ovvero Og-
getto Dati Tipizzato e si compone di un assembly
.NET (Tdo.dll) e di un generatore di codice sorgen-
te (TdoCodeGenerator.exe).
Il generatore di codice produce un insieme di
classi derivate (VB.NET / C#.NET ) che estendo-
no le classi base presenti all’interno dell’assem-
bly Tdo.dll, il tutto secondo un rigoroso modello
Object Oriented.
Ogni classe generata ha lo scopo di “rappresenta-
re” un oggetto del nostro database SQL, dove per
oggetti si intendono Tabelle, Viste, Stored Proce-
dure, Funzioni. Proprio secondo questo modello,
ogni tabella, vista, ecc… viene considerata un og-
getto che espone a sua volta attributi ed operazio-
ni (proprietà e metodi).
Ciò significa che il singolo campo di una tabella
o il singolo parametro di una stored procedure sa-
ranno a loro volta oggetti. Tutte queste classi sono
racchiuse gerarchicamente in un unico macro-og-
getto a rappresentazione dell’intero database – la
classe TdoHelper.
Più avanti vedremo che tutte
le operazioni possibili che pos-
siamo eseguire sul nostro data-
base (select, insert, update, de-
lete, stored procedure, ecc…)
partiranno proprio dall’invoca-
zione di metodi presenti all’in-
terno degli oggetti della classe
TdoHelper.
Tdo è Open SourceTdo, a partire da questa ver-
sione (2.0) è divenuto un pro-
getto open source e sia i sor-
genti che il pacchetto di in-
stallazione sono scaricabili da
sourceforge.net all’indirizzo
web: http://tdo.sourceforge.net.
Il pacchetto di installazione
installa sia la libreria di base
Tdo.dll che il generatore di co-
dice TdoCodeGenerator.exe.
Per poterlo utilizzare è
consigliabile disporre di MS
Visual Studio .NET 2005 e Sql
Server 2005 (o 2000 o 7.0).
Generazione del codice sorgente e del DataSet
tipizzatoLa prima operazione da com-
piere è sicuramente quella di in-
stallare Tdo e quindi di genera-
re il codice sorgente necessario
(occorre aver precedentemente
installato il .NET Framework
TDO
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 2.0
Andrea Ferendeles ha iniziato ad appassionarsi di informati-ca dai tempi del VIC20. Ha sviluppato in BASIC, Logo, Turbo Pascal, C, C++, Visual Basic, fino ad arrivare ai linguaggi .NET, quali VB.NET e C#. È certificato MCP, MCAD, MSF, MCSD (.NET), MCDBA, MCT. Può essere contattato via email: [email protected].
11N. 67 - Gennaio/Febbraio 2006 VBJ
2.0). Una volta installato, possiamo lanciare
TdoCodeGenerator – Figura 1 – e dal riqua-
dro dei progetti in alto a sinistra, aggiungere
un nuovo Tdo Project alla soluzione di default
Tdo Solution 1 – Figura 2.
All’aggiunta di un nuovo progetto vi verrà
chiesto di immettere le informazioni di con-
nessione al database Sql tra cui il nome del
progetto Tdo, il linguaggio in cui il codice
sorgente dovrà essere generato, la modalità
di selezione degli oggetti dal database (tutti
o selezione custom), la directory in cui i sor-
genti verranno scritti ed infine se creare un
file per ogni classe generata oppure un unico
file sorgente contenente tutte le classi.
Questa stessa finestra vi chiederà inoltre un
namespace .NET in cui dichiarare le classi ed
un namespace web root da utilizzarsi per tut-
te le operazioni di serializzazione XML (web
service che restituiscono oggetti Tdo).
Una volta creato il progetto è possibile sele-
zionare per quali tabelle, viste, stored proce-
dure, funzioni, Tdo dovrà generare una rela-
tiva classe wrapper, se avete scelto la modali-
tà di Selection only, altrimenti tutto sarà au-
tomaticamente incluso nella generazione. In
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
salvare la Tdo Solu-
tion ed il Tdo Project
appena creati e clic-
care sul pulsante Ge-
nerate – Figura 3. Si aprirà dunque la fine-
stra di generazione del codice sorgente che
mostrerà lo stato di avanzamento dell’opera-
zione – Figura 4 - e se il database è integro,
cioè ad esempio non ci sono viste che pun-
tano a tabelle inesistenti, il codice generato
verrà scritto correttamente nella cartella di
output specificata.
Nell’esempio mostrato in figura si è scelto
di voler generare in un unico file C#.NET
tutte le tabelle, viste, stored procedure e fun-
zioni del database Northwind. Il file è stato
generato nella root del disco C: e si chiama
TdoNorthwind.cs. Inoltre sulla macchina su
cui stiamo effettuando le prove (un pentium
IV 3.40 GHz con 2 Gb di RAM) la generazione
del codice ha richiesto 0,6 sec. per un totale
di 420Kb di codice generato e 8.835 linee di
codice, quindi come vedete tempi molto esi-
gui di generazione (nemmeno un secondo).
Nella stessa cartella troverete lo schema
XML del DataSet tipizzato dell’intero data-
base Northwind TdoNorthwindDataSet.xsd
con all’interno tabelle, viste, stored proce-
dure e relazioni.
TDO 2.0
Fi gu ra 1 Lanciamo TdoCodeGenerator
12 VBJ N. 67 - Gennaio/Febbraio 2006
Tips & Tricks:
• Spesso durante la fase di sviluppo di un
software e maggiormente nelle fasi ini-
ziali, il database subirà spesso modifiche
che richiederanno una nuova generazione
del codice Tdo (ad esempio viene aggiunta
una colonna ad una tabella o viene creata
una nuova vista) . Il consiglio è quello di
aggiungere il file di progetto Tdo (quel-
lo appena salvato e con estensione .tdo-
prj) direttamente alla soluzione di Visual
Studio .NET in modo da poterlo rigene-
rare on demand direttamente dall’IDE di
sviluppo facendovi doppio click (associa-
te, solo la prima volta, l’estensione .tdoprj
all’eseguibile TdoCodeGenerator.exe)
• Dopo aver creato il progetto in Visual
Studio .NET è bene inoltre cambiare la
Output folder tra le proprietà del nostro
progetto Tdo in modo da far sovrascrive-
re di volta in volta il sorgente che Visual
Studio .NET andrà a compilare (VS.NET
si accorgerà che il file è stato modificato
dall’esterno e vi chiederà di ricaricarlo).
Compilazione del codice generatoUna volta generato il codice andremo a crea-
re un progetto .NET (C# o VB) di tipo Class
Library che farà da Data Layer per le nostre
prove sul database Northwind e lo chiame-
remo appunto TdoNorthwind. A questo pro-
getto dovremo aggiungere il file sorgen-
te appena generato TdoNorthwind.cs, eli-
minando prima il file Class1.cs automatica-
mente aggiunto da VS.NET, ed infine il file
TdoNorthwindDataSet.xsd. Proprio su que-
st’ultimo faremo doppio click per far generare
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).
Tdo Object ModelSe andiamo a curiosare nel
codice sorgente che Tdo ha ge-
Fi gu ra 2 Aggiunta di un nuovo Tdo Project
TDO 2.0
13N. 67 - Gennaio/Febbraio 2006 VBJ
nerato per noi, ci accorgeremo che la struttura
è estremamente semplice ed intuitiva. Inizia-
mo col dire che esisterà una classe per ogni
tabella, vista, stored procedure e funzione.
Prendiamo in esame ad esempio la tabella
Employees che nel codice generato corrispon-
de alla classe EmployeesTable.
La Figura 5 mostra il diagramma delle clas-
si e la gerarchia degli oggetti alla quale ap-
partiene la classe:
System.Object � Tdo.Common.TdoEntityBase
� Tdo.Common.Tables.TdoTableBase �
Tdo.Northwind.Entities.EmployeesTable,
dove la classe Tdo.Common.TdoEntityBase im-
plementa l’interfaccia Tdo.Common.ITdoEntity
e la classe Tdo.Common.Tables.TdoTableBas
e implementa l’interfaccia Tdo.Common.Tabl
es.ITdoTable.
In Figura 6 e 7 rispettivamente e analoga-
mente i diagrammi delle classi della vista Al-
phabetical List Of Products e della stored pro-
cedure Cust Order Hist.
Tutte queste classi sono riesposte come pro-
prietà della classe TdoHelper … la classe che
rappresenta l’intero database.
In Figura 8 il suo diagramma ed in Figu-
ra 9 l’organizzazione dei namespace dell’as-
sembly Tdo.dll.
Ora che abbiamo fatto le presentazioni for-
mali vediamo in concreto come utilizzare la
classe TdoHelper.
Come si vede dalla Figura 8 tutte le proprie-
tà della classe TdoHelper che espongono ta-
belle iniziano per “t” (table).
Allo stesso modo proprietà che espongono
viste, stored procedure e funzioni avranno ri-
spettivamente “v”, “p” ed “f” come prefisso.
Questo semplice meccanismo permette du-
rante la stesura del codice, di rintracciare su-
bito, grazie all’intellisense di VS.NET, gli og-
getti sui quali vogliamo operare – Figura 10
e 11. Inoltre ogni singola classe che espone
una tabella del db, (ma lo stesso discorso vale
per viste, stored procedure e funzioni) ha tut-
ti i campi della tabella stessa esposti come
proprietà di tipo Tdo.Common.TdoTypes.Td
oTypeBase. Questo tipo base ha il compito di
formalizzare tutte le proprietà della colonna
(tipo, dimensione, nullabilità, ecc…) e ovvia-
mente il valore del campo attraverso la pro-
prietà Value , proprietà che poi verrà specia-
lizzata a seconda dell’effettivo tipo (int, char,
binary, ecc…) grazie ai generics di .NET 2.0.–
Figura 12.
Tips & Tricks:
• Tutte le classi generate sono estendibili
tramite ereditarietà, sono CLS compliant,
serializzabili e marcate con il prefisso
partial. Questo ci consente all’occorren-
za di poter “completare” le classi gene-
rate da Tdo con metodi/proprietà/eventi
custom in file .cs/.vb diversi da quelli ge-
nerati da Tdo (se rigenerassimo il codi-
ce sorgente, a fronte di cambiamenti nel-
lo schema del db, perderemmo le nostre
implementazioni).
La classe TdoHelperFacciamo ora alcuni esempi concreti di uti-
lizzo della classe TdoHelper generata per il
database Northwind.
Prendiamo il database Northwind, presen-
te ormai quasi per tradizione anche nella
versione Express di SQL 2005 e supponia-
mo di aver generato tramite TdoCodeGene-
rator tutte le classi necessarie per trattare
l’intero database Northwind.
Abbiamo detto che tutte le operazioni par-
tiranno dalla classe TdoHelper e allora co-
minciamo proprio col creare un’istanza di
questa classe.
Listato 1:
C#
//Automatic connection string building
TdoHelper tdo = new TdoHelper(“(local)”, “Northwind”,
“sqlusername”, “sqlpassword”);
VB.NET
‘Automatic connection string building
Dim tdo As New TdoHelper(“(local)”, “Northwind”,
“sqlusername”, “sqlpassword”)
TDO 2.0
14 VBJ N. 67 - Gennaio/Febbraio 2006
Le uniche informazioni da fornire al costrut-
tore della classe sono i parametri di connes-
sione al database.
In questo caso stiamo dicendo a Tdo di vo-
ler utilizzare l’istanza di default di Sql Server
presente sulla nostra macchina e di voler ac-
cedere al database Northwind tramite sql-au-
thentication. La classe TdoHelper è fornita di
ben 11 costruttori per consentirci di specifi-
care tutte le possibili modalità di connessio-
ne tra cui: Windows-authentication, asynchro-
nous processing, connection pooling, failover
partner, ecc….
Ecco, nel Listato 2, alcuni altri modi per spe-
cificare le informazioni di connessione:
Listato 2:
C#
//default constructor - assign connection string properties
TdoHelper tdo1 = new TdoHelper();
tdo1.ConnectionString = “data source=(local);Initial
Catalog=Northwind;Integrated Security=SSPI”;
//Windows Authentication
TdoHelper tdo2 = new TdoHelper(“(local)”, “Northwind”);
//Windows Authentication with asynchronous processing
enabled.
TdoHelper tdo2async = new TdoHelper(“(local)”,
“Northwind”,true);
//Sql Authentication
TdoHelper tdo3 = new TdoHelper(“(local)”, “Northwind”,
“sqlusername”, “sqlpassword”);
//Sql Authentication with asynchronous processing enabled.
TdoHelper tdo3async = new TdoHelper(“(local)”,
“Northwind”, “sqlusername”, “sqlpassword”,true);
//Several parameters
TdoHelper tdo4 = new TdoHelper(
“SqlServerName”, //sql server name
“DataBaseName”, //database name
“sqlusername”, //sql username
“sqlpassword”, //sql password
30, //connection life time
true, //connection reset
false, //enlist
50, //max pool size
1, //min pool size
true, //pooling
true, //asynchronous processing
“PartnerSqlServerName”); //sql backup server name
VB.NET
‘default constructor - assign connection string properties
Dim tdo1 As New TdoHelper()
tdo1.ConnectionString = “data source=(local);Initial
Catalog=Northwind;Intgrated Security=SSPI”
‘Windows Authentication
Dim tdo2 As New TdoHelper(“(local)”, “Northwind”)
‘Windows Authentication with asynchronous processing enabled.
Dim tdo2async As New TdoHelper(“(local)”, “Northwind”, True)
‘Sql Authentication
Dim tdo3 As New TdoHelper(“(local)”, “Northwind”,
“sqlusername”, “sqlpassword”)
‘Sql Authentication with asynchronous processing enabled.
Dim tdo3async As New TdoHelper(“(local)”, “Northwind”,
“sqlusername”, “sqlpassword”, True)
‘Several parameters
Dim tdo4 As New TdoHelper(“SqlServerName”, “DataBaseName”,
“sqlusername”, “sqlpassword”,
30, True, False, 50, 1, True, True, “PartnerSql-
ServerName”)
La classe TdoHelper è fornita di una serie
di proprietà che ne governano il comporta-
mento durante la fase di run-time, come ad
esempio la capacità di aprire e chiudere au-
tomaticamente la connessione al database
oppure far sì che tutte le operazioni CRUD
siano automaticamente racchiuse in una
transazione SQL.
Listato 3:
C#
//Automatic connection open before any operation
//... and close automatically after execution
tdo.AutomaticOpenCloseConnection = true; //default is true
TDO 2.0
15N. 67 - Gennaio/Febbraio 2006 VBJ
//Automatic BEGIN TRANSACTION on every CRUD operation
(and SPs)
// ... and commit after execution
tdo.AutomaticTransaction = false;
//Command timeout
tdo.CommandTimeOut = 60; //default is 60
VB.NET
‘Automatic connection open before any operation
‘... and close automatically after execution
tdo.AutomaticOpenCloseConnection = True ‘default is true
‘Automatic BEGIN TRANSACTION on every CRUD operation
(and SPs)
‘ ... and commit after execution
tdo.AutomaticTransaction = False
‘Command timeout
tdo.CommandTimeOut = 60 ‘default is 60
Operazioni di SELECT Vediamo ora come con Tdo sia possibile
eseguire operazioni di SELECT con una
semplice riga di codice. Ci sono diversi
metodi per eseguire tale operazione in base
al tipo di result-set desiderato; iniziamo con
il metodo SelectDataTable che restituisce
appunto un oggetto System.Data.DataTable
di ADO.NET 2.0 (in commento l’equivalente
statement T-SQL).
Listato 4:
C#
//SELECT * FROM dbo.Employees
DataTable dtEmployees = tdo.tEmployees.SelectDataTable();
VB.NET
‘SELECT * FROM dbo.Employees
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table()
TDO 2.0
Fi gu ra 3 Si comanda la generazione del codice sorgente
16 VBJ N. 67 - Gennaio/Febbraio 2006
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
DataTable dtEmployees =
tdo.tEmployees.SelectDataTable
(
tdo.tEmployees.Employeeid,
tdo.tEmployees.Lastname,
tdo.tEmployees.Firstname
);
VB.NET
‘SELECT EmployeeId, LastName, FirstName FROM dbo.Employees
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table( _
tdo.tEmployees.Employeeid, _
tdo.tEmployees.Lastname, _
tdo.tEmployees.Firstname)
… ed eventuali clausole Where/Order By/
Group by …
Listato 6:
C#
//SELECT EmployeeId, LastName, FirstName
// FROM dbo.Employees
// WHERE LastName=’Davolio’ AND FirstName=’Nancy’
DataTable dtEmployees = tdo.tEmployees.SelectDataTable
(
Clause.Where(tdo.tEmployees.Lastname==”Davolio” &
tdo.tEmployees.Firstname==”Nancy”),
tdo.tEmployees.Employeeid,
tdo.tEmployees.Lastname,
tdo.tEmployees.Firstname
);
VB.NET
‘SELECT EmployeeId, LastName, FirstName
‘ FROM(dbo.Employees)
‘ WHERE LastName=’Davolio’ AND FirstName=’Nancy’
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table( _
Clause.Where(tdo.tEmployees.Lastname = “Davolio” And
tdo.tEmployees.Firstname = “Nancy”), _
tdo.tEmployees.Employeeid, _
tdo.tEmployees.Lastname, _
tdo.tEmployees.Firstname)
… il tutto ancora con una sola riga di codice.
Tdo Sql Expression DOMVediamo alcune espressioni più complesse e
come sia possibile scriverle facendo uso del-
l’overloading degli operatori introdotto dal mo-
dello Tdo Sql Expression DOM (Tdo.Common.
TdoSqlExpressionDOM).
Listato 7:
C#
//SELECT EmployeeId, LastName, FirstName
// FROM dbo.Employees
// WHERE EmployeeId>=5
// OR
// (TitleOfCourtesy LIKE ‘Ms.’ AND LastName<>’Davolio’)
// AND
// (REGION IS NOT NULL)
DataTable dtEmployees = tdo.tEmployees.SelectDataTable
TDO 2.0
Fi gu ra 4 Stato di avanzamento della generazione
17N. 67 - Gennaio/Febbraio 2006 VBJ
(
Clause.Where
(
tdo.tEmployees.Employeeid>=5
|
(tdo.tEmployees.Titleofcourtesy % “Ms.”
& tdo.tEmployees.Lastname!=”Davolio”)
&
(tdo.tEmployees.Region!=DBNull.Value)
),
tdo.tEmployees.Employeeid,
tdo.tEmployees.Lastname,
tdo.tEmployees.Firstname
);
VB.NET
‘SELECT EmployeeId, LastName, FirstName
‘ FROM dbo.Employees
‘ WHERE EmployeeId>=5
‘ OR
‘ (TitleOfCourtesy LIKE ‘Ms.’ AND LastName<>’Davolio’)
‘ AND
‘ (REGION IS NOT NULL)
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table( _
Clause.Where _
( _
tdo.tEmployees.Employeeid >= 5 _
Or _
(tdo.tEmployees.Titleofcourtesy Mod “Ms.” And
tdo.tEmployees.Lastname <> “Davolio”) _
And _
(tdo.tEmployees.Region <> DBNull.Value) _
), _
tdo.tEmployees.Employeeid, _
tdo.tEmployees.Lastname, _
tdo.tEmployees.Firstname)
Nei Listati 8 e 9 vediamo l’utilizzo delle clas-
si TdoWheresetofValues e TdoWhereRange per
costruire espressioni SQL basate sugli opera-
tori T-SQL IN e BETWEEN.
Listato 8:
C#
//SELECT *
// FROM dbo.Employees
// WHERE Titleofcourtesy IN (‘Mrs.’,’Mr.’, ‘Ms.’)
DataTable dtEmployees = tdo.tEmployees.SelectDataTable
(
Clause.Where
(
tdo.tEmployees.Titleofcourtesy==new TdoWhere-
setOfValues(“Mrs.”,”Mr.”,”Ms.”)
)
);
VB.NET
‘SELECT *
‘ FROM dbo.Employees
‘ WHERE Titleofcourtesy IN (‘Mrs.’,’Mr.’, ‘Ms.’)
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData-
Table( _
Clause.Where _
( _
tdo.tEmployees.Titleofcourtesy = New TdoWhere-
setOfValues(“Mrs.”, “Mr.”, “Ms.”) _
) _
)
Listato 9:
C#
//SELECT *
// FROM dbo.Employees
// WHERE (BirthDate BETWEEN ‘1950-01-01’ AND ‘1960-12-31’)
DataTable dtEmployees = tdo.tEmployees.SelectDataTable
(
Clause.Where
(
tdo.tEmployees.Birthdate==new TdoWhereRange
(
new DateTime(1950,1,1),new DateTi-
me(1960,12,31)
)
)
);
VB.NET
‘SELECT *
‘ FROM dbo.Employees
‘ WHERE (BirthDate BETWEEN ‘1950-01-01’ AND ‘1960-12-31’)
Dim dtEmployees As DataTable = tdo.tEmployees.SelectDataTable( _
Clause.Where _
( _
tdo.tEmployees.Birthdate = New TdoWhereRange _
( _
TDO 2.0
18 VBJ N. 67 - Gennaio/Febbraio 2006
New DateTime(1950, 1, 1), New DateTi-
me(1960, 12, 31) _
) _
) _
)
Un esempio di Order by e Group By nei Li-
stati 10 e 11:
Listato 10:
C#
//SELECT *
// FROM dbo.Employees
// ORDER BY TitleOfCourtesy DESC, LastName, FirstName
DataTable dtEmployees = tdo.tEmployees.SelectDataTable
(
Clause.OrderBy
(
tdo.tEmployees.Titleofcourtesy,
OrderByOperator.Desc,
tdo.tEmployees.Lastname, OrderByOperator.Asc,
tdo.tEmployees.Firstname, OrderByOperator.Asc
)
);
VB.NET
TDO 2.0
Fi gu ra 5 Diagramma delle classi e gerarchia degli oggetti alla quale appartiene la classe EmployeesTable
19N. 67 - Gennaio/Febbraio 2006 VBJ
TDO 2.0
‘SELECT *
‘ FROM dbo.Employees
‘ ORDER BY TitleOfCourtesy DESC, LastName, FirstName
Dim dtEmployees As DataTable = tdo.tEmployees.SelectData
Table( _
Clause.OrderBy _
( _
tdo.tEmployees.Titleofcourtesy,
OrderByOperator.Asc, _
tdo.tEmployees.Lastname, OrderByOperator.Asc, _
tdo.tEmployees.Firstname, OrderByOperator.Asc _
) _
)
Listato 11:
C#
//SELECT CategoryID AS [Category ID], COUNT(ProductID) AS
Total
// FROM Products
// GROUP BY CategoryID
DataTable dtProductsCount = tdo.tProducts.SelectDataTable
(
Clause.GroupBy(tdo.tProducts.Categoryid),
tdo.tProducts.Categoryid.Alias(“Category ID”),
Functions.Count<TdoInt32>(tdo.tProducts.
Productid,”Total”)
);
VB.NET
‘SELECT CategoryID AS [Category ID], COUNT(ProductID) AS
Total
‘ FROM Products
‘ GROUP BY CategoryID
Dim dtProductsCount As DataTable = tdo.tProducts.
SelectDataTable _
( _
Clause.GroupBy(tdo.tProducts.Categoryid), _
tdo.tProducts.Categoryid.Alias(“Category ID”), _
Functions.Count(Of TdoInt32)(tdo.tProducts.
Productid, “Total”) _
)
Oltre al metodo SelectDataTable esistono al-
tri metodi fra cui SelectDataReader, SelectSca-
lar, SelectIdentity, SelectXmlReader che re-
stituiscono rispettivamente oggetti di tipo
System.Data.SqlDataReader, System.Object,
System.Int32, System.Xml.XmlReader, tutti
con vari overload analogamente al metodo Se-
lectDataTable.
Listato 12:
C#
//SELECT LastName, FirstName FROM Employees
// WHERE LastName = ‘Davolio’
SqlDataReader dataReader = tdo.tEmployees.SelectDataReader(
Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
CommandBehavior.SingleRow,
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname);
dataReader.Close(); //must be closed before another
select operation
//SELECT Birthdate FROM Employees
// WHERE LastName = ‘Davolio’
DateTime birthDate = (DateTime)tdo.tEmployees.SelectScalar(
Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
tdo.tEmployees.Birthdate);
//SELECT EmployeeId FROM Employees
// WHERE LastName = ‘Davolio’
int davolioEmpId = tdo.tEmployees.SelectIdentity(
Clause.Where(tdo.tEmployees.Lastname ==
“Davolio”)).Value;
//SELECT LastName, FirstName FROM Employees
// WHERE LastName = ‘Davolio’ FOR XML AUTO, ELEMENTS
XmlReader xmlEmployees = tdo.tEmployees.SelectXmlReader(
Clause.Where(tdo.tEmployees.Lastname == “Davolio”),
“FOR XML AUTO, ELEMENTS”,
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname);
VB.NET
‘SELECT LastName, FirstName FROM Employees
‘ WHERE LastName = ‘Davolio’
Dim dataReader As SqlDataReader = tdo.tEmployees.
SelectDataReader( _
Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _
CommandBehavior.SingleRow, _
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname)
dataReader.Close() ‘must be closed before another
select operation
‘SELECT Birthdate FROM Employees
‘ WHERE LastName = ‘Davolio’
20 VBJ N. 67 - Gennaio/Febbraio 2006
Dim birthDate As DateTime = DirectCast(tdo.tEmployees.
SelectScalar( _
Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _
tdo.tEmployees.Birthdate), DateTime)
‘SELECT EmployeeId FROM Employees
‘ WHERE LastName = ‘Davolio’
Dim davolioEmpId As Integer = tdo.tEmployees.SelectIdentity( _
Clause.Where(tdo.tEmployees.Lastname = “Davolio”)).Value
‘SELECT LastName, FirstName FROM Employees
‘ WHERE LastName = ‘Davolio’ FOR XML AUTO, ELEMENTS
Dim xmlEmployees As XmlReader = tdo.tEmployees.
SelectXmlReader( _
Clause.Where(tdo.tEmployees.Lastname = “Davolio”), _
“FOR XML AUTO, ELEMENTS”, _
tdo.tEmployees.Lastname, tdo.tEmployees.Firstname)
Operazioni di INSERT, UPDATE, DELE-TE e supporto alle Transazioni
Le operazioni di INSERT, UPDATE e DELE-
TE su tabelle sono altrettanto semplici e poco
costose in termini di righe di codice necessa-
rio. Prendiamo in considerazione la tabella Em-
ployees e la relativa classe Tdo Tdo.Northwind.
Entities.EmployeesTable.
Al suo interno troviamo mappati tutti i cam-
pi della tabella tra cui Employeeid, Lastname,
Birthdate, Photo, ecc…, riesposti con altrettan-
ti tipi Tdo e rispettivamente come TdoInt32,
TdoString, TdoDateTime, TdoBinary. All’in-
terno di ogni tipo, oltre alle varie informazioni
che caratterizzano il tipo del campo (AllowDB-
Null, AutoIncrement, MaxLenght, ecc…), tro-
viamo la proprietà Value; per i tipi suddetti la
proprietà sarà rispettivamente di tipo SqlInt32,
SqlString, SqlDateTime, SqlBinary ovvero Tdo
fa uso delle strutture presenti nel namespa-
ce System.Data.SqlTypes di ADO.NET 2.0 per
esporre i valori di ciascun campo. Infine non
dimentichiamo che la classe Tdo.Northwind.
Entities.EmployeesTable viene esposta come
proprietà della classe TdoHelper con il nome
tEmployees (prefisso “t” in quanto è una tabel-
la). Fatte queste considerazioni vediamo come
eseguire operazioni di inserimento, modifica e
cancellazione dalla tabella employees nei Li-
stati 13, 14, 15 e 16.
Nel Listato 15 c’è un esempio di transazio-
ne con Tdo. Tutti gli oggetti racchiusi nella
classe TdoHelper infatti condividono la stes-
sa connessione al database e partecipano au-
tomaticamente alla stessa transazione (se ri-
chiesta).
Per eseguire una serie di comandi in transa-
zione (è possibile specificare il livello di isola-
mento) è sufficiente racchiudere tutte le ope-
razioni Tdo tra due chiamate ai metodi Tdo
Helper.BeginTransaction e TdoHelper.Com-
mitTransaction (o TdoHelper.RollBackTran-
saction).
TDO 2.0
T-SQL C#.NET VB.NET
= == =<> != <>> > >
>= >= >=< < <
<= <= <=NOT ! NOTAND & AndOR | OrLIKE % Mod
NOT LIKE - -
Ri qua dro 1 Elenco degli operatori .NET in overloading per espressioni T-SQL secondo il modello Tdo Sql
Expression DOM
21N. 67 - Gennaio/Febbraio 2006 VBJ
Listato 15:
C#
// SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
// BEGIN TRANSACTION
// UPDATE Employees
// SET Lastname = ‘Ferendeles mod’, Firstname = ‘Andrea mod’
// WHERE Lastname = ‘Ferendeles’
// COMMIT TRANSACTION
tdo.OpenConnection();
tdo.BeginTransaction(IsolationLevel.ReadUncommitted);
tdo.tEmployees.Lastname.Value=”Ferendeles mod”;
tdo.tEmployees.Firstname.Value = “Andrea mod”;
int affectedRecords =
tdo.tEmployees.Update(Clause.Where(tdo.tEmployees.
Lastname == “Ferendeles”));
tdo.CommitTransaction();
tdo.CloseConnection();
//[SQL Profiler Trace]:
//---------------------
//exec sp_executesql N’update dbo.Employees
// SET LastName=@LastName,FirstName=@FirstName
// WHERE (Employees.LastName = @_p14705386011) ‘,
// N’@_p14705386011 nvarchar(10),@LastName nvarchar
(14), @FirstName nvarchar(10)’,
// @_p14705386011 = N’Ferendeles’,
// @LastName = N’Ferendeles mod’, @FirstName = N’Andrea mod’
VB.NET
‘ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
‘ BEGIN TRANSACTION
‘ UPDATE Employees
‘ SET Lastname = ‘Ferendeles mod’, Firstname = ‘Andrea mod’
‘ WHERE Lastname = ‘Ferendeles’
‘ COMMIT TRANSACTION
tdo.OpenConnection()
tdo.BeginTransaction(IsolationLevel.ReadUncommitted)
tdo.tEmployees.Lastname.Value = “Ferendeles mod”
TDO 2.0
Fi gu ra 6 Diagramma delle classi della vista Alphabetical List Of Products
22 VBJ N. 67 - Gennaio/Febbraio 2006
tdo.tEmployees.Firstname.Value = “Andrea mod”
Dim affectedRecords As Integer = _
tdo.tEmployees.Update(Clause.Where(tdo.tEmployees.
Lastname = “Ferendeles”))
tdo.CommitTransaction()
tdo.CloseConnection()
‘[SQL Profiler Trace]:
‘---------------------
‘exec sp_executesql N’update dbo.Employees
‘ SET LastName=@LastName,FirstName=@FirstName
‘ WHERE (Employees.LastName = @_p14705386011) ‘,
‘ N’@_p14705386011 nvarchar(10),@LastName nvarchar
(14), @FirstName nvarchar(10)’,
‘ @_p14705386011 = N’Ferendeles’,
‘ @LastName = N’Ferendeles mod’, @FirstName = N’Andrea mod’
Listato 16:
C#
//DELETE FROM Employees
// WHERE Lastname LIKE ‘%Ferendeles%’
int affectedRecords =
tdo.tEmployees.Delete(Clause.Where(tdo.tEmployees.
Lastname % “%Ferendeles%”));
//[SQL Profiler Trace]:
//---------------------
//exec sp_executesql N’delete from dbo.Employees
TDO 2.0
Fi gu ra 7 Diagramma delle classi della stored procedure Cust Order Hist
23N. 67 - Gennaio/Febbraio 2006 VBJ
// WHERE (Employees.LastName LIKE @_p631522191) ‘,
N’@_p631522191 nvarchar(12)’,
// @_p631522191 = N’%Ferendeles%’
VB.NET
‘DELETE FROM Employees
‘ WHERE Lastname LIKE ‘%Ferendeles%’
Dim affectedRecords As Integer = _
tdo.tEmployees.Delete(Clause.Where(tdo.tEmployees.Last-
name Mod “%Ferendeles%”))
‘[SQL Profiler Trace]:
‘---------------------
‘exec sp_executesql N’delete from dbo.Employees
‘ WHERE (Employees.LastName LIKE @_p631522191) ‘,
N’@_p631522191 nvarchar(12)’,
‘ @_p631522191 = N’%Ferendeles%’
Comandi AsincroniADO.NET 2.0 è stato dotato della possibili-
tà di eseguire comandi in modalità asincro-
na, in particolar modo sfruttando le coppie di
metodi BeginXXX/EndXXX dell’oggetto Sql-
Command.
Allo stesso modo Tdo fornisce con equiva-
lente sintassi coppie di metodi BeginXXX/
EndXXX per le tutte le operazioni finora vi-
ste. Vediamo nel Listato 17 un esempio di
esecuzione asincrona per una operazione di
INSERT.
Stored Procedure e User-FunctionAnalogamente ad operazioni CRUD, le Stored
Procedure sono rappresentate da Tdo tramite
classi derivate dalla classe base Tdo.Common.
Programmability.TdoStoredProcedureBase ed
esposte come proprietà dalla classe TdoHel-
per. Prendiamo in esame la stored procedure
CustOrderHist, definita con un parametro in
ingresso @CustomerID di tipo nchar(5) e nes-
sun parametro di output. Nel Listato 18 la sto-
red procedure viene invocata passando diretta-
mente il parametro CustomerId al metodo Fill-
DataTable in una sola riga di codice.
Nel Listato 19 invece la stessa invocazione ma
assegnando prima, singolarmente i valori di cia-
scun parametro e successivamente ricavando gli
eventuali valori dei parametri di output.
Listato 19:
C#
this.tdo.pCustorderhist.pCustomerid = “ALFKI”;
SqlParameterCollection outputParameters;
SqlDataReader custReader = his.tdo.pCustorderhist.Execute-
Reader(CommandBehavior.SingleResult, out outputParame-
ters);
//do something with custReader
custReader.Close(); //close before retrieve output para-
meters
this.tdo.pCustorderhist.AssignOutputParameterValues
(outputParameters);
//object somevalue = this.tdo.pCustorderhist.pSomeOutput-
Parameter.Value;
VB.NET
MyClass.tdo.pCustorderhist.pCustomerid = “ALFKI”
Dim outputParameters As SqlParameterCollection
Dim custReader As SqlDataReader =
MyClass.tdo.pCustorderhist.ExecuteReader
(CommandBehavior.SingleResult, outputParameters)
‘do something with custReader
custReader.Close() ‘close before retrieve output parameters
MyClass.tdo.pCustorderhist.AssignOutputParameterValues
(outputParameters)
‘object somevalue = this.tdo.pCustorderhist.pSomeOutput-
Parameter.Value
Nel Listato 20 la stessa stored procedure in-
vocata in modo asincrono più un esempio di
come la classe TdoHelper possa essere uti-
lizzata in un costrutto using (TdoHelper è
System.IDisposable).
Purtroppo nel database Northwind non è pre-
sente nessuna user-function, pertanto invito
tutti a creare la vostra user-function, a gene-
rare il codice sorgente con TdoCodeGenera-
tor e ad effettuare le dovute prove.
Listato 20:
C#
using (TdoHelper tdo = new TdoHelper(“(local)”,
“Northwind”, true))
{
tdo.pCustorderhist.AssignParameterValues(“ALFKI”);
TDO 2.0
24 VBJ N. 67 - Gennaio/Febbraio 2006
//assign all in one row
SqlParameterCollection outputParameters;
IAsyncResult asyncResult = tdo.pCustorderhist.
BeginExecuteReader(
null, null, out outputParameters);
//do something else
SqlDataReader custReader = tdo.pCustorderhist.
EndExecuteReader(asyncResult);
custReader.Read();
//do something with results
custReader.Close(); //close reader before retrieve
output parameters
tdo.pCustorderhist.AssignOutputParameterValues
(outputParameters);
TDO 2.0
Fi gu ra 8 Diagramma di TdoHelper
25N. 67 - Gennaio/Febbraio 2006 VBJ
int retValue = tdo.pCustorderhist.pReturnValue.Value;
}
VB.NET
Using tdo As New TdoHelper(“(local)”, “Northwind”, True)
tdo.pCustorderhist.AssignParameterValues(“ALFKI”)
‘assign all in one row
Dim outputParameters As SqlParameterCollection = Nothing
Dim asyncResult As IAsyncResult = tdo.pCustorderhist.
BeginExecuteReader( _
Nothing, Nothing, outputParameters)
‘do something else
Dim custReader As SqlDataReader = tdo.pCustorderhist.
EndExecuteReader(asyncResult)
custReader.Read()
‘do something with results
custReader.Close() ‘close reader before retrieve
output parameters
tdo.pCustorderhist.AssignOutputParameterValues
(outputParameters)
Dim retValue As Integer = tdo.pCustorderhist.pReturn
Value.Value
End Using
Tdo e DataSet tipizzatiIl generatore di codice TdoCodeGene-
rator, oltre a generare tutto il codice sor-
gente necessario alla trattazione dell’inte-
ro database, produce un DataSet tipizza-
to (System.Data.DataSet – Figura 13) di
ADO.NET 2.0, per ad esempio operazioni
di data-binding, operazioni in modalità di-
sconnessa o xml web service. Prima di po-
ter utilizzare il dataset è necessario fare dop-
pio click (o tasto destro del mouse – run cu-
stom tool) sul file .xsd dall’interno di Visual
Studio .NET.
Tale operazione fa si che l’IDE generi cor-
rettamente il code-behind (classi tipizzate)
a partire dallo schema XSD del dataset. Da
questo momento in poi il DataSet sarà visibi-
le come componente dalle applicazioni con-
sumatrici e trascinabile sul designer.
Tdo ovviamente fornisce tutto il supporto
necessario alla trattazione dei DataSet tipiz-
zati fornendo metodi per il caricamento e per
la manipolazione sino al livello di singola Da-
taRow. Nel Listato 21 un esempio di carica-
TDO 2.0
mento della DataTable Orders del DataSet
tipizzato NorthwindDataSet e nel Listato 22
vediamo come sia possibile aggiornare con
Tdo una riga già modificata nel dataste; in-
fine nel Listato 23 come inserire nel dataset
una riga già inserita da Tdo nel database.
TdoHelper … Select Join, SqlDependecy, SqlNotificationRequest,
Properties & EventsLa classe TdoHelper è fornita di una serie
di metodi per operazioni generiche o che non
riguardano nello specifico un solo database
object (una sola tabella/vista/SP/funzione).
Consideriamo una operazione di INNER
JOIN tra due tabelle: la tabella Orders e
la tabella Order Details. Secondo la logica
Object Oriented questa è un'operazione al
livello di database e dunque esposta attra-
verso un metodo della classe TdoHelper. La
filosofia di Tdo è quella di spingere lo svi-
luppatore ed il DBA ad operare più possibile
tramite Viste e Stored Procedure per ragioni
di performance e manutenibilità ormai arci-
note (Tdo recita: “crea gli oggetti nel db e
rigenera il codice al volo”); detto ciò, nella
classe TdoHelper troviamo comunque tutto
il supporto per operazioni di Join on-the-fly
in quanto non è sempre possibile interve-
nire, per diverse ragioni, sul database. Nel
Listato 24 un esempio di INNER JOIN con
il metodo TdoHelper.SelectJoin tra le due
tabelle sopra citate.
Listato 24:
C#
DataTable dtOrder_OrderDetails = tdo.SelectJoin(
new JoinHelper(Join.InnerJoin, tdo.tOrders.Orderid,
JoinOperator.Equal, tdo.tOrderDetails.Orderid),
Tdo è l’acronimo di
Typed Data Object
ovvero
Oggetto Dati Tipizzato
26 VBJ N. 67 - Gennaio/Febbraio 2006
Clause.Where(tdo.tOrders.Customerid == “ALFKI”),
tdo.tOrders.Orderid, tdo.tOrders.Customerid,
tdo.tOrders.Orderdate,
tdo.tOrderDetails.Productid, tdo.tOrderDetails.
Quantity, tdo.tOrderDetails.Unitprice);
//[SQL Profiler Trace]:
//---------------------
//exec sp_executesql N’select Orders.OrderID as [OrderID] ,
Orders.CustomerID as [CustomerID] ,
// 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’
VB.NET
Dim dtOrder_OrderDetails As DataTable = tdo.SelectJoin( _
New JoinHelper(Join.InnerJoin,
tdo.tOrders.Orderid, JoinOperator.Equal,
tdo.tOrderDetails.Orderid), _
Clause.Where(tdo.tOrders.Customerid = “ALFKI”), _
tdo.tOrders.Orderid, tdo.tOrders.Customerid,
tdo.tOrders.Orderdate, _
tdo.tOrderDetails.Productid, tdo.tOrderDetails.
Quantity, tdo.tOrderDetails.Unitprice)
‘[SQL Profiler Trace]:
‘---------------------
‘exec sp_executesql N’select Orders.OrderID as [OrderID]
,Orders.CustomerID as [CustomerID] ,
‘ 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
per ammettere operazioni di JOIN che
coinvolgano al più 6 tabelle/viste. Altri metodi
interessanti della classe TdoHelper sono:
• CreateCommand per creare rapidamente og-
getti SqlCommand, con la possibilità di passare
un parametro di tipo SqlNotificationRequest
(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/
dnvs05/html/querynotification.asp).
Nel Listato 25 un esempio di utilizzo del
metodo CreateCommand con l’oggetto Sys
tem.Data.Sql.SqlDependency per SQL Ser-
ver 2005.
• CreateDataAdapter per creare rapidamen-
te oggetti SqlDataAdapter.
• ExecuteNonQuery, ExecuteReader, Execu-
teScalar, ExecuteXmlReader.
• Fill, FillSchema.
Listato 25:
C#
public void example_025()
{
this.tdo = new TdoHelper(@”.\SQL2005”,”Northwind”);
SqlDependency.Start(tdo.Connection.ConnectionString);
SqlCommand empCmd = tdo.CreateCommand(
String.Format(“select * from {0}.{1}”,tdo.tEmploy-
ees.SchemaName ,tdo.tEmployees.EntityName),
CommandType.Text);
SqlDependency dep = new SqlDependency(empCmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
tdo.OpenConnection();
SqlDataReader dr = empCmd.ExecuteReader();
//do something with dr
dr.Close();
}
TDO 2.0
Fi gu ra 9 Organizzazione di namespace dell'assembly Tdo.dll
27N. 67 - Gennaio/Febbraio 2006 VBJ
public void dep_OnChange(object sender, SqlNotification-
EventArgs e)
{
System.Diagnostics.Debug.WriteLine(
String.Format(@”Source: {0}\r\nInfo: {1}, Type:
{2}”,e.Source,e.Info,e.Type));
}
VB.NET
Dim WithEvents dep As SqlDependency
Public Sub example_025()
MyClass.tdo = New TdoHelper(“.\SQL2005”, “Northwind”)
SqlDependency.Start(tdo.Connection.ConnectionString)
Dim empCmd As SqlCommand = tdo.CreateCommand( _
String.Format(“select * from {0}.{1}”,
tdo.tEmployees.SchemaName, tdo.tEmployees.EntityName), _
CommandType.Text)
dep = New SqlDependency(empCmd)
tdo.OpenConnection()
Dim dr As SqlDataReader = empCmd.ExecuteReader()
‘do something with dr
dr.Close()
End Sub
Private Sub dep_OnChange(ByVal sender As Object, ByVal e
As System.Data.SqlClient.SqlNotificationEventArgs) _
Handles dep.OnChange
System.Diagnostics.Debug.WriteLine( _
String.Format(“Source: {0}, Info: {1}\r\n, Type:
{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-
tà TdoHelper.TdoTables
per ottenere a run-time
tutti i nomi delle colonne di tutte le tabel-
le del db.
Listato 26:
C#
foreach (Tdo.Common.Entities.
Tables.ITdoTable table in
tdo.TdoTables)
{
System.Diagnostics.Debug.WriteLine(String.Format
(“Table: {0}”,table.EntityName));
foreach (Tdo.Common.TdoTypes.ITdoColumn column in
table.TdoColumns)
{
System.Diagnostics.Debug.WriteLine(String.Format
(“\tColumn: {0}”,column.ColumnName));
}
}
//Output Window sample:
//---------------------
//Table: [Order Details]
// Column: OrderID
// Column: ProductID
// Column: UnitPrice
// Column: Quantity
// Column: Discount
//Table: Categories
// Column: CategoryID
// Column: CategoryName
// Column: Description
TDO 2.0
Fi gu ra 10 Il prefisso permette di rintracciare facilmente gli oggetti
28 VBJ N. 67 - Gennaio/Febbraio 2006
// Column: Picture
// ...
VB.NET
For Each table As Tdo.Common.Entities.Tables.ITdoTable In
tdo.TdoTables
System.Diagnostics.Debug.WriteLine(String.Format
(“Table: {0}”, table.EntityName))
For Each column As Tdo.Common.TdoTypes.ITdoColumn In
table.TdoColumns
System.Diagnostics.Debug.WriteLine(String.Format
(“ Column: {0}”, column.ColumnName))
Next
Next
‘Output Window sample:
‘---------------------
‘Table: [Order Details]
‘ Column: OrderID
‘ Column: ProductID
‘ Column: UnitPrice
‘ Column: Quantity
‘ Column: Discount
‘Table: Categories
‘ Column: CategoryID
‘ Column: CategoryName
‘ Column: Description
‘ Column: Picture
‘ ...
Diamo un rapido sguardo agli eventi - Tabella
1 - che Tdo supporta sia nella classe TdoHelper
che nelle specifiche classi db-objects; nel Lista-
to 27 un esempio di come utilizzare tali eventi
per realizzare un componente custom di logging
per tutte le operazioni svolte tramite Tdo:
Listato 27:
C#
public void example_028()
{
tdo.StatementCompleted += new StatementCompletedEvent-
Handler(tdo_StatementCompleted);
DataTable dtEmployees = tdo.tEmployees.SelectDataTable();
//tdo_StatementCompleted raised
}
public void tdo_StatementCompleted(object sender,
StatementCompletedEventArgs e)
{
SqlCommand cmd = (SqlCommand)sender;
int rc = e.RecordCount;
// Write Log information somewhere
System.Diagnostics.Debug.WriteLine(String.Format
(“Text: {0} - Type: {1} - RecordCount: {2}”,
cmd.CommandText, cmd.CommandType, rc));
}
VB.NET
Public Sub example_028()
Dim dtEmployees As DataTable = tdo.tEmployees.
SelectDataTable()
‘tdo_StatementCompleted raised
TDO 2.0
Fi gu ra 11 Anche l'Intellisense aiuta nel lavoro
29N. 67 - Gennaio/Febbraio 2006 VBJ
TDO 2.0
Fi gu ra 12 Ogni classe che espone una tabella ha tutti i campi della tabella stessa esposti come proprietà
30 VBJ N. 67 - Gennaio/Febbraio 2006
End Sub
Private Sub tdo_StatementCompleted(ByVal sender As
Object, ByVal e As System.Data.StatementCompletedEventArgs)
Handles tdo.StatementCompleted
Dim cmd As SqlCommand = DirectCast(sender, SqlCommand)
Dim rc As Integer = e.RecordCount
‘ Write Log information somewhere
System.Diagnostics.Debug.WriteLine(String.Format
(“Text: {0} - Type: {1} - RecordCount: {2}”,
cmd.CommandText, cmd.CommandType, rc))
End Sub
ConclusioniTdo è dotato di altre caratteristiche che sa-
rebbe impossibile, per ragioni di spazio, citare
tutte insieme; ecco allora un breve elenco di
altre importanti feature offerte da Tdo:
• le classi Tdo ed il codice generato da Tdo-
CodeGenerator sono serializzabili sia in
formato binario che XML;
• tutte le classi sono CLS-Compliant (in al-
cuni casi viene fatta eccezione per il Da-
taSet tipizzato);
• tutto è tipizzato – a fronte di cambiamen-
ti nel database, per esempio si modifica il
nome o il tipo di un campo di una tabella,
il nuovo codice Tdo generato, segnala even-
tuali anomalie in fase di compile-time;
• tutti gli statement T-SQL che Tdo genera a
run-time sono parametrici (riuso dell’execu-
tion plan di Sql, no sql-injection, ecc…);
• un solo linguaggio (C# o VB.NET) per
scrivere statement T-SQL e quindi clau-
sole where ad esempio scritte in .NET (Tdo
Sql Expression DOM);
• documentazione Xml: tutto il codice ge-
nerato da Tdo è già documentato trami-
te .NET Xml Documentation ed il codice
è organizzato in #region, dandoci così un
maggior senso di “pulizia”;
• performance: Tdo è più veloce del 45% ri-
spetto a equivalenti operazioni compiute
TDO 2.0
Fi gu ra 13 Il generatore di codice TdoCodeGenerator produce un Dataset tipizzato di ADO.NET 2.0
31N. 67 - Gennaio/Febbraio 2006 VBJ
con l’oggetto SqlDataAdapter e del 7% ri-
spetto a statement non parametrici ese-
guiti tramite l’oggetto SqlCommand di
ADO.NET 2.0;
• può essere utilizzato per operazioni di data-
bindings;
• è interoperabile con COM/COM+;
• 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…);
tali funzioni possono essere utilizzate in
espressioni Tdo Sql Expression DOM.
• nel namespace Tdo.Common esiste una
classe Utility per scrivere su disco ad
esempio un intero oggetto Tdo in for-
mato Xml ed eventualmente rileggerne
il contenuto in un secondo momento (un
po’ come i metodi DataSet.WriteXml e
DataSet.ReadXml).
Arrivati a conclusione di quest’articolo ci
poniamo dunque la fatidica domanda: “Per-
chè usare Tdo anziché ADO.NET 2.0 ?”
Fatto salvo che la risposta a tale domanda
è assolutamente personale, possiamo però
individuare alcuni punti di forza di Tdo ri-
spetto al modello offerto da ADO.NET 2.0.
Innanzitutto Tdo non ha la pretesa di so-
stituirsi ad ADO.NET ma anzi ne costituisce
un complemento ed esso stesso ne fa uso,
sfruttando però al massimo tutte le carat-
teristiche offerte da MS Sql Server che in
ADO.NET, per ragioni di “generalità” del co-
dice, non sono state implementate (almeno
per il nostro specifico database).
La caratteristica più importante e signifi-
cativa di Tdo risiede proprio nella tipizza-
zione delle classi (Typed data object) rispet-
to al “nostro” database e non ad un DB ge-
nerico come giustamente ADO.NET, in fase
di progettazione ha dovuto fare (tutto è un
“object”).
Personalmente ho sempre avuto gli “in-
cubi” a cablare il nome di una tabella o il
nome di un campo direttamente nel codice
o a fare continuamente operazioni di casting
da object al mio tipo di dato e col passare
TDO 2.0
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
Tdo, a partire da questa
versione (2.0)
è divenuto un progetto
open source
32 VBJ N. 67 - Gennaio/Febbraio 2006
degli anni… ho visto molti sviluppatori im-
pazzire con database di configurazione (con
dentro i nomi delle tabelle e dei campi), file
xml chilometrici o peggio ancora sviluppa-
tori tesserati ai fun-club “tutto con Stored
Procedure” ☺ mascherandosi dietro al fin-
to motivo delle prestazioni.
Proprio su questo argomento qualcuno
di voi potrebbe obiettare dicendo: “ma ci
sono i DataSet tipizzati”; giusto, ma van-
no bene solo per operazioni disconnesse.
In ADO.NET 2.0 c’è stato un tentativo da
parte di Microsoft (con i TableAdapters) di
compensare questa mancanza, ma in realtà
il gap è ancora grande.
I TableAdapters, che poi non sono nient’al-
tro che SqlDataAdapter uno per ogni tabella,
ed i comandi Insert/Update/Delete e Select
che il dataset tipizzato genera, sono ancora
troppo generici e poco adattabili alle diver-
se tipologie di query di cui si ha bisogno a
run-time. Ad esempio il metodo Insert pre-
vede che gli vengano passati TUTTI i valori
dei campi; e se volessimo inserirne solo al-
cuni e settare gli altri a Null?!
Oppure se volessimo eseguire una select
con clausola where? Saremmo costretti, an-
cora una volta, a scrivere la clausola where di-
rettamente nel testo della query. L’altro pun-
to di maggior rilievo è dato dal fatto che Tdo
è molto più “spinto” per operazioni in moda-
lità connessa, delegando ad ADO.NET 2.0 la
parte disconnessa.
Anche qui non so quanti di voi ad esem-
pio utilizzano DataSet e DataAdapter per
operazioni di aggiornamento in applicazioni
ASP.NET; personalmente lo evito, non tanto
per questioni di performance, ma perchè si fi-
nisce sempre ad usarlo allo vecchia maniera
stile ADO VB6: l’utente modifica un record in
una schermata… e subito corriamo ad aggior-
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-
dono molte in giro.
Un altro punto a favore di Tdo è la possibili-
tà di scrivere espressioni T-SQL direttamente
in C# o VB.NET, quindi un solo linguaggio e
soprattutto controllo sui tipi in fase di compi-
lazione. Ultimo punto ma non il meno impor-
tante è che Tdo è open source: sia i sorgenti
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-
ce e maggior controllo. Chiunque fosse inte-
ressato a partecipare al continuo sviluppo di
Tdo o semplicemente per maggior chiarimen-
ti può mettersi in contatto con me.
Bibliografia[1] Andrea Ferendeles - “Performance con
GAC e NGEN in ASP.NET”, VBJ n.64/
Infomedia, 2005.
[2] Andrea Ferendeles - “Tdo Typed Data Ob-
ject 1.0”, VBJ n.62/Infomedia, 2005.
Riferimenti[3] Tdo home site: http://tdo.sourceforge.net
(documentazione e quickstart).
[4] Tdo sourgeforge site: http://sourceforge.net/
projects/tdo (download, news, forum, mail-
ing list).
TDO 2.0
Il pacchetto installa
sia la libreria
di base Tdo.dll che
il generatore di codice
TdoCodeGenerator.exe
Le classi Tdo ed il codice
generato da TdoCodeGene-
rator sono serializzabili sia in
formato binario che XML
34 VBJ N. 67 - Gennaio/Febbraio 2006
Multithreadingsemplice e robusto in
applicazioni Windows Forms 1.1 e 2.0
di Francesco Balena
Scrivere applicazioni Windows Form mul-
ti-threaded non è mai stato un compi-
to semplice. Ad esempio, occorre pren-
dere in considerazione tutti i possibili fattori
che possono portare a deadlock oppure a erro-
ri derivanti da race condition oppure ancora a
dati inconsistenti causati dall’accesso non sin-
cronizzato a variabili o altre risorse condivise
tra i vari thread dell'applicazione. I problemi di
questo tipo sono in realtà comuni a tutte le ap-
plicazioni che eseguono codice su più thread e
si possono risolvere solo mediante una attenta
analisi del codice e un uso accorto delle primi-
tive di sincronizzazione offerte dal linguaggio
(lock in C#, SyncLock in VB.NET) e dal .NET
Framework (gli oggetti Monitor, Mutex, Reade-
rWriterLock, Interlocked, ManualResetEvents e
AutoResetEvents), o ancora le API messe a di-
sposizione da Windows (ad esempio i semafo-
ri, che però sono disponibili come oggetti ma-
naged nella versione 2.0 di .NET).
A questi problemi si aggiunge
però un’altra limitazione, che
è peculiare delle sole applica-
zioni Windows Form: l’impos-
sibilità di invocare un metodo
o accedere alle proprietà di un
controllo di un form (o un altro
elemento dell'interfaccia uten-
te) dal codice in esecuzione da
un thread secondario.
Ad esempio, se il thread se-
condario sta eseguendo una
azione in background, non è
permesso aggiornare un con-
trollo ProgressBar per infor-
mare l’utente sullo stato di
avanzamento dell'azione.
Per essere più precisi, ac-
cedere a un controllo da un
thread secondario è un'azione
fortemente sconsigliata perchè
MultithreadingChi sviluppa in .NET 2.0 può scrivere applicazioni Win-
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.
TECNICHE
35N. 67 - Gennaio/Febbraio 2006 VBJ
controllo che è stato creato nel thread di in-
terfaccia utente solo per mezzo di un com-
plicato sistema di delegate.
In pratica, il codice che gira nel thread se-
condario deve creare un delegate che pun-
ta alla procedura che vuole eseguire nel
thread UI e deve passare questo delegate
al metodo Invoke del form (vedremo i det-
tagli tra breve).
Poichè il meccanismo non è proprio sem-
plice, molti programmatori lo implementano
male o non lo implementano affatto.
Per semplificare la vita agli sviluppatori,
.NET 2.0 contiene un nuovo controllo chia-
mato BackgroundWorker. L’uso di questo
controllo è davvero banale. Supponiamo ad
esempio che il thread secondario debba ese-
guire un countdown, contando da N a 0 e
mostrando su un controllo Label il numero
dei secondi che mancano alla fine del pro-
cesso. Ecco nel Listato 1 come si può fare
tutto ciò in VB 2005.
Per maggiori informazioni sul control-
lo BackgroundWorker si consulti la docu-
mentazione della versione 2.0 del .NET Fra-
può portare a malfunzionamenti o errori fa-
tali dell'applicazione: in alcuni casi un bug
di questo tipo può rimanere “in sonno” per
giorni o settimane, ma nella maggioranza
dei casi prima o poi un'applicazione che con-
tiene questo errore di programmazione an-
drà rovinosamente in crash, per giunta la-
sciandovi ignari di cosa sia realmente avve-
nuto. Questo è un errore talmente comune
che in .NET 2.0 il comportamento delle ap-
plicazioni Windows Forms è stato modifica-
to e ogni tentativo di accedere a un controllo
creato in un altro thread genera un'eccezio-
ne se il programma esegue in modalità De-
bug: in modo che il programmatore si possa
rendere conto del problema sin dalla fase
di test (l’errore non avviene in modalità Re-
lease perchè avrebbe reso inutilizzabili le
poche applicazioni esistenti in cui l’errore
esiste ma non ha effetti rovinosi).
Il controllo BackgroundWorkerdi .NET 2.0
In .NET 1.1 un thread secondario – detto
anche thread non-UI – può accedere a un
Fi gu ra 1 Il controllo BackgroundWorker in Visual Studio 2003 a design time
TECNICHE
36 VBJ N. 67 - Gennaio/Febbraio 2006
mework. Nonostante le sue indubbie qualità,
il controllo BackgroundWorker non è esente
da difetti e limiti. Ad esempio:
• Non permette di impostare la priorità del
thread secondario.
• Non permette di impostare il nome del
thread secondario, il che può rendere
difficoltoso il debug dell'applicazione (il
nome del thread serve proprio per iden-
tificare in modo univoco quale thread sta
eseguendo il codice su cui abbiamo posto
un breakpoint).
• Non offre alcun altro tipo di controllo sul
thread, ad esempio la possibilità di sospen-
derlo o terminarlo prima del termine na-
turale dell'esecuzione.
• Non permette di decidere se il thread se-
condario debba essere preso dal pool dei
thread di .NET o creato mediante un og-
getto System.Threading.Thread.
Per tutti questi motivi, ma anche per dare
la possibilità di utilizzare il controllo Back-
groundWorker nelle applicazioni .NET 1.1,
ho deciso di creare una versione custom del
controllo (Figura 1). Questa versione è scrit-
ta in C# per Visual Studio 2003 ed è qua-
si perfettamente compatibile con la versio-
ne per 2.0. Questo significa che se utilizzate
questo controllo nelle applicazioni .NET 1.1
potrete poi migrare il codice senza proble-
mi su .NET 2.0 (utilizzando il “vero” control-
lo BackgroundWorker). A meno che, ovvia-
mente, non abbiate deciso di usare una delle
feature extra sopra descritte, che il mio con-
trollo BackgroundWorker aggiunge al con-
trollo di .NET 2.0.
Li sta to 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) NextEnd 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
TECNICHE
37N. 67 - Gennaio/Febbraio 2006 VBJ
Il BackgroundWorker control customIl sorgente completo del controllo, allega-
to all’articolo insieme a un semplice pro-
getto demo, contiene circa 600 righe di C#.
Eliminando però i commenti XML e qual-
che porzione meno interessante, il sorgen-
te del controllo può essere condensato come
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
“esercizio per il lettore”.
La proprietà booleana WorkerUsesThrea-
dPool - che è assente nel control Back-
groundWorker di .NET 2.0 - indica se il
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
secondo offre un maggiore controllo sul
thread. Ad esempio, le proprietà Thred-
Priority e ThreadName sono utilizzate solo
nel secondo caso.
Se WorkerUsesThreadPool è True, allora
l’applicazione può utilizzare la proprietà
Thread (a sola lettura), che restituisce l’og-
getto System.Threading.Thread corrispon-
dente al thread secondario in cui viene ese-
guito il codice nell’evento DoWork. Grazie a
questa proprietà l’applicazione può sospen-
dere il thread, abortirlo, cambiare la priori-
tà mentre è in esecuzione, e così via. Tut-
te queste feature non sono disponibili se il
controllo utilizza un thread preso dal pool
(e non sono disponibili nel controllo stan-
dard fornito con .NET 2.0).
Quando l’applicazione chiama il metodo
RunWorkerAsync, il controllo invoca il me-
todo protected OnDoWork, dove si fa partire
il thread in uno dei due modi supportati. Se
WorkerUsesThreadPool è True, la procedu-
ra che richiama il thread secondario viene
invocata per mezzo di delegate asincroni:
questa tecnica permette, tra le altre cose, di
passare argomenti al thread senza bisogno
di adottare un particolare accorgimento.
Se invece WorkerUsesThreadPool è False,
si pone il problema di rendere disponibile
TECNICHE
al thread secondario gli argomenti passati
a RunWorkerAsync.
Il codice in OnDoWork memorizza questi
argomenti nella variabile privata evArgs e
poi chiama la procedura ThreadEntryPoint,
la quale recupera tali valori e li passa final-
mente al gestore dell’evento DoWork.
Intercettare il termine dell’esecuzione del
thread non pone particolari difficoltà: se si
usano i delegate asincroni basta registrare
come completion method del delegate il me-
todo DoWorkCompleted, il quale a sua volta
chiama OnRunWorkerCompleted: quest’ul-
timo metodo scatena l’evento RunWorker-
Completed. Se invece stiamo usando un og-
getto Thread, al ritorno dalla chiamata asin-
crona il metodo ThreadEntryPoint chiama
direttamente OnRunWorkerCompleted. Il
passaggio tra i due thread - il thread se-
condario usato dal controllo e il thread di UI
- è codificato nel metodo privato InvokeDe-
legate, che è in grado di attivare gli eventi
ReportProgress e RunWorkerCompleted (ri-
cordo che questi eventi devono essere ese-
guiti nel thread UI). Il metodo InvokeDele-
gate deve eseguire il dispatch della chiamata
al thread IU mediante il metodo Invoke di
un oggetto che supporta l’interfaccia ISyn-
chronizeInvoke. I casi che si presentano a
questo punto sono i seguenti:
a) la proprietà SynchronizeObject è imposta-
ta a un valore non-Nothing. In questo caso
abbiamo l’oggetto su cui chiamare il me-
todo Invoke.
b) la proprietà SynchronizeObject non è stata
impostata. In questo caso il metodo Invoke-
Scrivere applicazioni
Windows Form multi-
threaded non è mai stato
un compito semplice
38 VBJ N. 67 - Gennaio/Febbraio 2006
Delegate visita tutti gli oggetti che hanno registrato l’evento da scatenare (per mezzo del metodo GetInvocationList) e utilizza il primo che supporta l’interfaccia ISynchro-nizeInvoke.
c) Se nessun oggetto supporta questa inter-faccia, l’evento viene sollevato nel thread secondario (ovviamente, in questo caso non si dovrebbe accedere ai controlli di inter-faccia utente).
L’ultimo punto di interesse è nel fatto che il controllo BackgroundWorker supporta l’inter-faccia IExtenderProvider e quindi implementa il suo metodo CanExtend. Come è noto, que-sta interfaccia è normalmente usata per im-plementare i provider control, come HelpPro-vider e ToolTip, ma in questo caso il controllo non espone alcuna proprietà extender, quindi l’implementazione di questa interfaccia sem-brerebbe inutile a prima vista.
L’unico motivo per implementare questa in-terfaccia è la possibilità di impostare automati-camente la proprietà SynchronizingObject pari al valore del form al momento della creazione del controllo. Questo è infatti il comportamen-to di default del controllo BackgroundWorker in .NET 2.0, come pure di altri componenti che devono sollevare eventi in thread secondari, ad esempio FileSystemWatcher.
Il metodo CancelAsyncIl metodo CancelAsync richiede una spiega-
zione ulteriore. In realtà, questo metodo non fa null’altro che impostare a True il valore della proprietà CancellationPending. È compito del codice nell’evento DoWork di terminare “dol-cemente” la sua esecuzione quando scopre che
questa proprietà è True. Ad esempio, il codice che esegue il countdown dovrebbe essere mo-dificato in questo modo:
Private Sub BackgroundWorker1_DoWork(ByVal sender As O
bject, 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)
If BackgroundWorker1.CancellationPending Then Exit Sub
Next
End Sub
Per quanto questo meccanismo possa sembra-re poco raffinato a prima vista, si consideri che questa tecnica è semplice da implementare e mette comunque al riparo dall'eventualità che una cancellazione “brusca” del thread provochi 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 deve davvero essere interrotto a tutti i costi. In questo caso può essere conveniente impostare la proprietà WorkerUsesThreadPool a False e usare il metodo Abort dell’oggetto Thread espo-sto dal controllo:
BackgroundWorker1.Thread.Abort()
Quando l’esecuzione del thread secondario è interrotta in uno dei due modi descritti, la pro-prietà Canceled dell’oggetto RunWorkerComple-tedEventArgs ricevuto dall’evento RunWorker-Completed è impostata al valore True, e il codi-ce può determinare il motivo della cancellazio-ne testando la proprietà Error.
ConclusioniIl controllo BackgroundWorker descritto nel-
l’articolo può essere usato nelle applicazioni .NET 1.1 per prepararsi alla migrazione ver-so .NET 2.0, ma può anche essere usato in VisualStudio 2005 per sfruttare alcune fea-ture che Microsoft ha “dimenticato” di inse-rire nel controllo.
TECNICHE
Per semplificare la vita agli
sviluppatori, .NET 2.0 contie-
ne un nuovo controllo chia-
mato BackgroundWorker
40 VBJ N. 67 - Gennaio/Febbraio 2006
File compatti
e illeggibili:
Crittografia
di Scott Swigart, Swigart Consulting LLC
In questa serie di due articoli, si vedrà come
si possono aggiungere facilmente funziona-
lità di crittografia e di compressione dati
(ZIP) alle applicazioni VB6 esistenti utilizzando
il .NET Framework. Benché la crittografia e la
compressione possano non sembrare tecnologie
correlate, se si pensa ad esse, ciascuna prende
un insieme di dati ed esegue una trasformazio-
ne su di esso. Nel caso della crittografia, i dati
vengono resi illeggibili, mentre con la compres-
sione i dati vengono resi più piccoli. Si vedrà an-
che che entrambe utilizzano molte delle mede-
sime tecnologie sottostanti.
IntroduzioneHo un segreto, e non voglio
che lo conosciate. In effetti, ho
una applicazione, e voglio es-
sere in grado di memorizzare
le informazioni in un formato
crittografato in modo che non
possano essere lette da utenti
non autorizzati. Visual Basic 6
non ha alcuna funzionalità na-
tiva per eseguire la crittografia
o la decrittazione dei dati, ma
il Microsoft .NET Framework
dispone in modo nativo di crit-
tografia forte. In questo arti-
colo descrivo un componente
VB.NET che fornisce le funzio-
nalità di crittografia e di decrit-
tazione, e si può utilizzarlo mol-
to facilmente da VB6, ASP o da
qualsiasi altro ambiente COM.
Prima di tutto, è bene esamina-
re come Visual Basic 6 permet-
te di operare con i file. VB6 for-
nisce diverse keyword del lin-
guaggio per l’I/O su file:
‘ Lettura da file con Visual Basic 6
Dim dataIn As String
Crittografia
Crittografia e compressione di file in VB6 utilizzando il
Microsoft .NET Framework
Prima puntata
Scott Swigart è consulente, autore e speaker su tecnologie
emergenti e convergenti. Nella sua professione ha lavorato con
una vasta gamma di tecnologie, iniziando con la programmazio-
ne del Commodore 64 all’età di 12 anni, scrivendo diagnostica
dell’hardware per sistemi UNIX in C++, e realizzando applica-
zioni Windows desktop e Web. Negli anni, Scott ha lavorato
allo sviluppo di componenti, con tecnologie XML, .NET, Web
service e altri linguaggi, piattaforme e paradigmi. Grazie a que-
sta esperienza, Scott ha visto come la tecnologia evolve nel
tempo, e si è concentrato nell’aiutare le organizzazioni a trarre
il massimo vantaggio dalla tecnologia odierna, pur preparandosi
alla tecnologia di domani. Scott è anche un Microsoft MVP,
ed è coautore di numerosi libri e articoli. Scott può essere
contattato all’indirizzo [email protected].
VB6
© 2006 Microsoft Corporation. All right reserved
41N. 67 - Gennaio/Febbraio 2006 VBJ
Fondamenti sull’I/O di file con VB.NETPrima di affrontare la crittografia, è importan-
te comprendere come Visual Basic .NET gesti-
sce l’I/O dei file. Il Microsoft .NET Framework
utilizza qualcosa noto come “stream” quando
opera con i file, come è mostrato qui:
‘ I/O di file con Visual Basic .NET
Dim sw As New StreamWriter(“C:\log.txt”)
sw.WriteLine(“This is a test”)
sw.Close()
Si può vedere che Visual Basic .NET fornisce
specificamente delle classi per la lettura e la scrit-
tura da file (e da altri dispositivi di I/O). In que-
sto caso, il codice utilizza la classe StreamWri-
ter per scrivere righe di testo in un file. L’og-
getto StreamWriter è molto comodo, poiché non
si deve tener traccia di un handle di file, e tut-
te le operazioni per operare con un file vengo-
no evidenziate da Intellisense. Oltre alla classe
StreamWriter, ci sono anche le seguenti classi:
FileStream Lettura e scrittura da un fi-
le binario.
NetworkStream Lettura e scrittura da una
rete.
MemoryStream Lettura e scrittura dalla me-
moria come se fosse un file.
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-
te si vuole lavorare con
righe, non con blocchi
di 1024 byte, pertanto
Open someFileName For Input As #1
Do Until Eof(1)
Line Input #1, dataIn
Debug.Print dataIn
Loop
Close #1
Con VB6, si eseguono tutte le proprie opera-
zioni su un handle di file (#1 in questo caso),
e sta a voi gestire questo handle di file. Non vi
è un unico oggetto che incapsula le operazioni
su file, esistono invece diverse keyword (Open,
Close, Input, ecc). Se si vuole eseguire la crit-
tografia e la decrittazione con Visual Basic 6,
si dovrebbero leggere tutti i dati in memoria,
crittografarli utilizzando un componente di ter-
ze parti (generando ciò che è noto come “testo
cifrato”), e poi memorizzare il testo cifrato su
un file di output. Sarebbe auspicabile poter far
fluire le informazioni attraverso il proprio pro-
gramma, ma ciò non sarebbe facile da fare. Per
facilitare realmente una facile crittografia da
VB6, ho creato un componente di crittografia/
decrittazione. In seguito si vedrà come questo
componente è stato implementato, ma è bene
menzionare che non è necessario comprende-
re i dettagli di implementazione per utilizza-
re effettivamente il componente. Se si vuole
semplicemente utilizzare il componente sen-
za addentrarsi in come funziona, basta legge-
re il paragrafo “Crittografia VB6”.
VB6
Fi gu ra 1 Tentativo di visualizzare i dati crittografati con Notepad
42 VBJ N. 67 - Gennaio/Febbraio 2006
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 decodificati:
‘ Pila di stream per abilitare la crittografia in VB.NET
fs = New FileStream(fileName, FileMode.Create,
FileAccess.Write)
cs = New CryptoStream(fs, rc2.CreateEncryptor(),
CryptoStreamMode.Write)
sw = New StreamWriter(cs)
Qui, viene creata una classe FileStream per abilitare la scrittura su uno specifico file su di-sco. Un CryptoStream si basa sul FileStream in modo che le informazioni siano crittografa-te prima che vengano inviate al FileStream. Lo StreamWriter viene quindi basato sul Crypto-Stream in modo da avere una comoda funzio-nalità di WriteLine, piuttosto che dover opera-re con blocchi di dati a lunghezza fissa. Il Cryp-toStream è ciò che crea la magia della critto-grafia. Supporta molti algoritmi di crittografia, e pertanto deve essere configurato con alcune informazioni iniziali prima di essere utilizzato. Per questo componente, è stato utilizzato l’al-goritmo di crittografia RC2 [1], poiché fornisce una crittografia robusta e veloce. Tuttavia, se si vuole utilizzare la crittografia DES, Triple-DES o Rijndael, questo codice può essere modificato per utilizzare questi altri algoritmi:
‘ Configurazione della crittografia RC2 con VB.NET
Dim rc2 As New RC2CryptoServiceProvider
rc2.IV = IV
Dim pdb As New PasswordDeriveBytes(password, Nothing)
rc2.Key = pdb.CryptDeriveKey(“RC2”, “SHA1”, 128, IV)
La crittografia simmetrica si basa sull’utilizzo di una chiave segreta per crittografare le infor-mazioni. In questo caso, la chiave sarà fornita a questo algoritmo sotto forma di password. Dalla password, viene derivata una chiave crittogra-fica utilizzando la classe PasswordDeriveBytes. Per ulteriore sicurezza, può essere utilizzato un vettore di inizializzazione (IV). Un IV viene uti-lizzato per assicurare che se un documento con-tiene sezioni identiche di testo in chiaro, non vengano crittografate come testo cifrato identi-co. Ciò rende anche la chiave più sicura se più file vengono crittografati utilizzando la stessa chiave. Quando si utilizza questo componente, specificare un IV è opzionale.
Crittografia in VB6 Nuovamente, benché qui vengano trattate le
basi della crittografia, non è necessario conosce-re nulla su .NET o sulla crittografia per utiliz-zare questo componente. Da VB6, l’utilizzo di questo componente è semplice, come si vede nel seguente frammento di codice:
‘ Utilizzando del componente di crittografia da VB6
Dim e As wrappers.EncryptedFileWriterWrapper
VB6
Fi gu ra 2 Il form dell’applicazione d’esempio
43N. 67 - Gennaio/Febbraio 2006 VBJ
Set e = New wrappers.EncryptedFileWriterWrapper
e.Open “c:\file.bin”, txtPassword, False, “”
e.WriteLine (txtMessage)
e.Close
Come si vede, ora che l’EncryptedFileWri-
terWrapper è stato creato, è banale da utiliz-
zare. Basta crearne una istanza, passargli un
nome di file e una password, e iniziare a scri-
vere dati. I dati verranno crittografati e scritti
su file (Figura 1). La lettura e la decrittazione
dei dati è ugualmente semplice:
‘ Lettura e decrittazione dei dati utilizzando VB6
Dim e As wrappers.EncryptedFileReaderWrapper
Set e = New EncryptedFileReaderWrapper
e.Open “c:\file.bin”, txtPassword, “”
While Not e.EOF
txtMessage = txtMessage & e.ReadLine & vbCrLf
Wend
e.Close
Nuovamente, basta specificare un nome di
file e una password e iniziare a leggere. I dati
vengono decodificati dal componente e resti-
tuiti come semplice testo. Per vostra comodità,
ho realizzato un’applicazione VB6 che utilizza
il componente per eseguire la crittografia e la
decrittazione. Per utilizzare questo componen-
te, può essere necessario fare alcune cose:
1. Scaricare e installare il Microsoft .NET
Framework SDK 1.1 (se si è installato Vi-
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-
pria applicazione VB6.
4. Nel codice dell’articolo, aprire il progetto
VB6 nella cartella “Encryption”.
5. Premere il tasto F5 per eseguire l’appli-
cazione di esempio (Figura 2).
6. Immettere qualsiasi stringa come pas-
sword, e premere Save. Il testo viene
salvato in “c:\file.bin”. Se si apre questo
file in Notepad, si vedrà che è crittogra-
fato. La textbox della password viene an-
che azzerata.
7. Se si clicca su Load, l’informazione ver-
rà recuperata, decodificata e visualizzata
nella textbox.
Se si cambia password, in modo che non
corrisponda alla password con cui è sta-
to salvato il testo, si vedrà che il file non
viene decodificato (Figura 3)
ConclusioniVisual Basic 6 non fornisce una funzionalità
nativa di crittografia/decrittazione, ma Micro-
soft .NET Framework sì.
Come accade in tutto il .NET Framework, è
relativamente semplice esporre questa funzio-
nalità in modo che possa essere utilizzata da
un'applicazione VB6.
Questo articolo fornisce una soluzione pre-
confezionata per aggiungere la funzionalità di
crittografia e decrittazione alla propria appli-
cazione VB6.
E bastano solo poche righe di codice!
Riferimenti[1] http://en.wikipedia.org/wiki/RC2
[2] http://en.wikipedia.org/wiki/Data_
Encryption_Standard
[3] http://en.wikipedia.org/wiki/Triple_DES
[4] http://en.wikipedia.org/wiki/Advanced_
Encryption_Standard
Fi gu ra 3 Se si cambia password, in modo che
non corrisponda alla password con cui
è stato salvato il testo, si vedrà che il
file non viene decodificato
44 VBJ N. 67 - Gennaio/Febbraio 2006
Executable UML
di Lorenzo Vandoni
Nel precedente numero di questa rubrica
abbiamo parlato della Model Driven Ar-
chitecture (MDA) proposta dall’Object
Management Group (OMG), che si pone l’obiet-
tivo di separare completamente la logica appli-
cativa dalla piattaforma sulla quale l’applicazio-
ne dovrà essere implementata.
In particolare, abbiamo visto come questa archi-
tettura preveda la realizzazione di un modello di
alto livello denominato Platform Independent Mo-
del (PIM), che descriva il comportamento dell’ap-
plicazione. Questo modello verrà successivamen-
te convertito, più o meno automaticamente, in un
modello specifico per una o più piattaforme, come
CORBA, COM, Java o .NET (Platform Specific Mo-
del, o PSM), e quindi in un programma funzionante
(Platform Specific Implementation, o PSI).
Executable UML, come vedremo, non è altro che
uno strumento per mettere in pratica le linee gui-
da di MDA.
Da UML a xUML
UML è un formalismo di alto livello, che or-
mai si è fortunatamente imposto come la nota-
zione di riferimento per la stesura di diagrammi
di analisi e di progettazione. Al di là del fatto di
apprezzare o meno questo tipo di notazione, in-
fatti, non si può non considerare positivo il fat-
to che questa sia universalmente riconosciuta, e
che abbia sostituito la babele
di linguaggi e modelli propo-
sti ed utilizzati nelle scorse de-
cadi. Executable UML (xUML)
estende UML con l’introduzio-
ne di aspetti dinamici che con-
sentono ai diagrammi di essere
eseguibili, verificabili, e quindi
traducibili in codice applicativo
da speciali compilatori. Di fat-
to, in questo caso alcuni auto-
ri parlano di “Executable and
Translatable” UML, indicato
con l’acronimo xtUML.
xUML è quindi un sottoinsie-
me di UML (nel senso che ne
utilizza solo alcuni costrutti,
come Package, diagrammi di
classe e diagrammi di stato), e
una sua estensione, nel senso
che adotta strumenti aggiuntivi
per specificare dettagli e aspet-
ti dinamici dei sistemi model-
lati. Inoltre xUML è una meto-
dologia di lavoro automatizza-
ta, basata sulla notazione UML,
che formalizza alcuni concetti
chiave di MDA. xUML non è
ancora uno standard, ma pro-
babilmente lo diventerà pre-
sto, perché OMG ha già avvia-
to il processo di standardizza-
zione con una richiesta di pro-
poste in questo senso. In ogni
caso xUML è basato su questi
concetti:
Executable UML (xUML) è un formalismo che consente di
applicare concretamente le linee guida fornite dalla Model
Driven Architecture (MDA)
Lorenzo Vandoni è laureato in Informatica, ed è uno spe-
cialista di progettazione e sviluppo con tecniche e linguaggi
object-oriented. Ha collaborato alla realizzazione di framework,
strumenti di sviluppo e software commerciali in C++, Java e
Visual Basic. Può essere contattato tramite e-mail all’indirizzo
SOFTWARE ENGINEERING
45N. 67 - Gennaio/Febbraio 2006 VBJ
do in alcuni casi duplicazioni e ridondanze.
Con xUML, in un certo senso Mellor si pren-
de una tardiva rivincita, proponendo una no-
tazione semplificata e reintroducendo un si-
stema di generazione automatico.
Il processo di sviluppoLa più singolare particolarità di xUML è la
completa separazione tra la stesura del mo-
dello dell’applicazione e la definizione delle
regole di conversione, che guidano la tradu-
zione di elementi del modello in codice sor-
gente. In pratica, chi si preoccupa di definire
quale dovrà essere il comportamento dell’ap-
plicazione è virtualmente indipendente dal-
l’esperto della piattaforma di riferimento, che
invece ha il compito di definire la corrispon-
denza tra gli elementi dei diagrammi UML e
il codice sorgente.
Al modello xUML vengono aggiunti tutti i
dettagli necessari sia per l’esecuzione e il test
del modello, sia per la successiva conversione
in codice. L’esistenza di questi dettagli con-
sente anche di eseguire dei test di tipo forma-
le sul modello, per verificarne la consistenza
e la completezza.
Gli aspetti dinamici del modello vengo-
no specificati tramite un “action language”
come Action Specification Language (ASL),
che consente di specificare il comportamento
runtime, mentre i vincoli sull’esecuzione ven-
• il modello dell’applicazione, descritto con
UML, è indipendente da qualsiasi partico-
lare piattaforma applicativa
• tale modello può essere mandato in esecu-
zione, o simulato, il che costituisce un ot-
timo modo per verificare la correttezza dei
requisiti e del funzionamento del sistema
modellato
• le caratteristiche di ogni specifica piatta-
forma vengono incapsulate all’interno di
un opportuno “traduttore”, in grado di ge-
nerare codice a partire dal modello UML
Uno degli autori più attivi in questo campo è
Stephen Mellor, coautore, molti anni fa, di un
metodo di sviluppo del software object-orien-
ted, denominato Shlaer-Mellor, che si basava
su una specifica notazione (UML non era an-
cora stato introdotto), ma introduceva già al-
cuni meccanismi di generazione automatica
del codice. In seguito UML è nato come fusio-
ne delle notazioni proposte da Booch, Rum-
baugh e Jacobsen. Fin dalla sua introduzione,
una delle critiche rivolte a UML è quello di es-
sere eccessivamente complesso, e di derivare
questa sua complessità da motivi più politici
che scientifici. In UML sono infatti confluite
diverse caratteristiche delle notazioni origina-
riamente proposte dai tre autori, introducen-
Fi gu ra 1 Il processo di traduzione
SOFTWARE ENGINEERING
46 VBJ N. 67 - Gennaio/Febbraio 2006
gono espressi tramite clausole del linguaggio
Object Constraint Language (OCL).
Questo particolare permette anche di defi-
nire chiaramente delle linee guida per capi-
re quando un modello è veramente comple-
to. Normalmente, infatti, il livello di detta-
glio e il numero di informazioni inserite nel
modello sono lasciati alla sensibilità del pro-
gettista. Con xUML, invece, un modello sarà
completo solo quando potrà essere mandato
in esecuzione.
Dal modello al codice
Il processo di traduzione è basato su tre ele-
menti fondamentali:
• anzitutto vi è un insieme di pattern e re-
gole di traduzione. Un pattern descrive la
traduzione di un elemento xUML in codi-
ce, mentre una regola indica quali pattern
debbano essere utilizzati per tradurre un
diagramma
• un sistema di traduzione, che applica re-
gole e pattern al modello, per generare il
codice corrispondente
• una libreria runtime che contiene funzio-
nalità comuni di supporto al codice gene-
rato. Questa libreria serve, in pratica, per
evitare la generazione di codice ripetitivo,
ed incapsula un insieme di classi e funzio-
ni di utilità
Quando il progettista richiede la generazio-
ne del codice, il sistema di traduzione estrae
le informazioni necessarie dal modello xUML,
e utilizza le regole per selezionare i pattern
da applicare.
Questi pattern possono essere visti come
porzioni di codice generalizzate, in cui alcuni
“spazi” siano stati lasciati vuoti, per poter es-
sere riempiti con le informazioni provenienti
dal modello.
Tipicamente, l’invocazione di un pattern pro-
vocherà la chiamata ad altri sotto-pattern, a
loro volta selezionati applicando le regole di
traduzione.
Chiaramente tutto questo non può essere fatto
su carta, ma richiede il supporto di un tool di
SOFTWARE ENGINEERING
Fi gu ra 2 Il processo di sviluppo con xUML
xUML utilizza solo alcu-
ni costrutti di UML, come
Package, diagrammi di clas-
se e diagrammi di stato
47N. 67 - Gennaio/Febbraio 2006 VBJ
sviluppo. Esempi di strumenti del genere sono
BridgePoint di Project Technology, che può es-
sere trovato su www.projtech.com, e la MDA
Product Suite di Kennedy Carter, che può es-
sere trovata su www.kc.com/products.php.
Benefici di xUMLI benefici legati all’utilizzo di xUML sono
più o meno gli stessi già enunciati nel prece-
dente articolo, relativamente ad MDA. Oltre a
quelli più ovvi, legati al fatto di poter descri-
vere l’applicazione ad alto livello, poter scri-
vere meno codice, e poter agevolmente con-
vertire un’applicazione da una piattaforma (es.
Java su Linux) ad un’altra (es. Microsoft.NET),
vale la pena di citare i seguenti:
• anzitutto, il codice generato può essere ot-
timizzato per la piattaforma di riferimen-
to. Tale ottimizzazione può essere codifi-
cata una volta per tutte nel processo di
traduzione, senza richiedere particolari
competenze da parte degli sviluppatori
• la possibilità di mandare in esecuzione
i modelli UML permette ai clienti e agli
utilizzatori finali di verificare la correttez-
za del modello applicativo fin dalle prime
fasi dello sviluppo
SOFTWARE ENGINEERING
• possibilità di riutilizzo dei modelli appli-
cativi, o parte di essi, indipendentemente
dalla piattaforma di riferimento: un buon
modello di gestione del magazzino scritto
per un’applicazione Java client-server può
tranquillamente essere riutilizzato per rea-
lizzare un sistema per Pocket PC
• facilità di manutenzione, legata al fatto
che modifiche al modello, o al processo
di traduzione, possono essere facilmen-
te trasferite all’applicazione finale. Ad
esempio, l’ottimizzazione di un partico-
lare algoritmo, codificato nel processo
di traduzione ed utilizzato in molti pun-
ti dell’applicazione, potrà essere facil-
mente riportata sull’intera applicazione
semplicemente generando nuovamente
il codice sorgente
• il codice ottenuto è tipicamente ben do-
cumentato. Lo sforzo di documentare il
codice può essere fatto una volta sola ap-
plicandolo ai pattern utilizzati per la ge-
nerazione
Alcune perplessitàLa visione di xUML come meccanismo che
permetterà di abbandonare, in un futuro più
Fi gu ra 3 Il processo di traduzione può essere applicato a più modelli
48 VBJ N. 67 - Gennaio/Febbraio 2006
o meno lontano, linguaggi come Java e C++,
esattamente come da tempo è ormai stato ab-
bandonato l’assembler, è certamente affasci-
nante, ma personalmente non nascondo al-
cune perplessità.
L’idea di tradurre descrizioni di alto livel-
lo in codice non è certamente nuova. Tra la
fine degli anni ‘80 e la prima metà degli anni
’90 erano abbastanza diffusi i cosidetti stru-
menti CASE “di basso livello”, ovvero orien-
tati alla progettazione e alla generazione di
codice. Con questi strumenti, di solito rivol-
ti ad applicazioni per la gestione di databa-
se, lo sviluppatore doveva disegnare un dia-
gramma dello schema della base dati, e poi
aveva a disposizione degli editor grafici per
disegnare le maschere e i report del program-
ma. Contrariamente a quanto accade con am-
bienti di sviluppo visuali come Delphi o Vi-
sual Studio, queste maschere e report veniva-
no automaticamente dotate di molte funzio-
nalità di alto livello. L’architettura complessi-
va del sistema era sorprendentemente molto
simile a quella ora proposta da xUML: un in-
sieme di regole di traduzione, un motore in
grado di interpretarle, e una libreria di run-
time di supporto.
Questi sistemi soffrivano di varie limitazio-
ni: alcune di queste erano legate al periodo
storico in cui erano nati, prima tra tutte la
mancanza di standard come UML, ma altre
potrebbero rimanere valide anche per gli at-
tuali tool basati su xUML. Non bisogna sot-
tovalutare le lezioni del passato.
Tra queste limitazioni, una è data dalla ri-
gidità del processo di traduzione, che porta
ad ottenere applicazioni con un look-and-feel
piuttosto uniforme. Questo aspetto potrebbe
risultare positivo per applicazioni commerciali
da introdurre sul mercato, ma di solito è ne-
gativo nel caso di applicazioni sviluppate su
commissione, in quanto quasi sempre ogni
cliente avrà esigenze particolari.
L’esperienza inoltre ha mostrato che rara-
mente si riuscirà a codificare all’interno dei
pattern tutte le particolari esigenze di un pro-
gramma, e che per gestire le innumerevoli ec-
cezioni sarà necessario intervenire manual-
mente, modificando il codice generato. Oc-
correrà quindi acquisire le necessarie com-
petenze relative alla struttura di questo codi-
ce e della libreria di runtime, nonché adotta-
re qualche meccanismo che permetta di pre-
servare gli interventi manuali a fronte di una
nuova generazione del codice.
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
base dati relazionale, sono difficilmente cat-
turabili con questo tipo di notazione.
ConclusioniIl tentativo di realizzare modelli applicativi
di alto livello che siano eseguibili e consen-
tano quindi di verificare la correttezza delle
specifiche non è nuova, ed era anzi tra i prin-
cipali temi di ricerca nella comunità scien-
tifica agli inizi degli anni ’90. Nel frattem-
po, però, UML si è affermato come notazio-
ne standard per l’analisi e la progettazione
di applicazioni object-oriented, e quindi que-
ste idee hanno trovato nuovi stimoli.
Come già evidenziato nello scorso numero,
non si può non notare come MDA e xUML
vadano quasi in direzione opposta rispetto
alle metodologie agili, che stanno ultima-
mente avendo un grande successo. Da una
parte abbiamo un’attenzione quasi ossessiva
per le fasi di analisi, dall’altra un atteggia-
mento più pragmatico, e una maggiore en-
fasi per il ruolo dei programmatori. Sicura-
mente MDA e xUML trovano una giustifica-
zione nell’ambito di progetti molto grossi,
dove le metodologie agili possono mostra-
re alcuni limiti.
SOFTWARE ENGINEERING
Il sistema di traduzione
applica regole e pattern
al modello per generare il
codice corrispondente
50 VBJ N. 67 - Gennaio/Febbraio 2006
DotNetNuke:creazione di
un modulo
di Pietro Vite
DotNetNuke [5] è un portale scritto in
VB.NET da Shaun Walker ed altri,
usando come base l’applicazione IBuy-
Spy WorkShop, sviluppata da Microsoft come
uno dei Source Project che accompagnano il ri-
lascio di .NET Framework 1.0 Beta.
Nel primo capitolo del libro della Wrox dal titolo
“Professional DotNetNuke ASP.NET Portals” [1],
Shaun Walker racconta la genesi di DotNekNuke,
fino alla attuale versione 3.0. La necessità di svi-
luppare e gestire portali per piccole e medie azien-
de e a basso costo spinge Shaun a rimaneggiare il
codice VB.NET di IBuySpy WorkShop, applicativo
sviluppato da Microsoft per illustrare, con un’appli-
cazione web completa, le potenzialità di ASP.NET.
Nel libro, con molta sincerità, l’autore racconta gli
errori commessi, le difficoltà superate e il gruppo
di persone che si raccolsero intorno al progetto, in-
sieme al concreto supporto di Microsoft stessa, in-
teressata allo sviluppo di questa iniziativa, normal-
mente appannaggio di comunità Perl, Pyton, Php
o, al più, Java. Shaun Walker ha scritto un libro
che mi sento di consigliare a chi può essere interes-
sato a portali Web in tecnologia
Microsoft .NET perchè anche i
capitoli più tecnici, per esempio
quello sulle modalità di installa-
zione e sulle pratiche di ammini-
strazione, sono molto chiari, ma
soprattutto perché è stato scritto
dal creatore di DotNetNuke. Nel
sito www.dotnetnuke.org è an-
che citato un altro libro interes-
sante, edito da PACKT, dal tito-
lo “Building Websites with VB:
NET and DotNetNuke 3.0” [2].
La storia del portale Dot-NetNuke di AWD Sim
Nel 2004 AWD Sim [3] inizia a
valutare la possibilità di dotarsi di
un portale aziendale, molto utile
per la gestione delle informazioni
da erogare alla Rete di Vendita,
ma gli impegni di business piani-
ficati impediscono di trasforma-
rere i requisiti di business in un
progetto software. Nel 2005, dopo
una profonda riorganizzazione, il
management decide di soddisfa-
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
Pietro Vite è IT Manager di AWD Sim Italia. Ha lavorato per
diverse SIM e istituti di credito nell’area Finance e IT. Può essere
contattato tramite e-mail all’indirizzo [email protected].
WEB
51N. 67 - Gennaio/Febbraio 2006 VBJ
I contenuti dell’articolo
Dopo l’installazione dell’applicativo, che non descrivo perché ben documentata sul sito del prodotto, nei due libri citati prima e in alcuni articoli molto dettagliati recupe-rarabili da Google su Internet, illustro bre-vemente il concetto di modulo, l’insieme dei componenti che permettono la perso-nalizzazione del portale e proseguo quindi con la descrizione dello sviluppo di un mo-dulo che estrae da una tabella SQL Server l’ultimo messaggio da mostrare nel com-ponente.
I moduli di DotNetNuke
I moduli sono componenti software che ren-dono disponibili funzionalità di base per la gestione amministrativa di un portale. Di se-guito vedremo alcuni dei moduli disponibili con l’applicazione.
re le pressanti richieste della Rete di Vendita. L’architettura IT di AWD Sim è fortemente le-gata ai prodotti Microsoft. È naturale, quindi, orientare la scelta verso prodotti che valorizzino le competenze del personale IT cresciuti all’in-terno di AWD Sim. Nella primavera del 2005, l’Amministratore Delegato di Debug Software & Tailoring S.p.A. [4], la società software pro-duttrice del prodotto di amministrazione della Rete di Vendita utilizzato da AWD Sim, illustra la soluzione DNN adottata per lo sviluppo del proprio portale aziendale. La scelta di adotta-re DotNetNuke coniuga robustezza e sempli-cità d’uso, qualità dei componenti disponibili su Internet e, ora, anche la possibilità di ap-poggiarsi ad un fornitore competente e pronto a collaborare ai progetti di AWD Sim. Nell’au-tunno del 2005 AWD Sim rende disponibile alla Rete di Vendita i servizi principali richiesti dal Management.
Fi gu ra 1 Il file .ascx del modulo
WEB
52 VBJ N. 67 - Gennaio/Febbraio 2006
• Account Login permette l’accesso profilato• Announcement rende disponibile la pre-
sentazione di annunci su una pagina • Banners permette di mostrare banner agli
utenti che si sono loggati al sito. È possi-bile selezionare il numero e il tipo di ban-ners da presentare
• Contacts mette a disposizione le informa-zioni anagrafiche per un gruppo di lavoro
• Discussions rende disponibile la tipica la-vagna di discussione (threaded discussion board)
• Documents mette a disposizione una lista di documenti, compresi i classici link e download
• Events rende disponibili item che possono scadere automaticamente
• FAQ permette di gestire una lista di Fre-quented Asked Question e le risposte cor-rispondenti
• FeedBack permette ai visitatori di inviare messaggi all’amministratore del portale
• IFrame, particolare per Microsoft Internet Explorer, rende disponibili contenuti ap-partenenti ad un altro sito web all’interno di un frame
• Image permette di mostrare immagini at-traverso il classico HTML IMG Tag
• Links presenta una lista di link ad altri siti web
• News Feeds (RSS) permette la distribuzione di syndicated news feed nel formato Rich Site Summary
• Search Input abilita le ricerche nel sito web e il corrispondente modulo Search Results permette di mostrare i risultati della ricerca
• Text/HTML permette di esporre porzioni di testo o HTML
• User Accounts abilita utenti profilati• User Defined Table rende disponibili sul
sito dati tabellari• XML/XLS permette di mostrare informa-
zioni a partire da contenuti XML median-te trasformazioni XLS
Esistono moltissimi altri moduli addizionali. La carrellata permette di valutare la comples-sità della costruzione del portale e la flessibi-
lità delle soluzioni disponibili per soddisfare esigenze molto diverse.
Struttura di un modulo DotNetNukee tutorial
Il modulo è un componente software (assem-bly dll). L’interfaccia WYSYWYG del modulo offre una discreta autonomia ai redattori di te-sti e di contenuti. Nei libri precedentemente citati lo sviluppo di un nuovo modulo è sem-pre correlato alla possibilità di usare Visual Studio 2003. Per sottolineare la relativa sem-plicità di sviluppo di nuovi moduli mi limi-to ad usare NotePad e Visual Basic.NET, lin-guaggio con il quale è stato sviluppato tutto il codice del portale. È necessario possedere una installazione funzionante di DotNetNuke. Il tutorial che segue descrive la costruzione dello sviluppo di un modulo che estrae da una tabella SQL Server l’ultimo messaggio da mo-strare nel componente. Iniziamo a costruire la tabella all’interno, per esempio, del classi-co database NorthWind. Di seguito è riporta-to il codice T-SQL per la creazione della ta-bella e della vista.
T-SQL
// esempio di codice
CREATE TABLE [dbo].[Messages] (
[no_message] [int] NULL ,
[message] [nvarchar] (100) COLLATE SQL_Latin1_General_
CP1_CI_AS NULL
) ON [PRIMARY]
CREATE VIEW dbo.vw_last_Message
AS
SELECT message
FROM dbo.Messages
WHERE (no_message = (SELECT MAX(no_message) AS max_no_
message FROM dbo.Messages))
Non mi soffermo, qui e in altre parti del-l’articolo, a valutare la necessità di imposta-re indici e vincoli sulle tabelle, come a gesti-re tutte le possibili eccezioni nel codice, per non appesantire la lettura. L’esecuzione de-gli script T-SQL con Query Analyzer produ-ce la tabella e la vista collega ad essa. Sem-
WEB
53N. 67 - Gennaio/Febbraio 2006 VBJ
pre con SQL Server Analyzer si possono in-serire alcuni messaggi e analizzare l’output della vista. Per costruire l’interfaccia uten-te è necessario preparare un file .ascx con-tenente la label che mostrerà l’ultimo mes-saggio della tabella. Il codice è riportato nel file ShowMessage.ascx visibile in Figura 1 e nel file ShowMessage.ascx.resx ed entrambi i file non presentano caratteristiche partico-larmente interessanti. Passiamo al codice che estrae dalla tabella Messages il messaggio da mostrare nel modulo. Il file LastMessage.vb è allegato all’articolo e di seguito illustriamo alcuni elementi interessanti.
Visual Basic .NET
// esempio di codice
‘ Code adapted from DAL Builder Pro from www.dotnetnuke.dk
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Drawing
Imports System.Web.UI.WebControls
Imports DotNetNuke
Imports DotNetNuke.Common
Imports DotNetNuke.Services.Search
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Actions
Imports DotNetNuke.Services.Localization
Imports DotNetNuke.Services.Exceptions
Namespace LastMessage
Come si può vedere dal codice qui sopra è necessario importare molti package dell’appli-cativo DotNetNuke. Come si vede dalla porzio-ne di codice qui sotto è necessario definire la classe ShowMessage che eredita dalla classe di DotNetNuke PortalModuleBase ed imple-menta l’interfaccia IActionableImports
Visual Basic .NET
// esempio di codice
Public Class ShowMessage
Inherits PortalModuleBase
Implements IActionableImports System.Data
(….)
WEB
Public ReadOnly Property ModuleActions() As ModuleAction
Collection Implements IActionable.ModuleActions
Get
Dim actions As New ModuleActionCollection
actions.Add(GetNextActionID(), Localization.GetString
(ModuleActionType.AddContent, LocalResourceFile),
ModuleActionType.AddContent, “”, “”, EditUrl(), False,
DotNetNuke.Security.SecurityAccessLevel.Edit, True, False)
Return actions
End Get
End Property
Il codice dell’estrazione del messaggio dalla tabella è ‘standard’ MSDN per quanto riguar-da l’utilizzo di ADO.NET.
Installazione del modulo DotNetNuke
e tutorial
Passiamo ora a documentare l’installazione. È necessario creare il nuovo folder LastMes-sage sotto la directory DesktopModules del-l’installazione di DotNetNuke e aggiungere il file .ascx e .vb descritti sopra. All’interno di LastMessage si è creato il folder App_Lo-calResources contenente il file .resx discusso prima. Possiamo ora loggarci a DotNetNuke come Amministratori Host (user Host, per in-tenderci) ed accedere alla maschera Module Definitions del menu Host. Editiamo le de-finizioni del nuovo modulo avendo cura di compilare il text Folder Name con il nome del folder costruito in precedenza. Digitiamo Update e l’applicazione ci presenta la masche-ra delle Definitions della quale compiliamo il text New Definitions con il nome del modulo. Digitiamo Add Definitions e l’applicativo ci presenta la maschera del Default Cache Time che impostiamo a zero (0) e digitiamo Upda-te Cache Properties. Finalmente giungiamo alla maschera del Control, che deve risultare vuota. Digitiamo Add Control e l’applicativo presenta la maschera di Edit Control Module nella quale troviamo già impostati e text Mo-dule e Definition ed impostiamo: il text Title con il nome del modulo; il list Source, sele-zionando la posizione del nostro file .ascx; il list Type selezionando View. A questo punto il modulo è caricato nell’anagrafica dei mo-
54 VBJ N. 67 - Gennaio/Febbraio 2006
duli di DotNetNuke e disponibile per essere
posizionato in una pagina di prova.
Chiediamo all’applicativo di creare una nuo-
va pagina; digitiamo nella maschera del Pa-
ging Management i text Page Name, Page
Title e Page Description secondo il proprio
gusto e impostiamo, dalla stessa maschera,
in lato al centro, dalla view Module il nostro
modulo LastMessage, avendo cura di aver se-
lezionato l’option Add New Module e il view
Pane a Content. Il processo di installazio-
ne è finito e DotNetNuke compila al volo il
componente .ascx recuperando il file risorse
.resx e il codice .vb, il bin creato è eseguito
da ASP.NET che esegue il codice del Page_
Load, permettendo l’accesso a SQL Server
ed estraendo l’ultimo messaggio che è pas-
sato alla label del modulo. In caso di errori
DotNetNuke imposta il content della pagina
di prova con la trace dell’errore, che ritengo
però quasi illeggibile. In ogni caso è possi-
bile zippare il folder per decomprimerlo nel-
l’ambiente di produzione ed effettuare l’in-
stallazione come sopra.
ConclusioniRitengo DotNetNuke un ottimo prodot-
to soprattutto perché è possibile imparare/
documentare dal codice sorgente rilasciato
con l’applicativo e per quanto riguarda la se-
curity audit oltre che, ovviamemte, per quanto
riguarda il rapporto costo/rendimento dell’in-
vestimento. Inoltre, come descritto preceden-
temente, poiché il fornitore di software princi-
pale di AWD Sim utilizza lo stesso applicativo
per il proprio portale non si prevedono proble-
mi connessi a richieste improvvise di sviluppo
o di produzione di workaround per la gestione
di incompatibilità o bachi del softwre di base.
Bibliografia[1] Shaun Walker ed altri – “Professional Dot-
NetNuke ASP.NET Portals”, Wrox, 2005
[2] Daniel N. Egan – “Building Websites with
VB.NET and DotNetNuke 3.0”, Packt, 2005
Riferimenti[3] www.awd-italia.com
[4] www.debugswt.it
[5] www.dotnetnuke.com
WEB
55N. 67 - Gennaio/Febbraio 2006 VBJ
In questa quarta parte del nostro progetto ana-
lizzeremo un importante metodo della classe
RemoteProcesses, la cui trattazione è già stata
introdotta nella precedente puntata. Abbiamo vi-
sto che questa classe fondamentale ci consente di
implementare metodi e proprietà per controllare e
monitorare processi in esecuzione sulla macchina
remota. Riteniamo possa essere assai utile defini-
re anche un semplice metodo che permetta l’uti-
lizzo di un’eventuale stampante collegata alla po-
stazione di cui abbiamo il controllo mediante il no-
stro programma. Come sempre il .NET Framework
ci viene incontro mediante proprietà appartenenti
alla propria Class Library. Diamo uno sguardo alla
seguente porzione di codice.
Public Function printPrintableFile(ByVal ppff As String) As String
Dim PRNTreport As String = Nothing
Dim myp As New Process()
Try
myp.StartInfo.FileName = ppff
myp.StartInfo.CreateNoWindow = True
myp.StartInfo.Verb = “print”
myp.Start()
Catch pp As Exception
PRNTreport = “Errore: “ & pp.Message
End Try
If PRNTreport = Nothing Then
PRNTreport = “OK>>>”
End If
Return PRNTreport
End Function
Definiamo un semplicissi-
mo metodo pubblico chiama-
to printPrintableFile() che
restituisce un semplice valore
di flag di tipo String se l’opera-
zione di stampa è stata eseguita
correttamente. Prima di tutto
definiamo l’oggetto myp di tipo
Process, dopo di ché non ci re-
sta che assegnare alla proprie-
tà FileName il valore di ppff
passato come argomento del-
la funzione medesima. Tale va-
lore dovrà contenere il percor-
so assoluto all’interno del file-
system della macchina remota
che punta al file che si desidera
stampare. Impostiamo quindi a
True la proprietà CreateNoWin-
dow, al fine di non determina-
re la comparsa di alcuna GUI
sullo schermo del PC Target.
Molto importante è invece la
proprietà Verb che ci consente
di impostare il canale da utiliz-
zare nell’apertura dell’applica-
zione o del documento specifi-
cato dalla proprietà FileName.
Se il percorso specificato ricon-
duce ad un file di cui è possi-
Controllo Remotoin Visual Basic .NET
di Stefano Corti
APPLICAZIONI
Quarta puntata
Stefano Corti si occupa di programmazione PHP e JSP lato server, della piattaforma .NET e di integrazione di sistemi legacy con le nuove realtà del web, soprattutto in ambito gestionale, bancario e finanziario. È attualmente alle dipendenze di un primario gruppo bancario italiano. Può essere contattato via email: [email protected].
56 VBJ N. 67 - Gennaio/Febbraio 2006
End Sub
Public Sub PopError()
fPrompt &= oPromptTitle
MessageBox.Show(oMessage, fPrompt,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Sub
Come possiamo notare si tratta di tre proce-
dure Sub (quindi non restituiscono alcun valo-
re) che si occupano semplicemente di visualiz-
zare altrettante tipologie di Pop-Up contenen-
ti i relativi messaggi testuali. Questi metodi
provvedono alla visualizzazione del messag-
gio leggendo la stringa testuale dalla variabile
privata oMessage, di tipo String. La scritta che
comparirà nella barra del titolo della finestra
viene invece ricavata dalla variabile oPrompt-
Title. La variabile oMessage viene imposta-
ta per mezzo della proprietà Poptext, mentre
la proprietà di sola lettura Replytext consen-
te al ciclo principale del server di leggere la
stringa testuale memorizzata nella variabile
bile la stampa, il proces-
so ha inizio e la stampan-
te predefinita verrà attiva-
ta, altrimenti sarà solleva-
ta un’eccezione che il no-
stro codice intercette-
rà mediante un generico
blocco Try Catch.
Ora che abbiamo esami-
nato gli aspetti più im-
portanti della nostra clas-
se RemoteProcesses, pas-
siamo ad illustrare una
semplice classe del no-
stro progetto che ci con-
sente di instaurare com-
plete sessioni di chat te-
stuali tra Controller e
Target. Riteniamo infatti
importante poter effettua-
re uno scambio di messag-
gi tra gli utenti delle due
macchine, al fine di ren-
dere le operazioni di assi-
stenza maggiormente in-
terattive e senza spreco di ulteriori risorse
tecniche, come ad esempio l’utilizzo del tele-
fono cellulare laddove la connettività di uno
dei due utenti sia del tipo dial-up. In detta-
glio, il Controller sarà in grado di inviare su
Target ogni genere di messaggio, che com-
parirà sotto forma di una finestra di pop-up,
anche qualora la GUI Target sia stata ridotta
ad icona nella Systray. Tali pop-up potranno
essere di tipo meramente informativo, di er-
rore oppure con un campo di testo vuoto per
permettere l’inserimento di una risposta di ri-
torno. Vediamo i tre metodi principali.
Public Sub PopUp()
fPrompt &= oPromptTitle
MessageBox.Show(oMessage, fPrompt,
MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
Public Sub PopReply()
fPrompt &= oPromptTitle
oReply = InputBox(oMessage, fPrompt)
APPLICAZIONI
Li sta to 1 Struttura Select Case per la gestione del sistema di chat-line
Case Is = “TEXT” inMessages.Clear() inMessages.Text &= job(1) If CheckBox1.Checked Then Dim objPop As MessagePopUp = New MessagePopUp() objPop.Comingfrom = ControllerID.Text objPop.Poptext = job(1) objPop.PopUp() writer.Write(vbCrLf & “Messaggio inviato”) End If
Case Is = “TEXTWITHREPLY” inMessages.Clear() inMessages.Text &= job(1) Dim objPop As MessagePopUp = New MessagePopUp() objPop.Comingfrom = ControllerID.Text objPop.Poptext = job(1) objPop.PopReply() writer.Write(“REPTEXT>” & objPop.Replytext)
Case Is = “TEXTERRORMESSAGE” inMessages.Clear() inMessages.Text &= job(1) Dim objPop As MessagePopUp = New MessagePopUp() objPop.Comingfrom = ControllerID.Text objPop.Poptext = job(1) objPop.PopError() writer.Write(vbCrLf & “Messaggio inviato”)
57N. 67 - Gennaio/Febbraio 2006 VBJ
privata oReply, quale valore stringa restitui-
to dalla funzione InputBox() all’interno del-
la procedura Sub PopReply().
Public Property Poptext() As String
Get
Return oMessage
End Get
Set(ByVal Value As String)
oMessage = Value
End Set
End Property
Public ReadOnly Property Replytext() As String
Get
Return oReply
End Get
End Property
Occupiamoci ora di gestire queste funziona-
lità all’interno del ciclo principale del server.
Esaminiamo la porzione di codice visibile nel
Listato 1. Abbiamo previsto tre possibili co-
mandi del nostro protocollo applicativo che
consentono alle due macchine di dialogare. In
tutti e tre i casi viene creato l’oggetto objPop,
istanziando la classe MessagePopUp. In que-
sto contesto, il valore stringa di job(1) contiene
APPLICAZIONI
Li sta to 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 TryEnd Sub
58 VBJ N. 67 - Gennaio/Febbraio 2006
il messaggio testuale inviato dall’applicativo
Controller, che viene immediatamente pas-
sato alla proprietà Poptext. Successivamen-
te viene invocato il metodo che determina la
comparsa della relativa finestra. Osserviamo
anche che, nel caso in cui il Controller tra-
smetta un messaggio con risposta di ritorno
obbligatoria, tale risposta possa essere indi-
viduata leggendo la proprietà di sola lettura
Replytext, come abbiamo visto precedente-
mente. Il valore stringa ritornato da questa
proprietà viene quindi immesso nel socket
di comunicazione preceduto dal comando ap-
plicativo <REPTEXT>. Come abbiamo det-
to, si tratta di una classe estremamente sem-
plice, la cui trattazione non merita ulteriori
approfondimenti.
Incominciamo ora a
parlare di una funziona-
lità molto importante del
nostro progetto, ovvero
la possibilità di sposta-
re qualunque tipo di file
tra i due end-point. An-
che in questo caso do-
vremo provvedere a vei-
colare i flussi binari di
byte, utilizzando come
sempre TCP/IP e preve-
dendo un’architettura di
tipo client-server. Imple-
menteremo quindi appo-
site procedure Sub che
implementeranno tutto
il codice necessario per
porre in ascolto su de-
terminate porte due ele-
mentari server, sia su
Controller, sia su Target,
dal momento che deside-
riamo progettare un si-
stema che ci consenta di
trasferire file in entram-
be le direzioni. In questa
puntata ci concentrere-
mo particolarmente sul-
l’implementazione del
server su Target, dal mo-
mento che l’analoga operazione risulterà ov-
viamente molto simile ed intuitiva su Con-
troller. Passiamo subito all’esame del Listato
2. Come possiamo facilmente notare, questo
nuovo server è in ascolto sulla porta 13010.
Abbiamo scelto infatti un numero di porta
differente, in quanto vogliamo mantenere
completamente separato il flusso di coman-
di del nostro protocollo applicativo, dai flus-
si binari che rappresentano i byte trasmessi
del nostro file. Per il resto la progettazione
del codice non differisce molto da ciò che ab-
biamo già implementato nelle prime puntate.
Ricordiamo che anche l’esecuzione di questo
server viene inserita in un apposito Thread
il cui ciclo di vita viene inizializzato nel co-
struttore della form dopo la chiamata a Ini-
APPLICAZIONI
Li sta to 3 Procedura Sub di realizzazione del client per il trasferimento file
Public Sub beginToUpLoad(ByVal fileUp As String) Dim t_client As TcpClient Dim ip As String = inIP.Text Dim port As Int32 = 13010 t_client = New TcpClient() t_client.Connect(ip, port) t_output = t_client.GetStream() t_writer = New BinaryWriter(t_output) t_reader = New BinaryReader(t_output) ProgressBar1.Visible = True ProgressBar1.Minimum = 1 ProgressBar1.Maximum = CInt(FileLen(fileUp) / 120) ProgressBar1.Value = 1 Dim buffer(1024) As Byte Dim count As Integer Dim infile As FileStream = _ File.Open(fileUp, FileMode.Open, FileAccess.Read) Try count = infile.Read(buffer, 0, 1024) While count > 0 t_writer.Write(buffer, 0, count) count = infile.Read(buffer, 0, 1024) ProgressBar1.PerformStep() End While Catch ef As Exception inMessages.Text &= “Errore durante l’UpLoad del File: “ & _ vbCrLf & ef.Message Finally infile.Close() t_writer.Close() t_reader.Close() t_output.Close() t_client.Close() inMessages.Text &= vbCrLf & “FILE TRASFERITO A PC REMOTO.” End TryEnd Sub
59N. 67 - Gennaio/Febbraio 2006 VBJ
tializeComponent() in modo del tutto analo-
go al server principale.
Public Sub New()
MyBase.New()
InitializeComponent()
‘Aggiungere le eventuali istruzioni di inizializzazione
dopo la chiamata a InitializeComponent()
rThread = New Thread(AddressOf runServerTarget)
fThread = New Thread(AddressOf runReceiveFile)
rThread.Start()
fThread.Start()
End Sub
Quando la form viene visualizzata sullo
schermo Target, questi due Thread sono già
attivi e in ascolto sulle rispettive porte. Ab-
biamo inoltre previsto che i byte che compon-
gono il file da copiare non vengano trasmessi
uno alla volta, ma raggruppati in blocchi da
1024 byte ciascuno. Questo consente una più
razionale segmentazione del flusso binario e
una più corretta bufferizzazione del procedi-
mento stesso. Creiamo quindi l’array buffer,
di tipo byte, impostando la dimensione a 1024.
Il passo successivo consiste del creare un og-
getto di tipo FileStream che riceverà il valo-
re restituito dal metodo statico OpenWrite()
della classe File. Questo metodo, che restitui-
sce ovviamente un valore di tipo FileStream,
apre un file esistente inizializzandolo per la
relativa scrittura e accetta come parametro un
valore stringa che può contenere il percorso
completo del file da scrivere su disco, inclu-
so il nome stesso del file e l’eventuale esten-
sione. La classe FileStream viene utilizzata
per leggere, scrivere, aprire e chiudere file
su un file-system. Nel successivo blocco Try
passiamo alla variabile Integer count il valore
restituito dal metodo sovraccarico Read() del-
la classe BinaryReader() di cui abbiamo già
parlato in precedenza. In questo contesto però
utilizziamo tre parametri previsti da un caso
di overload, di seguito esemplificato:
Overloads Public Overridable Function Read( _
ByVal buffer() As Byte, _
ByVal indexAs Integer, _
ByVal countAs Integer_
) As Integer
Il metodo restituisce quindi un valore di
tipo Integer e accetta tre parametri: un array
di tipo Byte che rappresenta il buffer in cui
leggere i dati; un valore Integer, quale punto
di partenza nel buffer da cui avviare la lettu-
ra nel buffer stesso ed infine ancora un valo-
re Integer contenente il numero di caratteri
da leggere. Il ciclo While più interno control-
la costantemente che il valore di count sia
maggiore di zero, poiché in caso contrario è
stata certamente raggiunta la fine dell’ultimo
blocco di byte del nostro file. All’interno del
ciclo andremo quindi a porre il codice che ci
serve per scrivere il file che stiamo estraendo
dal socket di flusso e assegnare un nuovo va-
lore alla variabile count, affinché il ciclo pos-
sa ripetersi con la lettura del nuovo segmento
bufferizzato, oppure, in caso contrario, passa-
re l’esecuzione al blocco Finally che si occupa
di rilasciare tutte le risorse attivate.
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
Molto importante è ovviamente il meto-
do Write() per l’oggetto outfile di tipo File-
Stream. Questo metodo di overload scrive ef-
APPLICAZIONI
60 VBJ N. 67 - Gennaio/Febbraio 2006
APPLICAZIONI
fettivamente i dati su disco, utilizzando il pri-
mo parametro come matrice in cui vengono
scritti i byte. Il valore 0 è in pratica la posi-
zione di offset dei byte da cui iniziare la scrit-
tura e count rappresenta il numero massimo
dei byte da scrivere, ovvero il valore Integer
letto precedentemente dallo stream binario
con il metodo Read(), già discusso. Nel Lista-
to 3 è invece visibile il codice di implemen-
tazione del client relativo al processo appe-
na descritto. A parte la gestione di una Pro-
gressBar che mostra lo stato di avanzamento
del trasferimento in corso, il codice non pre-
senta particolari aspetti degni di attenzione.
In pratica la funzione accetta un parametro
stringa che contiene il percorso assoluto al-
l’interno del file system del Controller dove
si trova il file che desideriamo inviare alla
macchina Target. Successivamente creiamo
anche in questo caso un array di tipo Byte,
impostando la dimensione massima a 1024
e apriamo il file con il metodo Open() della
classe File, che ancora una volta restituisce
un valore di tipo FileStream. Quindi in un ci-
clo While del tutto analogo a quello previsto
per il server, scriviamo all’interno del socket
di flusso precedentemente stabilito i blocchi
di byte che, ad opera dei servizi esposti dai
livelli più bassi del modello ISO/OSI, di cui
non ci occupiamo, dovranno raggiungere at-
traverso la rete, qualunque essa sia, la mac-
china Target per essere nuovamente estratti
e scritti su disco per ottenere una copia esat-
ta del file di partenza. Nella prossima pun-
tata concluderemo la trattazione delle funzio-
ni e dei metodi per il trasferimento dei file e
vedremo come implementare un elementare
sistema di autenticazione.
.NET TO OLS
61N. 67 - Gennaio/Febbraio 2006 VBJ
.NETMonIl flow tracing senza sforzo
di Fabio Perrone
Uno strumento gratuito per gestire il flow
tracing di applicazioni .NET
Alzi la mano chi, durante lo sviluppo di un pro-
getto particolarmente complesso, non si è mai
posto il seguente problema: “Ho modificato il
metodo pubblico X della classe Y, chissà quali
altri metodi del mio progetto lo utilizzano?”.
A causa di problematiche legate ai tempi di con-
segna dei progetti software, i test di regressio-
ne sovente vengono trascurati, provocando poi
gravi danni quando un applicativo viene messo
in produzione. Una semplice ricerca in tutta la
soluzione non è quasi mai sufficiente, quindi è
necessario utilizzare tecniche di flow tracing. Il
flow tracing ha una doppia funzionalità: scopri-
re tutte le righe di un’applicazione che vengono
eseguite e il code coverage, vale a dire analizzare
se effettivamente tutte le righe di un determi-
nato metodo vengono eseguite. Per eseguire il
flow tracing, il .NET Framework mette a nostra
disposizione le Profiling API, uno strumento po-
tente ma alquanto complesso che richiede una
buona conoscenza di C++ e di tecniche avanza-
te di debugging (per chi fosse interessato con-
siglio l’ottimo “Debugging Applications for Mi-
crosoft .NET and Microsoft Windows” di John
Robbins). Per chi non avesse le conoscenze ne-
cessarie o desiderasse uno strumento già “pron-
to all’uso”, .NETMon della Foundstone costitui-
sce un’ottima soluzione, poiché permette di ef-
fettuare il flow tracing sia a livello di classe che
a livello di metodo.
Il primo aspetto importante da evidenziare è
che .NETMon lavora sugli eseguibili, il che si-
gnifica da un lato un inevitabile rallentamento
delle prestazioni durante l’esecuzione della no-
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
62
.NET TOOLS
VBJ N. 67 - Gennaio/Febbraio 2006
e quando deve essere tracciato, .NETMon per-mette di impostare una serie di valori che aiu-tano a scegliere se mostrare o nascondere de-terminati assembly, namespace o metodi; que-sta funzionalità è assolutamente indispensabile perché, ovviamente, il programma tiene traccia di tutti i metodi che vengono eseguiti, compre-si i metodi del CLR.
È importante ricordare che all’avvio di una nostra applicazione il CLR esegue una grande quantità di metodi d’inizializzazione, che pos-sono anche non essere tracciati grazie appun-to alla proprietà “Initial Delay”. Tale proprietà permette di impostare il ritardo iniziale di tra-cing dei metodi, cioè è un contatore che indica a .NETMon quando iniziare ad effettuare il tra-cing dell’applicazione. Per impostare un valore corretto di questa proprietà sono necessarie un po’ di prove, ma il grande vantaggio è che, una volta trovato un valore ottimale, è possibile ini-ziare a seguire la nostra applicazione senza at-tendere troppo tempo mentre il CLR esegue tut-te le sue inizializzazioni.
Per applicazioni che utilizzano pesantemente il multithreading, è possibile ordinare i meto-di per thread, in modo da creare un tracing se-parato per ogni thread. Com’è lecito aspettarsi, è possibile interrompere l’analisi del flusso in qualsiasi momento premendo sull’apposito but-ton “STOP”.
Il vero valore di questi strumenti senza dubbio non è soltanto la possibilità di eseguire il flow tracing, quanto fornire al tester i dati ottenuti in modo da poterli manipolare in qualche modo, mettendo a disposizione dei valori su cui è pos-sibile eseguire ulteriori studi, quali per esem-pio capire da chi è stato chiamato un determi-nato metodo.
I dati ci vengono quindi forniti o in una Text-box nell’ordine in cui i metodi vengono esegui-ti o in un TreeView che organizza le funzioni in base alle chiamate annidate che vengono effet-tuate, escludendo eventualmente il tipo di ritor-no d’ogni metodo e il nome del namespace: que-sto risulta uno strumento fondamentale grazie al fatto che è possibile esportare i dati del Tree-View in un file XML e quindi preparare un pic-colo programma che legga questo file XML e
popoli eventualmente qualche tabella di data-base per poter effettuare ricerche più avanzate, come conoscere da quali assembly viene richia-mato un metodo, quante volte un metodo viene richiamato e così via.
Per chi è particolarmente curioso, potrebbe es-sere interessante dare un’occhiata a quanti e qua-li metodi del CLR vengono richiamati durante l’esecuzione di una nostra applicazione mana-ged, permettendo di capire un po’ più a fondo quel bellissimo ma assai ampio mondo che è il .NET Framework.
A corredo dell’applicazione vengono forniti alcu-ni esempi per familiarizzare con le impostazioni dell’applicazione, nonché una buona documen-tazione in formato pdf.
Nonostante un leggero sforzo iniziale e l’inter-faccia grafica non proprio accattivante, il tool della Foundstone ripaga ampiamente fornendo uno strumento gratuito di fondamentale impor-tanza per qualsiasi tester che desideri controlla-re un’applicazione in modo più approfondito uti-lizzando il flow tracing o sviluppatore che voglia conoscere più a fondo le parti intrinseche di qual-siasi applicazione. È necessario ricordare che pur-troppo la versione attuale prevede la piena com-patibilità solo con il .NET Framework 1.1.
Prodotto.NETMon
Url di riferimento http://www.foundstone.com/index.htm?subnav=resources/navigation.htm&subcontent=/resources/proddesc/dotnetmon.htm
Sta to ReleaseStabile
Semplicità d’uso �����Per poter ottimizzare le impostazioni dell’applicazione è necessa-rio conoscerla un po’ a fondo
Utilità �����Strumento molto importante per chi desidera utilizzare il flow tracing di applicazioni
Qualità prodotto ����� Affidabile, nei test sul prodotto eseguiti non ha mai mostrato par-ticolari problemi
Qualità documentazione �����Il file di documentazione allegato permette di comprendere tutte le funzionalità del programma (in lingua inglese).
63
.NET TOOLS
N. 67 - Gennaio/Febbraio 2006 VBJ
NeatUploaddi Raffaele Di Natale
Un tool open source per eseguire l’upload di
un file e di monitorarne l’avanzamento
Il numero degli utilizzatori di internet ad alta ve-locità cresce giorno dopo giorno e sul web si mol-tiplicano i servizi orientati alla manipolazione di file di dimensioni sempre crescenti. Sebbene la posta elettronica risulti ancora uno dei metodi più immediati per inviare simili file (si veda G-mail, Alice…), qualche piccolo inconveniente potrebbe presentarsi se si volesse mettere a disposizione dei visitatori di un dato sito web, un servizio di reposi-
tory di file con annessa funzionalità di upload. In-fatti, quando si pensa di inviare file di centinaia di MByte, se non di GByte, il primo problema da af-frontare è quello di monitorare tale operazione di trasferimento. Quante volte, ad esempio, provando ad effettuare l’attach di un file in un messaggio di posta elettronica web-based, abbiamo l’impressio-ne che il trasferimento stia fallendo perché col pas-sare del tempo non riceviamo alcun messaggio di ‘conforto’ dall’applicazione? NeatUpload, mediante un opportuno control inserito in una pagina web – ovvero in una finestra popup – restituisce un fee-dback continuo all’utente e ne verifica tranquilla-mente l’attività di streaming anche e soprattutto relativamente a file di elevate dimensioni, per i quali il tempo di trasferimento potrebbe risultare particolarmente lungo.
L’applicazioneNeatUpload è una Web Application sviluppata
in C# che fa uso dei seguenti moduli:
� Brettle.Web.NeatUpload.dll, il modulo princi-pale dell’applicazione;
� Log4Net.dll, per il log delle informazioni di de-bug [1].
Le dll sono distribuite sia in modalità debug sia release. Alla base di NeatUpload vi sono quattro elementi che possono essere utilizzati ed organiz-zati a nostro piacimento, oltre ad essere facilmen-te modificabili e quindi personalizzabili:
� InputFile, un control custom con caratteri-stiche avanzate rispetto all’HtmlInputFile in quanto ci permette di accedere al file tempo-raneo in fase di upload, così come al nome ori-ginario del file e al suo tipo;
� ProgressBar, un control custom che ha il com-pito di mostrare lo stato di avanzamento del-l’upload oltre a comandarne l’interruzione. Può essere inserito all’interno di una pagina oppu-re richiamato in una finestra popup. Inoltre, nel caso in cui JavaScript non risulti abilitato oppure il browser non supporti gli IFrame e la ProgressBar è inserita inline nel documento, essa apparirà come un semplice link tramite il quale potrà essere visualizzata la finestra po-pup contenente la barra di avanzamento;
� Progress.aspx: la presenza di una ProgressBar richiama la pagina progress.aspx che ne effet-tuerà l’aggiornamento, in base alle indicazioni provenienti dall’UpLoadHttpModule;
� UpLoadHttpModule, è il modulo principale dell’applicazione ed ha il compito di intercet-tare le richieste, gestire l’upload ed effettuare i controlli sulle dimensioni dei file.
Prerequisiti e InstallazioneNeatUpload è un’applicazione ASP.Net multi-
piattaforma in quanto supporta sia il framework .NET che Mono. È stata testata utilizzando Win-dows XP SP2 con Internet Information Services [IIS] e .NET Framework 2.0.
Dopo aver scaricato da [2] ed estratti i file rela-tivi all’applicazione, gli scenari possibili sono es-senzialmente due: il primo è rappresentato dal-la possibilità di utilizzare direttamente l’appli-cazione mediante una pagina dimostrativa ap-positamente sviluppata; la seconda è quella di integrare NeatUpload all’interno di un’applica-zione già esistente.
Nel primo caso è sufficiente configurare Nea-tUpload come WebApplication nell’ambiente IIS. Successivamente, per testarne le funzionalità ba-sterà accedere alla pagina dimostrativa denomi-nata appunto demo.aspx.
Tramite questa pagina, dopo aver selezionato un file e cliccato sul pulsante Submit, il file se-lezionato verrà copiato all’interno della cartel-la temporanea di sistema (normalmente in C:
64
.NET TOOLS
VBJ N. 67 - Gennaio/Febbraio 2006
[assembly: log4net.Config.XmlConfigurator(ConfigFile=”log4net.
config”, Watch=true)]
Se eseguiamo la nostra applicazione in moda-
lità debug o release dovremo copiare Breattle.
Web.NeatUpload.dll, in versione debug o relea-
se, rispettivamente nella cartella bin di debug
o release dell’applicazione finale. Copiamo infi-
ne l’intero contenuto della cartella NeatUpload
(NeatUpload-1.0/NeatUpload/) all’interno di una
sottodirectory omonima, opportunamente creata
nella cartella relativa all’applicazione principale.
Infine, per poter utilizzare il modulo UpLoadHt-
tpModule si dovrà aggiungere al Web.Config, sot-
to la sezione configuration/system.web/httpmodu-
les, la seguente riga:
<add name=”UploadHttpModule” type=”Brettle.Web.NeatUpload.Uploa
dHttpModule, Brettle.Web.NeatUpload” />
Prima di passare a sviluppare una pagina ad hoc
per la nostra applicazione è conveniente testare
l’installazione copiando ed eseguendo la pagina
dimostrativa demo.aspx.
PersonalizzazioneSe non si hanno particolari esigenze, le uni-
che modifiche necessarie per utilizzare NeatU-
\Documents and Settings\NOMEPC\ASPNET\
Impostazioni locali\Temp).
Completato l’upload il file sarà immediatamente
eliminato: ovviamente ciò non accadrà in un’ap-
plicazione reale. In Figura1 è illustrata la pagina
demo.aspx ed evidenziata la ProgressBar conte-
nuta all’interno della finestra popup. Una demo
online dell’applicazione è inoltre disponibile al-
l’indirizzo [3].
Integrare NeatUpload ad un’applicazione esistente
La pagina dimostrativa serve solo a testare le
funzionalità di base dell’applicazione, ma è chia-
ro che NeatUpload è pensata per essere integra-
ta ad un’applicazione già esistente o in fase di
sviluppo. Per cominciare osserviamo che anche
NeatUpload, così come altri tool .NET descritti in
questa rubrica, utilizza Log4Net [1] che può es-
sere configurato per ottenere interessanti infor-
mazioni di debug, specialmente nella prima fase
di integrazione e sviluppo dell’applicazione. Una
copia di Log4Net.dll è presente nella cartella …/
bin di NeatUpload in formato release e debug, op-
pure può essere ottenuta seguendo le indicazioni
date in [1]. Dopo aver copiato Log4Net.dll all’in-
terno della cartella …/bin della nostra applicazio-
ne non resta che modificare il file Global.asax.cs
inserendo la seguente riga di codice C#:
65
.NET TOOLS
N. 67 - Gennaio/Febbraio 2006 VBJ
pload in maniera ottimale riguardano l’imposta-
zione della cartella temporanea dei file caricati,
l’impostazione della dimensione massima dei file
in upload, lo spostamento del file caricato ver-
so una directory definitiva (il repository vero e
proprio) ed il testo da visualizzare come link nel
caso in cui il browser impedisca la corretta vi-
sualizzazione della ProgressBar all’interno del-
la pagina. Il codice della pagina demo.aspx si
presta ad essere modificato facilmente e in ogni
caso è sempre possibile seguire i suggerimenti
presenti in manual.html della cartella docs del-
l’applicazione. Le prime due modifiche avvengo-
no inserendo due righe all’interno della sezione
configuration/appSettings di Web.Config. Per im-
postare una directory temporanea che ospiterà i
file caricati si inserirà
<add key=”NeatUpload.DefaultTempDirectory” value=”TempDir” />
Per limitare la dimensione massima di un file
basta inserire
<add key=”NeatUpload.MaxRequestLength” value=”DimKbytes” />
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-
l’applicazione chiamante, altrimenti il file sarà
semplicemente ignorato senza alcun avvertimen-
to. Facendo riferimento al file demo.aspx, se vo-
gliamo spostare definitivamente il file caricato
verso una data cartella, ad esempio la cartella
principale dell’applicazione, è necessario modi-
ficare il blocco di codice relativo alla funzione
membro Page_PreRender come di seguito:
if (this.IsPostBack)
{
if (inputFile.TmpFile != null)
{
inputFile.TmpFile.MoveTo(Path.Combine(Request.
PhysicalApplicationPath, Path.GetFileName(inputFile.FileName)));
}
Infine, facendo considerando il codice relativo
alla ProgressBar
<Upload:ProgressBar id=”progressBar” runat=”server” >
<asp:Label id=”label” runat=”server” Text=”Your Text Here”/>
</Upload:ProgressBar>
sarà sufficiente modificare il campo Text inse-
rendo il testo desiderato che costituirà il link in
caso di mancata visualizzazione della Progres-
sBar.
ConclusioniNeatUpload è una WebApplication efficace e
senza fronzoli. Bene si adatta, grazie alla modu-
larità che la contraddistingue, alle svariate esi-
genze delle applicazioni web che puntano a ge-
stire streaming significativi di file. Sicuramente
l’interfaccia è migliorabile, ma se si esclude que-
sto aspetto bisogna riconoscere che le aspettati-
ve non restano deluse.
Riferimenti[1] D. Mauri - “. Net Tools”, Visual Basic Journal,
n.52, Lug/Ago 2003
[2] http://www.brettle.com/neatupload
[3] http://www.brettle.com/Demo.aspx
ProdottoNeatUplaod
Url di riferimento http://sourceforge.net/projects/mail2rss
Sta to Releasev. 1.0.16
Semplicità d’uso �����Decisamente ottima la versione dimostrativa dell’applicazione, buona la sua integrazione con applicazioni esistenti.
Utilità �����Chi ha in mente di sviluppare una funzionalità di streaming per la propria applicazione web trova in NeatUpload senz’altro la solu-zione a tutti i suoi problemi presenti e futuri grazie alla sua ver-satilità
Qualità prodotto ����� È un prodotto di ben fatto e nei test effettuati non sono stati ri-scontrati problemi.
Qualità documentazione �����La documentazione è in inglese ma è molto chiara e dettagliata, ma soprattutto disporre di una pagina dimostrativa efficace rap-presenta la documentazione più eloquente.