Folgender Codeabschnitt mag auf den ersten Blick harmlos aussehen, ist in wirklichkeit aber mörderisch:
private void Do(DataTable table)
{
foreach (DataRow dr in table.Rows)
{
//dr[0] = ....
}
}
Warum ?
Weil die Rows Collection der DataTable ALLE Rows, unabhängig von Ihrem Zustand sprich RowState zurückgibt!
Wenn ich aber bei einer gelöschten Row (gelöscht heisst ja nur dass die Row ein spezielles Kennzeichen bekommt, sie wird noch lange nicht aus der DataTable entfernt) auf einen Wert zugreifen will (lesend oder schreibend) dann fliegt mir (zurecht) eine Exception um die Ohren!
Das heisst: bei Iterationen über eine ganze DataTable NIEMALS über die Rows-Collection gehen sondern eine der zwei folgenden Möglichkeiten wählen
a) eine DataView dazwischen schalten (Ja DataViews in iherer standardeinstellung filtern gelöschte Rows heraus, auch ein Grund dafür warum gelöschte Rows im Grid nicht mehr angezeigt werden, das Grid verwendet IMMER eine DataView
wenn ich wieder mal "zu faul" bin eine eigene DataView zu instantiieren dann verwendet ich
table.DefaultView
dabei sollte man aber sich sein, dass niemand einen RowFilter auf die DefaultView gesetzt hat (umkehrschluss, RowFilter setze ich immer nur bei explizit erzeugten DataViews ein)
oder der Andere Weg
foreach (DataRow r in table.Select())
{
}
Select gibt mir ebenfalls nur die gültigen Zeilen zurück und das gleich in einem Array!
Ergänzung:
Bei der Verwendung von DataRow.Delete gibt es noch ein paar spezielle Probleme
foreach(DataRowView drv in table.DefaultView)
{
drv.Row.Delete()
}
kann NICHT verwendet werden da sich durch den Delete-Befehl der Enumerator verändert => .Net verabschiedet sich mit einer Exception aus der Schleife
foreach(DataRow r in table.Rows)
kann aus den oben drfestellten Gründen ebenfalls nicht verwendet werden
bisher habe ich den Weg über die Sicht verwendet, aber die Rows zuerst in eine ArrayList kopiert und mit einem zweiten foreach über die ArrayList das Delete() aufgerufen. Umständlich aber funktioniert!
hier der neueste Lösungsansatz
DataRow[] rows = table.select()
foreach(DataRow r in rows)
{
r.Delete();
}
läuft intern ähnlich wie die zwischengeschaltete Arraylist, nur dass der Code wesentlich schöner, sauberer, typsicherer und weniger Fehleranfällig ist.
Der Befehl
DataRow[] rows = table.select()
bringt uns zwei Dinge:
- Select gibt uns nur gültige Rows (ev. bereits vorher gelöschte bekommen wir gar nicht mehr zu sehen, was ja erwünscht ist)
- die Ergebnismenge wird in ein Array umkopiert welches wunderbar als Enumerator für das foreach geeignet ist da es sich durch Delete() nicht ändert!