Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
C# / VB.NET - Caricamento asincrono TableAdapter?
Forum - C# / VB.NET - Caricamento asincrono TableAdapter?

Avatar
peteruncle9 (Normal User)
Rookie


Messaggi: 26
Iscritto: 30/05/2009

Segnala al moderatore
Postato alle 23:51
Martedì, 28/08/2012
Salve, devo caricare le righe di una tabella di un database durante la digitazione in una casella di testo, in modo che i risultati compaiano durante la digitazione. Per non rallentare l'UI vorrei far avvenire il caricamento in un BackgroundWorker, ma ci sono due problemi:
1) Se digito due lettere velocemente non fa in tempo a caricare la tabella e si sovrappongono i due comandi RunWorkerAsync(), invece se provo a fermare il backgroundworker, la datagridview a cui è associata la tabella manda un messaggio di errore nei dati
2) Quando un backgroundworker carica una tabella per cui nella datagridview è necessaria la scrollbar, resta lo spazio nero sulla destra ma non viene disegnata. Ho provato con doublebuffering ma nemmeno questo risolve.

PM Quote
Avatar
ampeg (Normal User)
Pro


Messaggi: 124
Iscritto: 21/04/2011

Segnala al moderatore
Postato alle 8:19
Mercoledì, 29/08/2012
prova a caricare la tabella su una DataTable e fai la ricerca tramite il metodo Select, dovrebbe laveorare più velocemente che non direttamente sul DB

PM Quote
Avatar
peteruncle9 (Normal User)
Rookie


Messaggi: 26
Iscritto: 30/05/2009

Segnala al moderatore
Postato alle 10:02
Mercoledì, 29/08/2012
Ho provato questo, ed era una buona soluzione visto che il metodo BindingSource.Filter è molto veloce, ma il problema era lo stesso: al primo caricamento impiegava più di 30 secondi (la tabella ha circa 50.000 righe). La soluzione sarebbe l'utilizzo del BackgroundWorker solo per caricare la tabella la prima volta, ma non riesco a capire per quale motivo si blocca la barra di scorrimento della datagridview (resta lo spazio a destra ma non viene più disegnata)

PM Quote
Avatar
ampeg (Normal User)
Pro


Messaggi: 124
Iscritto: 21/04/2011

Segnala al moderatore
Postato alle 12:24
Mercoledì, 29/08/2012
cercare di ottenere quello che vuoi fare con oltre un certo numero di righe diventa problematico e non ha molto senso, perché mentre digiti il testo da ricercare potresti ottenere una griglia con migliaia di record... a chi interessa visualizzare "al volo" una griglia con tutti quei record ? ... è una situazione che si verifica senza alcuna utilità per nessuno, perché poi immagino che il dato rilevante sia il risultato finale che porta a visualizzare un numero di righe "umanamente visionabili"... non so se mi son spiegato

quindi potresti prendere in considerazione un metodo differente

spiega nel dettaglio cosa devi fare, che tipo di ricerca, su quale campo perché se questo campo contiene molti valori che si ripetono puoi caricare una distinta di quel campo in una combobox, riducendo notevolmente i tempi di lettura dal DB

impostare la proprietà della combo "AutoCompleteSource = AutoCompleteSource.ListItems" e "AutoCompleteMode = AutoCompleteMode.SuggestAppend" affinché quando digiti il testo nella combo ti venga suggerito un autocompletamento della parola, quando vuoi che vengano visualizzati i dati nella gridview relativi alla parola, lo fai premendo il tasto invio

PM Quote
Avatar
peteruncle9 (Normal User)
Rookie


Messaggi: 26
Iscritto: 30/05/2009

Segnala al moderatore
Postato alle 13:38
Mercoledì, 29/08/2012
Una tabella contiene un elenco di persone con i propri dati (nome, cognome, data di nascita...), un'altra contiene gli ordini di ogni persona, e ovviamente ogni persona può avere più record associati nella seconda tabella .
Bisogna cercare gli ordini associati a una persona partendo da nome e cognome.

