C# - Datagrid und Backgroundworker - Performance



  • Hallo,

    ich bin ein ziemlicher Neuling in C# und versuche gerade eine Datenquelle an ein Datagrid zu binden. Das funktioniert auch soweit, allerdings "friert" genau dann die Oberfläche ein, solange die Daten an das Datagrid gebunden werden.

    Ich habe die Performance durch Virtualisation bereits deutlich steigern können. Allerdings würde ich dem User gerne einen Cancel-Button zur Verfügung stellen, falls ihm die Abfrage doch zu lange dauert. Dafür habe ich mich mit dem Thema Backgroundworker auseinander gesetzt.Wenn ich eine einfache Schleife einbaue, kann ich andere Elemente auf der Oberfläche anklicken und die Abfrage durch den Cancel-Button beenden, aber nicht mit dem Binden der Datenquelle an das Datagrid.

    Stoße ich hier mit dem BackgroundWorker and die Grenzen des Machbaren oder ich nutze ich ihn einach nur falsch? 🙂

    Hier der Code in der DoWork-Funktion meines BackgroundWorkers

    void worker_DoWork(object sender, DoWorkEventArgs e)
        {
    
              dgOutput.Dispatcher.BeginInvoke(
                    (Action)(() =>
                   {
                       result = Am.getKundenView(txtFirma.Text,
                                                  txtPLZ.Text,
                                                  txtOrt.Text,
                                                  txtAbteilung.Text,
                                                  txtName.Text,
                                                  txtVorname.Text,
                                                  "",
                                                  ""
                                                 );
    
                     dgOutput.ItemsSource = result; 
    
       }
    


  • Hallo,

    so wie du bisher den BackgroundWorker und den Dispatcher benutzt macht es keinen Sinn, denn mittels BeginInvoke führst du die gesamte Methode wieder im GUI-Hauptthread aus.
    Richtig(er) wäre:

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // Aufruf innerhalb des BackgroundWorker-Threads
        result = Am.getKundenView(...);
    
        dgOutput.Dispatcher.BeginInvoke(
                (Action)(() =>
                { 
                    dgOutput.ItemsSource = result;
                } 
    }
    

    Aber eigentlich wurde der BackgroundWorker extra so entworfen, daß man keinen manuellen (Begin)Invoke-Aufruf mehr benötigt, denn im RunWorkerCompleted-Ereignis kannst du direkt auf die Controls zugreifen:

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        dgOutput.ItemsSource = result;
    }
    

    (natürlich vorher diese Methode abonnieren 😉



  • Hi, vielen herzlichen Dank für deine prompte Antwort !!

    Ich erhalte nun folgende Fehlermeldung

    "Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet."

    Diese tritt bereits in der doWork wegen der getKundenView-Methode auf:

    void worker_DoWork(object sender, DoWorkEventArgs e)
            {
    
                result = Am.getKundenView(txtFirma.Text,
                                                   txtPLZ.Text,
                                                   txtOrt.Text,
                                                   txtAbteilung.Text,
                                                   txtName.Text,
                                                   txtVorname.Text,
                                                   "",
                                                   ""
                                                   );
    
            }
    

    Die Variable result ist vom Typ IQueryable, die ich mittels einer LinQ-Abfrage fülle.

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
                {
    
                          dgOutput.ItemsSource = result;
    
                }
    


  • Du darfst nicht auf Objekte der GUI zugreifen, also z.B. auf txtName u.s.w. was schätzungsweise Textboxen sind.
    Ich verstehe auch nicht so ganz was Du damit bezweckst, warum genau benötigt diese Methode (getKundenView) so viele Parameter? Was passiert darin genau?

    Fix: Lies die Werte aus den Controls im GUI Thread aus, übergieb sie irgendwie an die worker_DoWork-Methode und führe dann die langlaufende Aktion aus.



  • Ah super, das ergibt Sinn. Vielen Dank für den Hinweis 🙂

    Der User kann über mehrere Textboxen die View filtern. Das sind dann eben diese Parameter, die du hier siehst. Hast du einen Tipp wie ich die Anzahl der Parameter einschränken kann? Soll ich das lieber über ein Array lösen? Hast du da einen Profi-Tipp für mich? :-))

    Vielen Dank für eure tolle Unterstützung!

    LG Auraya

    PS: Ich habe das jetzt so gemacht wie oben und die Textboxen lese ich über die Begin-Invoke-Methode aus.

    Die Datenbankabfrage an sicht dauert nicht so lange. Auch, wenn es ziemlich viele Daten sind.

    DIE GUI friert so ca. 10 sek ein, sobald ich das IQueryable wie oben beschrieben an das Datagrind binde 😞



  • Das IQueryable ist dann das Problem, denn dieses wird dann wiederum erst im GUI-Thread (bei der Datenbindung) aufgelöst. Du müßtest also schon im Worker-Thread die Daten holen, z.B. per ToList():

    result = Am.getKundenView(...).ToList();
    


  • Hallo,

    leider hatte ich in der Zwischenzeit keine Zeit mich um das Projekt zu kümmern und muss deshalb dieses Thema nochmal aufgreifen.

    Wie kann ich denn aus einer IQueryable eine List machen? Das funktioniert nicht so einfach mit diesem toList()-Methoden-Aufruf!

    Ich mache eine LINQ-Abfrage und ich erhalte dafür auch ein IQueryable zurück, wenn ich wie folgt über den DataContext auf die Daten zugreife:

    FROM x in zDataContext.view
    SELECT  new {a=x.a,
                b=x.b,
                c=x.c};
    

    Ich bin ja noch ziemlicher Anfänger in C# und verstehe dann die anschließende Typenkonvertierung <> noch nicht so ganz. Hat da jemand für mich einen hilfreichen, weiterführenden Link?

    Vielen Dank,

    CAuraya



  • folgende Seite b hat mich auch zu diesem Thema ein wenig weiter gebracht. Also von daher Closed 🙂

    http://stackoverflow.com/questions/8738010/get-linq-subquery-into-a-user-defined-type


Anmelden zum Antworten