|
Le classi
Sorgenti e consumatori di dati |
In queste pg vedremo come creare ed usare dei moduli di classe
predisposti al collegamento con i dati, gettando le basi per la comprensione
dei concetti inerenti larchitettura delle applicazioni Client/Server
Grazie alla tecnologie ADO è
possibile implementare delle classi, dette orientate ai dati (Data-Aware), che possono
fungere da sorgente o da consumatore di dati. La classe Sorgente, è una classe
collegata ad un database che fornisce i dati ad un consumatore. Per capirci, questo è
quello che fa il controllo data di Visual Basic. Le classi sorgente sono flessibili ed
efficienti potendo interagire con qualsiasi tipo di dato, relazionale e non (lavorano con
ODBC, OLEDB e ADO). Questo classe a volte è anche chiamata: Server, Provider o Source. La
classe Consumatore, è una classe che si collega ad oggetti definiti come sorgente e ne
riceve i dati. Per capirci, questo è quello che avviene quando un textbox è collegato ad
un recordset. Questa classe è anche conosciuta come Consumer o Cliente, ecc. Di solito
per connettere efficientemente la classe Sorgente e quella Consumatore viene usato un
oggetto BindingCollection (introdotto nei precedenti articoli), come tra poco
constateremo. Prima di procedere oltre facciamo le seguenti considerazioni che in parte
approfondiremo nel corso dellarticolo:
le classi orientati ai dati possono essere usate come gli altri oggetti,
Visual Basic, orientati ai dati (DataEnvironment, ADO Data);
le classi possono essere trasformate in componenti COM e quindi in oggetti che
possono sostituire i controlli Visual Basic orientati ai dati; questo non era possibile
con Visual Basic 5 che permetteva di creare solo componenti Consumer;
per usare le classi orientati ai dati bisogna impostare i seguenti riferimenti nel
progetto: Microsoft Data Binding Collection, Microsoft Data Source Interfaces, Microsoft
Activex Data Objects library.
Nellarticolo faremo diversi esempi in particolare vedremo come impostare una classe
Provider e una classe Consumer e come utilizzare le classi con i controlli Visual Basic.
Classi Orientate ai dati
La prima cosa da descrivere è come si crea una classe orientata ai dati. A tal
fine ci sono sia metodi manuali che automatici (attraverso lutilizzo di un Wizard o
di Template), noi come il solito descriveremo il più complesso. Vediamo come
procedere.Create un nuovo
. |
Fig. 1: Mostra la finestra inserisci
modulo di Classe |
progetto Exe Standard ed inserite due moduli di classe. In
tale fase osservate che nella finestra, di inserimento modulo di classe, avete quattro
possibilità: modulo di classe, creazione guidata classi, Consumatore dati complesso e
Fonte Dati. (Fig. 1) Le prime due sicuramente le conoscete a memoria (?), mentre le
restanti, forse, non le avete mai utilizzate, esse permettono di creare la classe Consumer
e la classe Source. Noi, come accennato non utilizzeremo tali modelli in quanto preferiamo
creare le classi, orientati ai dati, attraverso un modulo di classe standard; questo ci
permetterà di capire meglio le proprietà caratteristiche
|
Il codice per la Sorgente
Dopo aver impostato la fonte dati, inserite il seguente
codice nella classe Sorgente. Nella parte dichiarativa:
Private
RecSetcli As ADODB.Recordse |
Nella Sub Class_Initialize:
Private Sub
Class_Initialize() Dim db As Connection Set db = New Connection Set RecSetcli = New
ADODB.Recordset db.CursorLocation = adUseClient db.Open "PROVIDER=MSDASQL;
dsn=clienti;uid=;pwd=;" RecSetcli.Open "select * From clienti", db,
adOpenStatic, adLockOptimistic End Sub |
Nella Initialize abbiamo inserito il
codice per caricare il RecordSet (REcSetcli) che viene restituito ai Consumer (classi o
controlli). Notate che: usiamo una connection collegata, al database attraverso il DSN
Clienti; per il DSN non abbiamo definito Password (pwd) e UserId (uid); il recordset viene
caricato con tutti i record presenti nella tabella Clienti.
Per concludere con la Source inseriamo il codice in GetDataMember, il metodo che deve
restituire, alloccorrenza, RecSetcli al Consumatore. Per GetDataMember è
sufficiente unistruzione dassegnazione.
Private Sub
Class_GetDataMember( DataMember As String, Data As Object)
Set Data = RecSetcli End Sub |
DataBindingBehavior
e DataSourceBehavior
Dunque dopo aver inserito i due moduli di classe (uno per la
Source e laltro per la Consumer) osservate le due proprietà:
DataSourceBehavior DataBindingBehaviorvior (in inglese Behavior sta per comportamento). La
prima è necessaria per trasformare la classe in Source (impostata su vbDataSource) mentre
la seconda serve per trasformare la classe in Consumer. Osservando DataBindingBehavior si
nota che sono disponibili due valori e di conseguenza due tipi di Consumer:
Consumer semplice (vbSimpleBound), un Consumer legato al solo record corrente
della fonte dati.
Consumer complesso (vbComplexBound), legato a più record della fonte dati,
non solo a quello corrente (per esempio questo avviene nel caso di un controllo che mostra
più record).
Noi per semplicità creeremo un Consumer semplice (scusate il giro di parole!!).
Allora, rinominate i moduli di classe rispettivamente Source e Consumer e per Source
impostate DataSourceBehavior, mentre per Consumer impostate DataBindingBehavior su
vbSimpleBound. Dopo queste impostazione noterete che sul modulo Consumer non è successo
niente di particolare, mentre nel modulo di classe Source sono stati inseriti i metodi
GetDataMember e DataMemberChanged e la proprietà DataMembers. Inoltre, noterete che nel
progetto è stato inserito un riferimento a Msdatsrc.tlb (necessario per GetDataMenber).
Oltre a questo però, come accennato, è necessario inserire altri due riferimenti, uno su
Microsoft Activex Data Objects library (oggetti ADO) e un altro su Microsoft Data Binding
Collection (Binding Collection).
GetDataMember
Il metodo GetDataMember fa parte della classe Source.
. |
Fig. 2: La figura mostra la finestra
di progettazione della classe Sorgente |
Esso in fase di disegno può essere selezionato, sulla finestra
di progettazione della classe, attraverso il ComboBox dichiarazioni dopo aver impostato il
ComboBox generale su class. (Fig. 2) GetDataMember presenta i seguenti attributi:
DataMember, una stringa che serve per specificare quale dato (membro dati,
recordset) deve essere restituito dalla classe;
Data, riferimento al data member (nel nostro caso RecordSet ADO) da
restituire.
Il metodo è richiamato automaticamente quando un consumatore di dati richiede una nuova
fonte dati.
La fonte dati
Per fare degli esempi, con le classi orientate ai dati, abbiamo pensato di
servirci di un database con due tabelle. Per esempio con le tabelle Clienti ed Ordini con
i seguenti campi.
Cliente: IdCliente, NomeSocieta, Citta, CAP, Paese, ecc.
Ordine: IdOrdine, ClienteId, Note, NumeroPezzi, ecc.
Il database, di nome Clienti, verrà collegato alla classe sorgente attraverso
unorigine dati ODBC. Questultimo lo creiamo, manualmente, attraverso
lamministratore di ODBC, presente nel pannello di controllo. Se sul vostro computer
non sono presenti i Driver ODBC, potete usare un database creato direttamente in Visual
Basic, come illustrato nei precedenti articoli. |
Il
codice per il Consumatore
Per quanto riguarda la classe Consumer inseriamo il codice per manipolare i
valori che gli vengono forniti dalla classe sorgente, in altre parole gli attribuiti da
associare ai campi di RecSetcli. Per il momento inseriamo solo IDClienti, per gli altri
campi le istruzioni sono analoghe.
Private
MVarIDCliente As String Public Property Get IDCliente() As String IDCliente =
MVarIDCliente End Property Public Property Let IDCliente( mNewVal As String) MVarIDCliente
= mNewVal End Property |
Per poter usare le classi inseriamo dei controlli e del codice sulla
Form1 del progetto. A tal fine disegnate un bottone, un textbox (di nome TxtConsumatore)
sulla form1 e predisponete il seguente codice nella form_ load.
Private
objSorgente As Source Private objBindingCollection As BindingCollection Private
objConsumatore As Consumer Private Sub Form_Load() Set objSorgente = New Source
richiama Initialize della Source Set objBindingCollection = New BindingCollection
Set objConsumatore = New Consumer Set objBindingCollection. DataSource =objSorgente
richiama GetDataMember objBindingCollection.Add Txtconsumatore,"Text",
"IDCliente" associa il txtconsumatore objBindingCollection.Add
objConsumatore, "IDCliente", "IDCliente" associa
lattributo IDCliente della classe Consumatore End Sub |
Il codice precedente è necessario per associare i consumatori e la
sorgente di dati. La Add della BlindingCollection ha la seguente sintassi:
oggetto.Add(OggettoConsumatore,
NomeProprietà
Consumatore, CampoDati,
[ FormatoDati],[ Chiave]).
|
Per questo con objBindingCollection.Add Txtconsumatore,
"Text", "IDCliente" si associa la proprietà text di TxtConsumatore al
campo IDCliente del RecordSet (restituito dalla Classe sorgente). Per mostrare come usare
la classe consumer inseriamo, nella Command_Click, la seguente istruzione.
Private Sub
Command1_Click()
MsgBox objConsumatore.IDCliente
End Sub
|
A questo punto, inserite dei dati nelle tabelle (altrimenti si
blocca tutto, mancano i controlli necessari) ed eseguite il programma. Prima di procedere
oltre vi invitiamo a riflettere su dove è conveniente fare il collegamento (Binding) tra
la classe Source e quella Consumer: a livello di form (come abbiamo fatto), oppure a
livello di classe Consumer?
Source con più RecordSet
Complichiamoci le cose definendo una classe Source con due membri di dati:
RecSetcli e RecSetOrd collegati rispettivamente alla tabella Clienti ed Ordini. In questo
caso la classe Source, in base al valore di DataMember, restituirà uno dei due recordset.
Per imbestialirci ulteriormente sulla form prevediamo un bottone per leggere ciclicamente
|
Fig. 3: La form diesempio. |
i record della fonte dati ed inserirli in una lista (passando
attraverso la classe Consumer). Questa volta, per costruire il codice, iniziamo dalla
form, sulla quale inseriamo i seguenti elementi.
1. Tre Textbox: txtConsumatore, txtConsumatore2 e Text1. I primi due li collegheremo
a RecSetCli attraverso la BindingCollection laltro lo collegheremo nel modo classico
a REcSetOrd.
2. Una ListBox, sulla quale inseriremo, ciclicamente, il contenuto del recordset
relativo ai clienti.
3. Un CommandButton nel quale inseriamo le righe di codice che permettono di
ciclare sui record delle tabelle (naturalmente nellipotesi che i record
siano pochi).
Per la form prevediamo il seguente codice:
Private
Sub Form_Load() Set objSorgente = New Source Set objBindingCollection = New
BindingCollection Set objConsumatore = New Consumer Text1.DataMember = "Ordini"
sceglie il RecordSet Set Text1.DataSource = objSorgente Text1.DataField =
"IdOrdine" Set objBindingCollection. DataSource = objSorgente
objBindingCollection. DataMember = "Clienti" sceglie il RecordSet
objBindingCollection.Add txtConsumatore,"Text", "IDCliente"
objBindingCollection.Add TxtConsumatore1, " Text", "NomeSocieta"
objBindingCollection.Add o bjConsumatore,"IDCliente", "IDCliente"
objBindingCollection.Add objConsumatore, "Ragione", "NomeSocieta" End
Sub |
|
Come
accennato attraverso la proprietà DataMember si specifica il membro dei dati che si vuole
usare (come si capirà tra poco osservando GetDataMember). Nel CommandButton, chiamato
Gira, inseriamo il seguente codice.
Private Sub Gira_Click()
Static MAX As Integer
Static I As Integer
If Not objSorgente.Salta Then
MAX = MAX + 1
List1.AddItem objConsumatore.
IDCliente + " " +
objConsumatore.Ragione
Else
List1.List(I) = List1.List(I)
+ "-" +
objConsumatore.
IDCliente + " " +
objConsumatore.Ragione
I = I + 1
If I = MAX Then I = 0
End If
objSorgente.Gira
End Sub
|
Con Gira_Click, ogni volta che si clicca, si inserisce la
coppia di valori IdCliente, Ragione nella listbox, i record della fonte dati
vengono letti ciclicamente grazie al metodo Gira e allattributo Salta
della classe sorgente. Nella procedura abbiamo definito due variabili Static: MAX ed I che
permettono di individuare il numero di record della tabella e il punto della listbox dove
scrivere (secondo voi al posto di MAX si poteva utilizzare RecSetcli.RecordCount? se si! a
quale livello, Form o Classi?). MAX risulterà uguale al numero di record della tabella
dato che, nella classe Source, lattributo Salta è posto a vero quando si arriva
allultimo record della tabella.
Vediamo come modificare la classe Consumer e la classe Sorgente. Nella consumer inseriamo
un altro attributo Ragione.
Option Explicit
Private MVarRagione As String
Public Property Get Ragione()
As String
Ragione = MVarRagione
End Property
Public Property Let Ragion
(mNewVal As String)
MVarRagione = mNewVal
End Property
|
Mentre nella classe sorgente inseriamo quanto segue.
String, Data As Object)
Select Case DataMember
Case "Clienti"
Set Data = RecSetcli
Case "Ordini"
Set Data = RecSetord
End Select
End Sub
Private Sub Class_Initialize()
come prima
Set RecSetcli = New
ADODB.Recordset
Set RecSetord = New
ADODB.Recordset
RecSetcli.Open "select
* From clienti ", db,
adOpenStatic, adLockOptimistic
DataMembers.Add "Clienti"
RecSetord.Open "select
idordine From Ordini ", db,
adOpenStatic, adLockOptimistic
DataMembers.Add "Ordini"
End Sub |
Il metodo GetDataMember è stato modificato e prevede luso del
parametro DataMember per selezionare il recordset da restituire. Nella procedura
Initialize abbiamo anche previsto il settaggio dellinsieme DataMembers (DataMembers.
Add "Clienti",
) però poi non lo utilizziamo! Per esercizio provate ad
usarlo (suggerimento: con Data MemberChanged). Per concludere inseriamo il codice nel
metodo Gira. Ogni volta che il metodo è richiamato, sposta in avanti di uno il record
corrente, quando arriva allultimo record si posta al primo.
Public Sub Gira()
RecSetcli.MoveNext
RecSetord.MoveNext
If RecSetcli.EOF = True Then
Salta = True
RecSetcli.MoveFirst
End If
If RecSetord.EOF = True Then
RecSetord.MoveFirst
End If
End Sub
|
A questo punto eseguite il programma, anche
attraverso il Debug.
Conclusione
Queste pg possono servire come introduzione ai concetti inerenti larchitettura delle
applicazioni Client/ Server, in parole semplici del modo in cui si organizzano i
componenti di unapplicazione. Larchitettura che di solito si utilizza per tale
scopo è la Three-Tier che prevede la distribuzione dei componenti su tre livelli
(Three-Tier): livello interfaccia, livello servizi e livello dati; dove ognuno di questi
livelli contiene dei componenti specializzati (per esempio il livello interfaccia contiene
solo form, ecc.) che non dipendono da quelli degli altri livelli. |
|