Benutzer mit den meisten Antworten
Methode in einem Backgroundworker innerhalb einer Klasse ausführen.

Frage
-
Hallo zusammen,
ich habe eine WPF-Anwendung geschrieben, die timerabhängig SQL-Abfragen an unsere ERP-System-DB schickt und daraus Daten in eine Datei exportiert.
Das ganze läuft über einen alten Sybase SQL Anywhere 12 via OdbcDataReader, das zur Umgebung.
Grundsätzlich habe ich folgende Problematik schon häufiger gehabt aber keine adäquate Lösung gefunden:
In meinem Projekt habe ich eine Klasse angelegt, in der ich die Exportfunktionen von der Hauptanwendung "ausgegliedert" habe, um mehr Übersicht zu bekommen. Die Klasse Exports enthält mehrere Methoden, beispielsweise GPEXPORT(), auf die ich im Hauptprogramm zugreife, wenn der es für den Timer mal wieder Zeit ist.
Besagte GPEXPORT()-Methode führt aber eine sehr umfangreiche Abfrage aus, die die UI einfriert. Das kennt man ja.
Also versuche ich mich mal wieder an einem BackgroundWorker, stoße aber wie üblich auf das Problem, wie ich meine Methoden im BackgroundWorker ausführen kann.
Die GPEXPORT()-Methode nimmt zwei Argumente an: string ExportPath und bool? LockedGp.
Im Hauptprogramm rufe ich GPEXPORT dann so auf:
Exports GPexport = new Exports(); GPexport.Dsn = "Der ConnectionString"; GPexport.GPEXPORT("C:\\Export.txt", false);
Dann kommt natürlich der Freeze.
Mache ich das nun in einem BackGroundWorker, wie rufe ich denn dann meine Methode auf?
Ich muss ja erstmal den BackgroundWorker erzeugen. Die zeitaufwendige Arbeit wird dann aber in den MSDN-Beschreibungen dazu immer in einer Methode xyz_DoWork(object Sender, DoWorkEventArgs e) gemacht.Da muss ich doch dann irgendwie meine GPEXPORT()-Methode reinkriegen, der ich dann aber auch noch meine Argumente mitgeben muss und dann, wie rufe ich denn die Methode in meinem Hauptprogramm auf? Denn ich will ja GPEXPORT(..., ...), die dann im Hintergrund ohne die UI zu freezen ausgeführt wird.
Oje, ich hoffe, man kann meine Gedankengänge nachvollziehen.
Denn ich finde diese Gedankenproblematik extrem schwer zu beschreiben.Gruß und vorab schon einmal danke,
monsee
- Bearbeitet monsee Freitag, 3. Juni 2016 08:52
Antworten
-
die Methode BackgroundWorker.ReportProgress kann nicht nur einen int mitliefern sondern auch ein object, in das du alles stecken kannst so z.B. :
new object[] { "aktion_die_du_ausführen_möchtest", "andere werte die du für die gewünschte methode brauchst" };
nun musst du im Progress Changed event nur noch prüfen, was du machen wolltest:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { object[] param = (object[]) e.UserState;
if(param[0].ToString() == "aktion_die_du_ausführen_möchtest")
{
//param[1] ist nur ein beispiel falls du werte mit geben musst, kannst natürlich auch
//mehrere Werte mitgeben die du dann mit param[2], ... aufgreifen kannst
//Tipp: in objects kannst du alles speichern und wieder zu z.B. int zurück umwandeln
AuszuführendeMethode(param[1]);
} }
Ich hoffe, dass das dir einen hilfreichen Ansatz gibt.
Tipp: Nicht vergessen BackgroundWorker.WorkerReportsProgress auf true zu setzen.
My website: http://www.dragonskills99.16mb.com
- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 14. Juni 2016 13:58
- Als Antwort markiert monsee Dienstag, 14. Juni 2016 14:04
-
Hallo Monsee,
das einfachste dürfte sein, wenn Du die Export Klasse als Member Variable in einer Klasse führst. Dann zu Beginn die erforderlichen Parameter setzt und dann RunWorkerAsync startest. Damit hast Du sowohl in DoWork wie in den anderen Ereignissen Zugriff darauf.
public class ExportBackground { private BackgroundWorker backgroundWorker = new BackgroundWorker(); private Exports GPexport = new Exports(); public BackgroundWorkerForm() { backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += ExportBackground_DoWork; backgroundWorker.RunWorkerCompleted += ExportBackground_Completed; } public void Run() { // je nach Geschmack auch ber Parameter oder Eigenschaften der Klasse GPexport.Dsn = "Der ConnectionString"; GPexport.GPEXPORT("C:\\Export.txt", false); // Starten backgroundWorker.RunWorkerAsync(); } private void ExportBackground_DoWork(object sender, DoWorkEventArgs e) { // Verarbeitungsschleife (sollte Methode in GPExport sein) // Ein Ergebnis zurückgeben e.Result = GPexport.Summary; } private void ExportBackground_Completed(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { // Fehlerbehandlung bzw. Abbruch, hier nicht implementiert return; } txtBox1.Text = e.Result as String; } }
Wobei man bei der Übergabe der Parameter mehrere Möglichkeiten hat: Als Parameter für die Run Methode, oder als separate Eigenschaften, die dann Export befüllen - ggf. kann man aus der Export Klasse auch eine Eigenschaft machen.
Gruß Elmar
- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 14. Juni 2016 13:55
- Als Antwort markiert monsee Dienstag, 14. Juni 2016 14:04
Alle Antworten
-
Hallo Monsee,
Du kannst Deine Exports Klasse genauso im DoWork verwenden, wie Du es bisher außerhalb getan hast.Setze sie dort ein wo im BackgroundWorker Beispiel das Pausieren (Thread.Sleep) als Platzhalter steht.
Einzige Bedingung für die Verwendung: Du darfst innerhalb von DoWork nicht auf GUI (WPF) Elemente zugreifen. Außerdem sollte das GUI keine Veränderungen an den Daten vornehmen, die Du gerade verarbeitest - im Zweifelsfall eine Kopie anlegen.
Ob es dann fertig ist, kannst Du Dir über das RunWorkerCompleted Ereignis mitteilen lassen und dort z. B. den nächsten Timer starten.
Gruß Elmar
-
Hallo Elmar,
danke für den Tipp, ich habe den BackgroundWorker nun am Start, so, wie ich mir das in etwa vorgestellt habe.
Aber: Das nächste Problem, was mir ständig begegnet:
Ich schrieb ja schon, dass ich von der Klasse eine Instanz bilde:
Exports GPexport = new Exports(); GPexport.Dsn = "Der ConnectionString"; GPexport.GPEXPORT("C:\\Export.txt", false);
In der Klasse Exports habe ich einen Getter/Setter namens Summary.
Mit diesem fülle ich die TextBox auf meinem MainForm, zumindest klappt das in der nicht BackgroundWorker-Version:
Also ganz schlicht:
GPEXPORT() { string ausgabe = ""; while (rdr.read()) { for (int i = 0; i < rdr.FieldCount; i++) { ausgabe += rdr.GetValue(i).ToString(); } } Summary = ausgabe; }
Auf dem HauptForm mache ich also nur noch:
txtBox1.Text = GPexport.Summary;
Dieser Vorgang klappt aber nicht im BackgroundWorker RunWorkerCompleted,
wo ich einfach Summary = e.Result.ToString();
gesetzt habe.
Setze ich stattdessen ein Debug.Print(e.Result.ToString());
Erhalte ich die Ergebnisse aus dem SQL.Kannst Du mir einen Tipp geben, wie ich das noch bewerkstelligen kann?
Danke, monsee
-
die Methode BackgroundWorker.ReportProgress kann nicht nur einen int mitliefern sondern auch ein object, in das du alles stecken kannst so z.B. :
new object[] { "aktion_die_du_ausführen_möchtest", "andere werte die du für die gewünschte methode brauchst" };
nun musst du im Progress Changed event nur noch prüfen, was du machen wolltest:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { object[] param = (object[]) e.UserState;
if(param[0].ToString() == "aktion_die_du_ausführen_möchtest")
{
//param[1] ist nur ein beispiel falls du werte mit geben musst, kannst natürlich auch
//mehrere Werte mitgeben die du dann mit param[2], ... aufgreifen kannst
//Tipp: in objects kannst du alles speichern und wieder zu z.B. int zurück umwandeln
AuszuführendeMethode(param[1]);
} }
Ich hoffe, dass das dir einen hilfreichen Ansatz gibt.
Tipp: Nicht vergessen BackgroundWorker.WorkerReportsProgress auf true zu setzen.
My website: http://www.dragonskills99.16mb.com
- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 14. Juni 2016 13:58
- Als Antwort markiert monsee Dienstag, 14. Juni 2016 14:04
-
Hallo Monsee,
das einfachste dürfte sein, wenn Du die Export Klasse als Member Variable in einer Klasse führst. Dann zu Beginn die erforderlichen Parameter setzt und dann RunWorkerAsync startest. Damit hast Du sowohl in DoWork wie in den anderen Ereignissen Zugriff darauf.
public class ExportBackground { private BackgroundWorker backgroundWorker = new BackgroundWorker(); private Exports GPexport = new Exports(); public BackgroundWorkerForm() { backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += ExportBackground_DoWork; backgroundWorker.RunWorkerCompleted += ExportBackground_Completed; } public void Run() { // je nach Geschmack auch ber Parameter oder Eigenschaften der Klasse GPexport.Dsn = "Der ConnectionString"; GPexport.GPEXPORT("C:\\Export.txt", false); // Starten backgroundWorker.RunWorkerAsync(); } private void ExportBackground_DoWork(object sender, DoWorkEventArgs e) { // Verarbeitungsschleife (sollte Methode in GPExport sein) // Ein Ergebnis zurückgeben e.Result = GPexport.Summary; } private void ExportBackground_Completed(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { // Fehlerbehandlung bzw. Abbruch, hier nicht implementiert return; } txtBox1.Text = e.Result as String; } }
Wobei man bei der Übergabe der Parameter mehrere Möglichkeiten hat: Als Parameter für die Run Methode, oder als separate Eigenschaften, die dann Export befüllen - ggf. kann man aus der Export Klasse auch eine Eigenschaft machen.
Gruß Elmar
- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 14. Juni 2016 13:55
- Als Antwort markiert monsee Dienstag, 14. Juni 2016 14:04