Inizialmente il programma carica la prima tabella ("Persone") completamente in memoria, ed è visibile in una datagridview.
Attraverso la digitazione in tre textbox (Nome, Cognome e Data di nascita) la datagridview viene filtrata (DataBindingSource.filter = ...). Serve una datagridview per poter vedere istantaneamente persone con lo stesso cognome, ed i dati associati (residenza, email, telefono) senza dover aprire tutta la scheda con le informazioni.
Subito dopo aver filtrato la tabella Persone (questo avviene in modo istantaneo), devo caricare la seconda tabella "Ordini", in modo che visualizzi in una seconda DataGridView gli ordini associati alla prima riga della tabella "Persone" (e quando si seleziona un'altra riga si aggiorni la seconda datagridview con i dati associati all'altra riga); questo avviene con una query al database (OrdiniTableAdapter.FillByIDpersona(...)).

Ed ecco il problema: questa query rallenta tutta l'esecuzione, la digitazione nelle textbox va a scatti e l'interfaccia grafica si blocca.
Le alternative che avevo pensato erano o
- caricare tutta la tabella "Ordini" in memoria come la tabella "Persone" e poi filtrare il BindingSource con l'ID della persona, però come dici è un inutile spreco di risorse ed impiega troppo tempo,
- oppure utilizzare un BackgroundWorker per la query FillByIDpersona, ma ottengo un errore se digito la seconda lettera nella textbox prima che abbia finito di caricare la tabella perchè invia il comando RunWorkerAsync mentre è ancora in esecuzione, e cancellare il worker comporta un errore nell'associazione di dati. Inoltre caricando la tabella in background comporta che la datagridview si blocchi e non disegni più la barra di scorrimento (si vede lo spazio in nero).

PM Quote
Avatar
ampeg (Normal User)
Pro


Messaggi: 124
Iscritto: 21/04/2011

Segnala al moderatore
Postato alle 17:31
Mercoledì, 29/08/2012
ok per la griglia "persone", anch'io uso quel metodo per filtrare clienti e fornitori, ma solo perché so già che in prospettiva non avrò ma i un numero tale di record da rallentare l'operazioni in tempi disumani

x la seconda grigla "ordini" tu dici:


Subito dopo aver filtrato la tabella Persone (questo avviene in modo istantaneo), devo caricare la seconda tabella "Ordini", in modo che visualizzi in una seconda DataGridView gli ordini associati alla prima riga della tabella "Persone"


se ho capito bene in pratica la seconda griglia si popola in atomatico poiché ogni volta che vengono trovati record nella prima griglia "persone" si autoseleziona la prima riga ed invoca l'evento per popolare la seconda griglia

è necessario che si comporti in questo modo, cioè è voluta questa condizione o ne puoi fare a meno ?

poi gli altri dati x gli ordini degli altri nominativi possono essere ottenuti selezionando manualmente le righe della prima griglia e questo è ok

al la dell'uso del thread separato (che secondo me non è la soluzione adatta al tuo problema) il motivo per cui rallenta la ricerca è perché ad ogni carattere digitato avvengono 2 ricerche, la prima sulle persone da filtrare, la seconda sugli ordini relativi alla prima riga trovata di "persone", mentre dovrebbe esserci una sola ricerca, quella sulle "persone", la seconda ricerca deve avvenire solo se decidi di selezionare manualmente una riga dalla prima griglia





PM Quote
Avatar
peteruncle9 (Normal User)
Rookie


Messaggi: 26
Iscritto: 30/05/2009

Segnala al moderatore
Postato alle 18:43
Mercoledì, 29/08/2012
Il riempimento della tabella "Ordini"  automatico con la ricerca con l'autoselezione della prima riga della tabella "Persone" è utile perchè fa risparmiare un passaggio, dato che l'obiettivo della ricerca è prevalentemente ApriOrdine(...).

