Una delle cose di cui non possono fare a meno i programi, sono sicuramente le barre di progressione.
Durante le operazioni piu' costose in termini di tempo, qualunque programma ben fatto ha obbligatoriamente una barra di progressione che avanzando fa aspettare piu' volentieri ed eventualmente invoglia a fare qualcosa d'altro per tornare più tardi. Vediamo qui di seguito la screenshot del programma che andremo a scrivere:
Il programma in esecuzione
Tutto questo si può fare agevolmente (anzi si deve fare!) nei cicli, dove si conteggia a priori il numero esatto di loop e poi si incrementa la progressione.
Purtroppo però volte non sempre è facile sapere quanti passaggi si faranno, come nel caso di query dove non si piò sapere in anticipo il numero di righe ritornate.in questo caso percio' spesso si tende a far eseguire la query e aspettare il risultato, magari mettendo l'icona del cursore con la clessidra, cosa che mediamente può andar bene ma nel caso di moltissimi dati da lo spiacevole effetto "inchiodato".
A questo inconveniente si può ovviare facendo una query preventiva per farsi dare il conteggio delle righe che andremmo a prelevare per poi prelevarle effettivamente con la classe #DataReader (# sta per oledb,sql etc..).
La classe #DataReader ci per mette di leggere i dati riga per riga con il metodo 'Read' che sposta il puntatore al record successivo.
Basterà girare per ogni colonna della riga e farsi dare il valore della cella per creare una tabella come risultato della query.
Ma piu' precisamente:
Innanzitutto si devono preparare gli oggetti base ado.net per accedere ed eseguire query su database e cioè un command per le query, una connessione e ovviamente un reader:
System.Data.SqlClient.SqlConnection cn = new System.Data.SqlClient.SqlConnection(@"data source=(local)\netsdk;initial catalog=northwind;persist security info=False;user id=sa");
System.Data.SqlClient.SqlDataReader zRd;
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("",cn);
cn.Open();
Facciamo una query in più di quella effettiva che dia il conteggio (quindi un numero) delle righe prima di leggerle.
Lo standard sql ci viene in aiuto, infatti basta anteporre "select count(*) from " alla %miaquery% in questo modo:
cmd.CommandText = "select count(*) from (%miaquery%) as tbl";
ed ottimizzare chiamando ExecuteScalar che ritorna un valore unico senza creare tabelle:
progressBar1.Maximum = (System.Int32)cmd.ExecuteScalar();
progressBar1.Value =0;
adesso disponiamo del conteggio delle righe che ci ritornerà la %miaquery% quindi riutilizziamo il command pereseguire quest'ultima facendoci tornare un Reader per "scorrere" i dati.
In più ci salviamo il conteggio delle colonne (dato che dovremmo girare su ogni cella è meglio parcheggiarlo su una variabile) :
cmd.CommandText = %miaquery%;
zRd = cmd.ExecuteReader();
int zCount = zRd.FieldCount;
adesso abbiamo tutto quello che serve per creare una tabella con i dati, per prima cosa creiamo lo schema delle colonne:
DataTable zDt = new DataTable("miaTabella");
for(int i = 0 ; i < zCount; i++)
{
zDt.Columns.Add(zRd.GetName(i), zRd.GetFieldType(i));
}
e poi la riempiamo:
while(zRd.Read())
{
DataRow zdr = zDt.NewRow();
for(int i = 0 ; i < zCount; i++)
{
zdr = zRd.GetValue(i);
}
zDt.Rows.Add(zdr);
progressBar1.Value += 1;
}
Utilizzando il Reader dobbiamo ricordarci di chiudere la connessione (altrimenti rimane aperta con uno spreco di risorse) e po possiamo agganciare alla griglia la tabella completa:
cn.Close();
dg.DataSource = zDt;
Nel caso dell'esempio si incrementa ad ogni nuova riga ma è possibile essere più precisi incrementando nel ciclo interno (per ogni cella) e quindi il max della progressbar sara = nRighe * NColonne
Pro:Fa aspettare "più volentieri" il risultato di interrogazioni pesanti
Contro:Funziona solo con database che supportano le subquery come Access, Sql e Mysql (versione 4.1 in poi).
Si raddoppia il numero delle query, anche se una delle 2 è minima fa sempre lavorare il database.
Non si utilizza un #DataAdapter che torna utile per aggiornare i dati dopo eventuali modifiche.