v2006 01 vbj67

68

Upload: turing-club

Post on 07-Jun-2015

446 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: v2006 01 vbj67
Page 2: v2006 01 vbj67
Page 3: v2006 01 vbj67
Page 4: v2006 01 vbj67

VBJ 67

IN OFFERTA VBJ 67

Scrivi a

[email protected]

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 €

Page 5: v2006 01 vbj67

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

[email protected]

AmministrazioneSara Mattei

([email protected])

GraficaManola Greco ([email protected])

Technical BookLisa Vanni ([email protected])

Segreteria Enrica Nassi

([email protected])

StampaTIPOLITOGRAFIA PETRUZZI

Citta’ di Castello (PG)

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

www.infomedia.it

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

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

Sito Web www.infomedia.it

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

anche parziale di te sti e immagini.

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

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

Registrazione presso il Tribunale di Pisa n. 20/1999

Page 6: v2006 01 vbj67
Page 7: v2006 01 vbj67
Page 8: v2006 01 vbj67
Page 9: v2006 01 vbj67

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

Page 10: v2006 01 vbj67

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].

Page 11: v2006 01 vbj67

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

Page 12: v2006 01 vbj67

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

Page 13: v2006 01 vbj67

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

Page 14: v2006 01 vbj67

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

Page 15: v2006 01 vbj67

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

Page 16: v2006 01 vbj67

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

Page 17: v2006 01 vbj67

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

Page 18: v2006 01 vbj67

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

Page 19: v2006 01 vbj67

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’

Page 20: v2006 01 vbj67

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

Page 21: v2006 01 vbj67

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

Page 22: v2006 01 vbj67

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

Page 23: v2006 01 vbj67

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

Page 24: v2006 01 vbj67

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

Page 25: v2006 01 vbj67

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

Page 26: v2006 01 vbj67

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

Page 27: v2006 01 vbj67

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

Page 28: v2006 01 vbj67

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

Page 29: v2006 01 vbj67

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à

Page 30: v2006 01 vbj67

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

Page 31: v2006 01 vbj67

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

Page 32: v2006 01 vbj67

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

Page 33: v2006 01 vbj67
Page 34: v2006 01 vbj67

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

Page 35: v2006 01 vbj67

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

Page 36: v2006 01 vbj67

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

Page 37: v2006 01 vbj67

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

Page 38: v2006 01 vbj67

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

Page 39: v2006 01 vbj67
Page 40: v2006 01 vbj67

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

Page 41: v2006 01 vbj67

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

Page 42: v2006 01 vbj67

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

Page 43: v2006 01 vbj67

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

Page 44: v2006 01 vbj67

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

[email protected].

SOFTWARE ENGINEERING

Page 45: v2006 01 vbj67

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

Page 46: v2006 01 vbj67

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

Page 47: v2006 01 vbj67

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

Page 48: v2006 01 vbj67

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

Page 49: v2006 01 vbj67
Page 50: v2006 01 vbj67

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

Page 51: v2006 01 vbj67

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

Page 52: v2006 01 vbj67

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

Page 53: v2006 01 vbj67

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-

Page 54: v2006 01 vbj67

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

Page 55: v2006 01 vbj67

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].

Page 56: v2006 01 vbj67

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”)

Page 57: v2006 01 vbj67

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

Page 58: v2006 01 vbj67

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

Page 59: v2006 01 vbj67

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

Page 60: v2006 01 vbj67

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.

Page 61: v2006 01 vbj67

.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

Page 62: v2006 01 vbj67

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).

Page 63: v2006 01 vbj67

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:

Page 64: v2006 01 vbj67

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#:

Page 65: v2006 01 vbj67

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.

Page 66: v2006 01 vbj67
Page 67: v2006 01 vbj67
Page 68: v2006 01 vbj67