VB
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 l’architettura 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 dell’articolo:
• 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.
Nell’articolo 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 l’utilizzo di un Wizard o di Template), noi come il solito  descriveremo il più complesso. Vediamo come procedere.Create un nuovo

classifigura1.jpg (6589 byte) .
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, all’occorrenza, RecSetcli al Consumatore. Per GetDataMember è sufficiente un’istruzione d’assegnazione.

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 l’altro 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.

classifigura2.jpg (7722 byte) .
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 un’origine dati ODBC. Quest’ultimo lo creiamo, manualmente, attraverso l’amministratore 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 l’attributo 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

classifigura3.jpg (4985 byte)
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 l’altro 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 nell’ipotesi 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 all’attributo “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, l’attributo Salta è posto a vero quando si arriva all’ultimo 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 l’uso del parametro DataMember per selezionare il recordset da restituire. Nella procedura Initialize abbiamo anche previsto il settaggio dell’insieme 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 all’ultimo 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 l’architettura delle applicazioni Client/ Server, in parole semplici del modo in cui si organizzano i componenti di un’applicazione. L’architettura 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.