Oppure

Loading
28/08/12 21:51
peteruncle9
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.
aaa
29/08/12 6:19
ampeg
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
aaa
29/08/12 8:02
peteruncle9
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)
aaa
29/08/12 10:24
ampeg
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
aaa
29/08/12 11:38
peteruncle9
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).
aaa
29/08/12 15:31
ampeg
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




aaa
29/08/12 16:43
peteruncle9
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.
aaa
29/08/12 18:32
ampeg
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

Imports System.Data.OleDb
Imports System.ComponentModel

Public Class Form1
  Private _bw As New BackgroundWorker
  Private _cn As New OleDb.OleDbConnection

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    _bw.WorkerSupportsCancellation = True

    AddHandler _bw.DoWork, AddressOf _bw_DoWork
    AddHandler _bw.RunWorkerCompleted, AddressOf _bw_RunWorkerCompleted

    'impostare la stringa di connessione del DB
    _cn.ConnectionString = "connection string"
    _cn.Open()

  End Sub

  Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
    _cn.Close()
  End Sub

  Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged
    Call Fill()
  End Sub

  Private Sub Fill()
    Dim dt As New DataTable
    Dim cmd As New OleDbCommand
    Dim da As New OleDbDataAdapter

    cmd.Connection = _cn
    da.SelectCommand = cmd

    'qui va la query per la selezione nominativi, mettere i nomi tabelle e campi desiderati
    cmd.CommandText = "SELECT * FROM Persone WHERE Nome LIKE '" & TextBox1.Text & "%'"

    da.Fill(dt)
    dgv1.DataSource = dt

    If dgv1.Rows.Count > 0 Then

      If _bw.IsBusy Then
        _bw.CancelAsync()
      Else
        _bw.RunWorkerAsync()
      End If

    Else

      dgv2.DataSource = Nothing
    End If

  End Sub

  Private Sub _bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)

    Dim cmd As New OleDbCommand
    Dim da As New OleDbDataAdapter
    Dim dt As New DataTable

    'recupero l'idpersona dalla prima riga di dgv1
    'mettere il nome della colonna desiderato
    Dim sID As String = dgv1.Rows(0).Cells("IDPersona").Value.ToString

    cmd.Connection = _cn
    da.SelectCommand = cmd

    'qui va la query per la selezione ordini, mettere i nomi tabelle e campi desiderati
    cmd.CommandText = "SELECT  * FROM Ordini WHERE IDPersona = " & sID

    da.Fill(dt)

    e.Result = dt

  End Sub

  Private Sub _bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)

    If e.Cancelled = True Then

    ElseIf e.Error IsNot Nothing Then

    Else

      Dim dtResult As DataTable = e.Result
      dgv2.DataSource = dtResult

    End If
  End Sub

End Class
Ultima modifica effettuata da ampeg 30/08/12 5:51
aaa