Announcement

Collapse
No announcement yet.

Große Listen...

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Große Listen...

    Hallo zusammen,
    ich habe eine große Liste, die ich per Select aus dem SQL-Server lese. Ich befülle damit eine ComboBox ( Ich benutze Vb 2005 ).

    Aus Performancegründen habe ich mir überlegt erste ein Teil zu laden. Mir ist aber nicht ganz klar wie ich das am Sinnvollsten mache.

    Wie sollte z.B. meine Select aussehen. Wann und wie muss ich den Rest der Daten nachladen...?
    Beim klicken der Combo, oder wann...? Und wie ist das dann am Sinnvollsten...? Geich den ganzen Rest, oder wieder nur ein Teil...? Wie macht man so etwas ....?


    Hat jemand ne Idee, oder sogar nen Beispiel...?


    Danke

  • #2
    Hallo Markus,

    die Antwort auf Deine Frage hängt von 2 Faktoren ab:

    1. Wie viele Einträge müssen aus der Datenbank abgerufen werden.
    2. Welche Bandbreite steht zwischen der Datenbank und Deinem Client zur Verfügung.

    Wenn .NET 2.0 verwendet wird, liegt die grundlegend überarbeitete Version der DataTable-Klasse vor. Gegenüber .NET 1.x arbeitet die neue DataTable auch bei sehr vielen Datensätzen linear, so dass dort Hunderttausende Einträge ohne Probleme untergebracht werden können.

    Da in Deinem Fall höchstens einige Tausend Einträge in der Listbox landen, macht das teilweise Laden keinen Sinn.

    Was eventuell mehr Sinn macht, ist das Auslagern der SELECT-Abfrage in einen zweiten Thread, indem die BackgroundWorker-Komponente genutzt wird. In diesem Fall kann der Anwender ganz normal mit der Benutzeroberfläche hantieren, auch wenn die Daten der SELECT-Abfrage noch nicht vollständig von der Datenbank abgeholt wurden.

    Comment


    • #3
      Originally posted by Andreas Kosch View Post
      Was eventuell mehr Sinn macht, ist das Auslagern der SELECT-Abfrage in einen zweiten Thread, indem die BackgroundWorker-Komponente genutzt wird. In diesem Fall kann der Anwender ganz normal mit der Benutzeroberfläche hantieren, auch wenn die Daten der SELECT-Abfrage noch nicht vollständig von der Datenbank abgeholt wurden.
      Hallo Andreas,
      erst ein Mal besten Dank für deine Antwort...

      Den Select habe ich in einer Stored Procedure auf dem SQL-Server liegen. Aber ich würde gern mal versuchen mit dem BackgroundWorker zu arbeiten. Das habe ich bis jetzt noch nicht getan. Wie setzt man den am besten ein. Hast du evtl. mal nen kleines Beispiel...?

      Noch etwas als Hintergrundwissen:
      Ich Lade die Daten während des Form Load in eine ComboBox. Macht es Sinn in solchem Fall den BackgroundWorker zu nutzten?
      Wann setzt man generell den BackgroundWorker ein?

      Wenn ich z.B. zwei ComboBoxen mit Daten füllen müsste, würde ich das in einem BackgroundWorker setzten oder würde ich dafür zwei benutzten? Muss man in solchem Fall etwas besonderes beachten?

      Danke
      Zuletzt editiert von M Merlin; 28.02.2007, 09:31.

      Comment


      • #4
        Hallo Markus,

        Hast du evtl. mal nen kleines Beispiel...?
        ich bin momentan auf der BASTA! und daher von meinem Archiv und von Visual Studio 2005 "abgeschnitten"

        Wann setzt man generell den BackgroundWorker ein?
        Der Backgroundworker ist "nur" eine sofort einsetzabere (bequeme) Implementierung eines zusätzlichen Threads. Ein Einsatz ist daher immer dann sinnvoll, wenn der primäre Thread der Anwendung (dort läuft die Benutzeroberfläche) durch eine lang andauernde Funktion blockiert würde. Dies macht sich zum Beispiel bemerkbar, wenn der Benutzer das Fenster verschiebt, ob dort weisse Flächen sichtbar werden, weil der primäre Thread momentan während einer Funktino die Botschafswarteschlange nicht auslesen kann.

        In Deinem konkrete Fall hängt es davon ab, ob der Anwender durch die Ladezeit der Daten "belästigt" wird. Einen zusätzlichen Thread (Backgroundworker) sollte man nur dann einsetzen, wenn es auch einen sichtbaren Vorteil der parallelen Verarbeitung gibt.

        Comment


        • #5
          Ich habe mal mit dem Backgroundworker etwas getestet. Das geht dann doch nicht so einfach. Da ich noch Info`s an die Form (Statusstrip ) zurückgebe.... ( bekomme Fehler - anderer Thread.... )

          Da muss man dann wohl mit Delegate/Invoke arbeiten.... Irgendwie hab ich das nicht hinbekommen....

          Würde mich dann doch über nen kleines Beispiel freuen...

          Danke

          Comment


          • #6
            Hallo Markus,

            sobald ich von der BASTA!2007 Spring zurück bin, mache ich mich gleich an die Arbeit ;-)

            Comment


            • #7
              Ich wollte natürlich bei der BASTA nicht stören....


              Schon mal besten Dan im voraus für die Bemühungen....

              Comment


              • #8
                @ andreas Kosch

                Hallo,
                ich wollte mal fragen, ob sie noch an das Codebeispiel denken....?

                Comment


                • #9
                  Hallo,

                  ja - aber man glaubt gar nicht, welche EMail-Berge in nur 5 Tagen entstehen können. Aber das Wochenende steht ja kurz bevor, so dass spätestens am Sonntag das Beispiel da sein sollte ;-)

                  Comment


                  • #10
                    Hallo,

                    wie versprochen, füge ich das BackgroundWorker-Beispiel an. Damit der Ladevorgang auch sichtbar wird, holt sich das Programm 19972 Datensätze aus der AdventureWorks-Datenbank eines Servers ab:

                    <div style="font-family: Consolas; font-size: 10pt; color: black; background: white;"><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">Private</span> <span style="color: blue;">Sub</span> Form1_Load(<span style="color: blue;">ByVal</span> sender <span style="color: blue;">As</span> System.Object, <span style="color: blue;">ByVal</span> e <span style="color: blue;">As</span> System.EventArgs) <span style="color: blue;">Handles</span> <span style="color: blue;">MyBase</span>.Load</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">' Benutzeroberfl&#228;che von der Datenquelle trennen</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ContactBindingSource.RaiseListChangedEvents = <span style="color: blue;">False</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">' Datens&#228;tze in einem separaten Thread von der Datenbank abholen</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; BackgroundWorker1.RunWorkerAsync()</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">' Auswirkung des separaten Threads sichtbar machen</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ListBox1.Items.Add(<span style="color: #a31515;">"BackgroundWorker-Methode RunWorkerAsync wurde aufgerufen"</span>)</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ListBox1.Items.Add(<span style="color: #a31515;">"&nbsp; -&gt; Anzahl der Datens&#228;tze im DataSet: "</span> + DataSet1.Contact.Rows.Count.ToString())</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">End</span> <span style="color: blue;">Sub</span></p><p style="margin: 0px;">&nbsp;</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">Private</span> <span style="color: blue;">Sub</span> BackgroundWorker1_DoWork(<span style="color: blue;">ByVal</span> sender <span style="color: blue;">As</span> System.Object, <span style="color: blue;">ByVal</span> e <span style="color: blue;">As</span> System.ComponentModel.DoWorkEventArgs) <span style="color: blue;">Handles</span> BackgroundWorker1.DoWork</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">Me</span>.ContactTableAdapter.Fill(<span style="color: blue;">Me</span>.DataSet1.Contact)</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">End</span> <span style="color: blue;">Sub</span></p><p style="margin: 0px;">&nbsp;</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">Private</span> <span style="color: blue;">Sub</span> BackgroundWorker1_RunWorkerCompleted(<span style="color: blue;">ByVal</span> sender <span style="color: blue;">As</span> System.Object, <span style="color: blue;">ByVal</span> e <span style="color: blue;">As</span> System.ComponentModel.RunWorkerCompletedEventArgs) <span style="color: blue;">Handles</span> BackgroundWorker1.RunWorkerCompleted</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">' Auswirkung des separaten Threads sichtbar machen</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ListBox1.Items.Add(<span style="color: #a31515;">"BackgroundWorker-Ereignis RunWorkerCompleted wurde ausgel&#246;st"</span>)</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ListBox1.Items.Add(<span style="color: #a31515;">"&nbsp; -&gt; Anzahl der Datens&#228;tze im DataSet: "</span> + DataSet1.Contact.Rows.Count.ToString())</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">' DataSet ist gef&#252;llt, die Datenbindung der ComboBox aktivieren</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ContactBindingSource.RaiseListChangedEvents = <span style="color: blue;">True</span></p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ContactBindingSource.DataSource = DataSet1</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; EmailAddressComboBox.DisplayMember = DataSet1.Contact.EmailAddressColumn.ColumnName</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; EmailAddressComboBox.ValueMember = DataSet1.Contact.ContactIDColumn.ColumnName</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; EmailAddressComboBox.DataSource = ContactBindingSource</p><p style="margin: 0px;">&nbsp;&nbsp;&nbsp; <span style="color: blue;">End</span> <span style="color: blue;">Sub</span></p></div>

                    Bevor der Anhang ausgeführt wird, muss die Verbindungszeichenfolge auf die Bezeichnung des eigenen Servers geändert werden
                    Attached Files

                    Comment


                    • #11
                      O.K. Danke....

                      Mein Problem liegt darin, das ich im doWork noch Info`s an die Form(Statusstrip ) zurückgeben möchte. Da bekomme ich einen Fehler - anderer Thread....

                      Comment


                      • #12
                        Hallo,

                        in diesem Fall muss innerhalb von DoWork auch die Hilfe der MethodInvoker-Klasse in Anspruch genommen werden. Das erweiterte Beispiel sieht nun so aus:

                        Code:
                            ' Puffervariable für die Information, 
                            ' die von einem Thread zum anderen übergeben werden soll
                            Private sStatus As String
                        
                            Private Sub Form1_Load(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) Handles MyBase.Load
                                ' Benutzeroberfläche von der Datenquelle trennen
                                ContactBindingSource.RaiseListChangedEvents = False
                                ' Datensätze in einem separaten Thread von der Datenbank abholen
                                BackgroundWorker1.RunWorkerAsync()
                                ' Auswirkung des separaten Threads sichtbar machen
                                ListBox1.Items.Add("BackgroundWorker-Methode RunWorkerAsync wurde aufgerufen")
                                ListBox1.Items.Add("  -> Anzahl der Datensätze im DataSet: " + DataSet1.Contact.Rows.Count.ToString())
                            End Sub
                        
                            Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
                              ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
                                '
                                ' MethodeInvoker sorgt dafür, dass der Aufruf im anderen Thread ausgeführt wird
                                '
                                Dim aMI As New MethodInvoker(AddressOf DoSetStatus)
                                '
                                ' Threadsicherer Zugriff auf die Puffervariable, über die die Daten zwischen 
                                ' den einzelnen Threads ausgetauscht werden
                                '
                                SyncLock Me
                                    sStatus = "Schritt 1"
                                End SyncLock
                                '
                                ' Statusanzeige im Formular aktualisieren, indem die 
                                ' MethodeInvoker-Instanz ausgeführt wird
                                '
                                StatusStrip1.Invoke(aMI)
                                '
                                ' Thread eine halbe Sekunde schlafen legen, damit das Ergebnis
                                ' visuell sichtbar ist.
                                '
                                System.Threading.Thread.Sleep(500)
                                SyncLock Me
                                    sStatus = "Schritt 2"
                                End SyncLock
                                StatusStrip1.Invoke(aMI)
                                System.Threading.Thread.Sleep(500)
                                SyncLock Me
                                    sStatus = "Schritt 3"
                                End SyncLock
                                StatusStrip1.Invoke(aMI)
                                'Me.ContactTableAdapter.Fill(Me.DataSet1.Contact)
                            End Sub
                        
                            Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, _
                              ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
                                ' Auswirkung des separaten Threads sichtbar machen
                                ListBox1.Items.Add("BackgroundWorker-Ereignis RunWorkerCompleted wurde ausgelöst")
                                ListBox1.Items.Add("  -> Anzahl der Datensätze im DataSet: " + DataSet1.Contact.Rows.Count.ToString())
                                ' DataSet ist gefüllt, die Datenbindung der ComboBox aktivieren
                                ContactBindingSource.RaiseListChangedEvents = True
                                ContactBindingSource.DataSource = DataSet1
                                EmailAddressComboBox.DisplayMember = DataSet1.Contact.EmailAddressColumn.ColumnName
                                EmailAddressComboBox.ValueMember = DataSet1.Contact.ContactIDColumn.ColumnName
                                EmailAddressComboBox.DataSource = ContactBindingSource
                            End Sub
                        
                            '
                            ' Hilfsmethode wird über die MethodInvoker-Instanz aufgerufen, so dass
                            ' der Zugriff auf die Statusanzeige immer vom primären Thread des
                            ' Formulars aus erfolgt
                            '
                            Private Sub DoSetStatus()
                                ToolStripStatusLabel1.Text = sStatus
                            End Sub

                        Comment


                        • #13
                          O.K. Danke....

                          Das hat mir sehr weiter geholfen....

                          Comment


                          • #14
                            @ Andreas Kosch

                            Hallo...

                            Ich habe noch mal ne Frage....

                            Wenn ich im doWork vom BackGroundWorker an mehreren Controls etwas übergeben muss, muss ich dann auch für jedes Control ein MethodInvoker deklarieren?

                            Oder wenn ich für ein Control an zwei verschiedenen Stellen im doWork auch zwei verschiedene Eigenschaften übergebe....

                            Muss ich für alles eine neue MethodInvoker deklarieren ?

                            z.B.:

                            Code:
                            Dim aMIS As New MethodInvoker(AddressOf DoSetStatus)
                            Dim aMILV As New MethodInvoker(AddressOf DoSetLabelVisible)
                            Dim aMILC As New MethodInvoker(AddressOf DoSetLabelCaption)
                            oder geht das auch einfacher...?


                            Desweiteren würde ich gern wissen, wenn ich z.B. 3 ComboBoxen parallel füllen möchte, muss ich dann auch 3 BackGraundworker deklarieren.? Oder, wie gehe ich in solchem Fall vor?
                            Zuletzt editiert von M Merlin; 21.03.2007, 17:53.

                            Comment


                            • #15
                              Hallo,

                              innerhalb der Hilfsmethode DoSetStatus können mehrere Controls des gleichen Formulars aktualisiert werden (genauer formuliert: es können alle die Controls aktualisiert werden, die vom gleichen Thread erzeugt wurden).

                              Wenn die Daten für die 3 ComboBoxen alle von der gleichen Datenbank geladen werden, macht das Auslagern in jeweils einen eigenen Thread keinen Sinn. Denn in diesem Fall ist die Netzwerkverbindung der Flaschenhals, der die Datenübertragung serialisiert. Nur dann, wenn die Daten von unterschiedlichen Servern stammen und die verschiedenen Server unterschiedlich schnell arbeiten, hätte das parallele Laden über separaten Threads einen Vorteil.

                              Comment

                              Working...
                              X