Con il thread separato non ho rallentamenti ma l'applicazione diventa più instabile, la datagridview genera un DataError se annullo il thread durante l'esecuzione per riavviarlo (cioè digito un'altro carattere nella textbox di ricerca), e potrei gestirlo (e.cancel = true); ma non riesco a capire per quale motivo se carico una tabella da un thread separato la datagridview si blocca e non visualizza la barra di scorrimento (lasciando lo spazio nero a destra, ma con le freccette riesco a scorrerla).

C'è lo stesso problema nella datagridview se provo a mettere in un thread separato il caricamento iniziale delle "Persone", ed è un peccato perchè mi consentirebbe di evitare di bloccare per circa dieci secondi l'interfaccia grafica.

PM Quote
Avatar
ampeg (Normal User)
Pro


Messaggi: 124
Iscritto: 21/04/2011

Segnala al moderatore
Postato alle 20:32
Mercoledì, 29/08/2012
ho provato a ricostruire uno scenario simile con il mio DB, ti posto il codice come l'ho pensato io
a me funziona bene anche con una quantità considerevole di record, è veloce e non da problemi di instabilità vedi se può esserti utile

tieni presente che l'ho provato su un DB SQL Server

sul Form1 c'è un TextBox1 per digitare la ricerca

ci sono 2 datagridview (dgv1, dgv2): nella prima ci sono i nominativi, nella seconda gli ordini

Codice sorgente - presumibilmente VB.NET

  1. Imports System.Data.OleDb
  2. Imports System.ComponentModel
  3.  
  4. Public Class Form1
  5.   Private _bw As New BackgroundWorker
  6.   Private _cn As New OleDb.OleDbConnection
  7.  
  8.   Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
  9.  
  10.     _bw.WorkerSupportsCancellation = True
  11.  
  12.     AddHandler _bw.DoWork, AddressOf _bw_DoWork
  13.     AddHandler _bw.RunWorkerCompleted, AddressOf _bw_RunWorkerCompleted
  14.  
  15.     'impostare la stringa di connessione del DB
  16.     _cn.ConnectionString = "connection string"
  17.     _cn.Open()
  18.  
  19.   End Sub
  20.  
  21.   Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
  22.     _cn.Close()
  23.   End Sub
  24.  
  25.   Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged
  26.     Call Fill()
  27.   End Sub
  28.  
  29.   Private Sub Fill()
  30.     Dim dt As New DataTable
  31.     Dim cmd As New OleDbCommand
  32.     Dim da As New OleDbDataAdapter
  33.  
  34.     cmd.Connection = _cn
  35.     da.SelectCommand = cmd
  36.  
  37.     'qui va la query per la selezione nominativi, mettere i nomi tabelle e campi desiderati
  38.     cmd.CommandText = "SELECT * FROM Persone WHERE Nome LIKE '" & TextBox1.Text & "%'"
  39.  
  40.     da.Fill(dt)
  41.     dgv1.DataSource = dt
  42.  
  43.     If dgv1.Rows.Count > 0 Then
  44.  
  45.       If _bw.IsBusy Then
  46.         _bw.CancelAsync()
  47.       Else
  48.         _bw.RunWorkerAsync()
  49.       End If
  50.  
  51.     Else
  52.  
  53.       dgv2.DataSource = Nothing
  54.     End If
  55.  
  56.   End Sub
  57.  
  58.   Private Sub _bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
  59.  
  60.     Dim cmd As New OleDbCommand
  61.     Dim da As New OleDbDataAdapter
  62.     Dim dt As New DataTable
  63.  
  64.     'recupero l'idpersona dalla prima riga di dgv1
  65.     'mettere il nome della colonna desiderato
  66.     Dim sID As String = dgv1.Rows(0).Cells("IDPersona").Value.ToString
  67.  
  68.     cmd.Connection = _cn
  69.     da.SelectCommand = cmd
  70.  
  71.     'qui va la query per la selezione ordini, mettere i nomi tabelle e campi desiderati
  72.     cmd.CommandText = "SELECT  * FROM Ordini WHERE IDPersona = " & sID
  73.  
  74.     da.Fill(dt)
  75.  
  76.     e.Result = dt
  77.  
  78.   End Sub
  79.  
  80.   Private Sub _bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
  81.  
  82.     If e.Cancelled = True Then
  83.  
  84.     ElseIf e.Error IsNot Nothing Then
  85.  
  86.     Else
  87.  
  88.       Dim dtResult As DataTable = e.Result
  89.       dgv2.DataSource = dtResult
  90.  
  91.     End If
  92.   End Sub
  93.  
  94. End Class


Ultima modifica effettuata da ampeg il 30/08/2012 alle 7:51
PM Quote