Hallo,
ich bin gerade dabei von einer Firebird DB (db1) zu einer anderen Firebird DB (db2) zu kopieren, da ich neue Tabellen+Feldnamen brauche.
Nun hätte ich gerne den Progress des Kopiervorgangs angezeigt. Der Kopiervorgang an sich wird vom Backgroundworker abgearbeitet.
Ich reporte den Progress in meiner Kopier-Schleife:
foreach (System.Data.DataRow db_datarow in db_ds.Tables["Inter"].Rows)
{
db2_datarow["Nachname"] = db1_datarow["IName"];
i++;
backgroundWorker1.ReportProgress((i * 100) /(anzahl_db1_reihen));
}
Außerdem:
private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Nun zeigt meine Progressbar zwar etwas an, jedoch füllt sich diese innerhalb von 2-3sec obwohl der Kopiervorgang noch nicht abgeschlossen ist (dauert insgesamt momentan etwa 2-3min).
Ich weis momentan nicht so recht an was das liegen könnte. Vllt. hat von euch jemand eine Idee ?
LG
verwendetes Datenbanksystem: Firebird
Hast mal mit dem Debugger durchgesteppt, was überhaupt berechnet wird?
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hast mal mit dem Debugger durchgesteppt, was überhaupt berechnet wird?
Ja, wenn ich durchsteppe zählt er mein e.ProgressPercentage langsam hoch.
Vorallem ob anzahl_db1_reihen gesetzt ist.
Mein anzahl_db1_reihen ist gesetzt. Das habe ich bereits überprüft.
LG
Dann stimmt an der Gesagtlogik was nicht. Aber das sieht man nicht, weil Du hier nicht alles an Code zeigst, was hier relevant ist.
Könnte auch nen Zugriffsfehler durch die Threads sein. Aber kann man mit dem CodeSchnippsel nur raten.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Wie hast die limits deiner Progressbar gesetzt
bzw geht sie auch von 0- 100 und dein ProgressPercentage auch ?
Hallo,
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
progressBar1.Maximum = 100;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
Meine ProgressPercentage geht auch von 0-100
Mir fällt da gerade ein:
Nach der foreach-Schleife update ich die DB mit:
db2.Update(db2_ds, "Kontakte");
Ist es möglich, das die foreach-Schleife in der progressBar korrekt dargestellt wird, jedoch das Update jenes ist, welches soviel Zeit beansprucht ? Wie könnte ich dieses Updaten der DB in den progress mit einschließen ?
//Edit: habe gerade mal das update mit in die foreach schleife gepackt. So funktioniert es. Wollte zwar alles in einem Rutsch updaten, aber so habe ich wenigstens meine ProgressBar.
Wenn Du es genau und unperformant willst, Du musst Du das Update bei jedem Element abschicken.
Wie lang ein Gesamt-Update braucht kann man nie sagen. In so einem Fall schätzt man.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Zur Performance:
Solche clientseitigen Batch-Inserts würde ich immer über DataReader, Prepared Statements und Parametern lösen...
Bist du auf DataSets angewiesen?
Dataset ist gleichschnell wie eigene Statements, wenn man nicht jede Zeile speichert sondern Blöcke in der Größe von 1 - 500 MB.
Bei mir hat ein Datenimport von einigen Hundertausend Datensätze einzeln 36 h gedauert, in Blöcken von max. 500 MB nur 1 h.
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Wenn du das Ganze Performanter machen willst aber die Progress bar nicht verlieren willst kannst du auch einfach nach rowCount / 100 updaten (oder noch genauer pro balken in der Bar)
Wie viel das bringt hängt stark davon ab wieviele Zeilen du schreibst.
Hallo,
danke für die vielen Beiträge. Die Perfomance stört mich hier nicht so sehr. Das Programm wird vermutlich eh nur mal alle paar Jahre aus dem Datensalat gewühlt. Viel mehr stört mich gerade das beim canceln des backgroundworker trotzdem in die "erfolgreich erledigt" Bedingung gesprungen wird (Race).
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Vorgang abgebrochen!");
unlock_textfelder();
}
else
{
MessageBox.Show("Done!");
unlock_textfelder();
}
}
Die korrekte Meldung wäre mir jedoch schon wichtig. Gibt es da ein Patentrezept um soetwas zu vermeiden ?
LG
Leider kann man anhand des Schnipsels nicht sagen wo dein Problem liegt jedoch bezweifle ich eine Race-Condition da du ja entweder das event mit Canceld auf true oder false feuerst.
Kannst du die Stelle in deinem BackgroundWorker Posten an denen die events sind / an der du dafür sorgst das der worker sauber terminiert?
Naja, einfach mein Abort-Button:
private void iAbbrechenButton_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
In meiner foreach-Schleife ist noch folgendes:
if (backgroundWorker1.CancellationPending == false)
{
backgroundWorker1.ReportProgress((db2.Tables["Kontakte"].Rows.Count * 100) / (anzahl_db1_reihen));
}
else
{
break;
}
Laut MSDN sollte dein Verhalten nur auftreten wenn du das CancelAsync in der DoWork Methode aufrufst.
BackgroundWorker.CancelAsync()
Im notfall kannst du das ganze durch eine eigene Mechanik ersetzten auch wenn ich nicht weiß warum es zu deiner Race Condition kommt.
sowas wie:
while(this.cancelled || work finished)
{
//work
}
und im button dann cancelled setzen
und in der WorkerCompleted auf das eigen cancelled abfragen
In meiner DoWork steht nur folgendes:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
else
{
Copy_db2_to_db1();
}
}
Hallo,
das bringt so leider nix.
Weil die Do-Work Methode wird nur einmal aufgerufen.
Beim Aufruf das Cancellation-Pending natürlich noch false.
Anschließend rufst du die Methode auf wo du die Daten kopierst.
Und diese Methode hat keine Ahnung von den CancellationPending.
Du musst in dieser besagten Methode abfragen ob das CancellationPending true ist und dann den Vorgang gegebenfalls abbrechen.
Grüße
Michael
Wenn ich das richtig verstanden habe, soll ich in meine foreach-Schleife welche die Daten kopiert sowas packen:
if (backgroundWorker1.CancellationPending == false)
{
backgroundWorker1.ReportProgress((pro_ds.Tables["Kontakte"].Rows.Count * 100) / (anzahl_imos_reihen));
}
else
{
backgroundWorker1.CancelAsync(); // <-- Das hier
break;
}
Das funktioniert aber ebenfalls nicht. Es wird immernoch die Meldung "Done!" ausgegeben obwohl "Abbruch" erscheinen sollte.
Hi PreAlpha,
so kann das aber nicht funktionieren. Zuerst prüfst du das CancellationPending-Flag, das du aber erst mit CancelAsync() setzt, wenn es bereits gesetzt ist.
Der Vorgang soll vom Benutzer abgebrochen werden, deshalb mußt du auf ein Benutzer-Ereignis reagieren. Das CancellationPending-Property sollte also beim Klick auf die Schaltfläche gesetzt werden, und deine Routine sollte sich beenden, wenn das Flag gesetzt ist. Siehe auch das Beispiel in der Doku unter BackgroundWorker.
Christian
Weeks of programming can save you hours of planning
Hi,
es IST ein Benutzer-Ereignis, siehe meinen früheren post weiter oben:
Naja, einfach mein Abort-Button:
private void iAbbrechenButton_Click(object sender, EventArgs e) { backgroundWorker1.CancelAsync(); }
In meiner foreach-Schleife ist noch folgendes:
if (backgroundWorker1.CancellationPending == false) { backgroundWorker1.ReportProgress((db2.Tables["Kontakte"].Rows.Count * 100) / (anzahl_db1_reihen)); } else { break; }
Ich habe jetzt einfach eine eigene bool variable "cancelled" eingeführt welche ich beim drücken auf den Abort Button auf true setze und die Methode CancelAsync() aufrufe. In RunWorkerCompleted prüfe ich nun einfach ob cancelled true ist und werfe dann die richtige Meldung aus.
Komischerweise funktioniert das einwandfrei. Wundert mich nur einwenig, da CancelAsync() ja eigentlich e.Cancelled auf true setzen sollte.