Schleife müllt Arbeitsspeicher zu

  • VB.NET

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von Cell.

    Schleife müllt Arbeitsspeicher zu

    Edit by ErfinderDesRades: (Thema verschoben) Bitte richtiges UnterForum wählen!

    Hallo zusammen,

    ich habe mal wieder ein Problem. Folgende Schleife müllt mir nach und nach den Arbeitsspeicher zu und ich kann nicht erkennen wo. Nachdem das Programm dann ca 600000 Datensätze durchgeackert hat bekomme ich dann eine Out of Memory Ausnahme (klar irgendwann ist der Speicher voll)

    Hier die Schleife:

    VB.NET-Quellcode

    1. Do
    2. 'Prüfen ob datensatz bereits existiert
    3. cmd.CommandText = "SELECT Count(*) FROM palletdata Where RecordNo = '" & DataGridView1.Rows(i).Cells(0).Value _
    4. & "' AND Time ='" & DataGridView1.Rows(i).Cells(1).Value & "'"
    5. cmd.Connection = conn
    6. Anzahl = cmd.ExecuteScalar
    7. 'Wenn Datensatz nicht existiert soll er in die Datenbank geschrieben werden
    8. If Anzahl = 0 Then
    9. cmd.CommandText = "INSERT INTO `bocsv8-1`.`palletdata` (`RecordNo`, `Time`, `JobID`, `StationNo`, `PalletNo`, `DeltaQuantityPos`, `DeltaQuantityNeg`, `WorkStep`, `ErrorCodePart1`, `ErrorCodePart2`, `ErrorCodePart3`, `LogisticsCode`, `LogisticsStatus`, `PartStatus1`, `PartStatus2`, `PartStatus3`, `PartStatus4`, `PartNumber`)" _
    10. & " VALUES (" _
    11. & "'" & DataGridView1.Rows(i).Cells(0).Value & "'," _
    12. & "'" & DataGridView1.Rows(i).Cells(1).Value & "'," _
    13. & "'" & DataGridView1.Rows(i).Cells(2).Value & "'," _
    14. & "'" & DataGridView1.Rows(i).Cells(3).Value & "'," _
    15. & "'" & DataGridView1.Rows(i).Cells(4).Value & "'," _
    16. & "'" & DataGridView1.Rows(i).Cells(5).Value & "'," _
    17. & "'" & DataGridView1.Rows(i).Cells(6).Value & "'," _
    18. & "'" & DataGridView1.Rows(i).Cells(7).Value & "'," _
    19. & "'" & DataGridView1.Rows(i).Cells(8).Value & "'," _
    20. & "'" & DataGridView1.Rows(i).Cells(9).Value & "'," _
    21. & "'" & DataGridView1.Rows(i).Cells(10).Value & "'," _
    22. & "'" & DataGridView1.Rows(i).Cells(11).Value & "'," _
    23. & "'" & DataGridView1.Rows(i).Cells(12).Value & "'," _
    24. & "'" & DataGridView1.Rows(i).Cells(13).Value & "'," _
    25. & "'" & DataGridView1.Rows(i).Cells(14).Value & "'," _
    26. & "'" & DataGridView1.Rows(i).Cells(15).Value & "'," _
    27. & "'" & DataGridView1.Rows(i).Cells(16).Value & "'," _
    28. & "'" & DataGridView1.Rows(i).Cells(17).Value & "')"
    29. cmd.ExecuteNonQuery()
    30. letzter = DataGridView1.Rows(i).Cells(0).Value
    31. End If
    32. ProgressBar1.Value = i
    33. Label1.Text = i & " / " & DataGridView1.Rows.Count
    34. i = i + 1
    35. 'Application.DoEvents()
    36. Loop Until i = DataGridView1.Rows.Count


    Was ich an der Geschichte nicht so ganz raffe ist, dass ich kein neues Objekt oder eine Instanz eines neuen Objekts erstelle sondern nur bestehende Objekte neu befülle. Also warum wächst der Speicher so an?

    Ich hoffe jemand von euch hat eine Idee

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Mal abgesehen davon, dass man Daten und Gui von einander Trennen sollte, (was mit deinem akuten Problem nichts zu tun hat), würde ich nun einfach mal den TaskManager öffnen, deinen Prozess suchen, die Schleife durchwatscheln, und schauen, wo speicher alloziiert wird. Ich sehe jetzt auf anhieb Nichts, was den RAM zumüllen würde. Vermutlich gibt es bessere Wege, oder übersehe ich etwas, aber das wäre nun das erste, das mir dazu einfällt / was ich tun würde.

    Edit: ebenso wäre eine richtige Benennung deiner Controls, von Vorteil, sollte das Programm mal größer werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „EaranMaleasi“ ()

    Danke schonmal für die schnelle Antwort

    Ich habe deinen Rat befolgt und die Schleife durchgestept unf gleichzeitig die Speicherbelastung im Auge behalten. Und nun das kuriose Ergebnis. Jedes mal wenn ich in die Variable Anzahl den Wert aus der DB zuweise wächst der Speicher um 8kb an. Aber warum?

    Hier zum besseren Verständnis nochmal die Zeile die ich meine

    VB.NET-Quellcode

    1. Anzahl = cmd.ExecuteScalar


    Ich habe den Code jetzt so abeändert das ich das Ergebnis nicht mehr in eine Variable schreibe sondern direkt auswerte. Beim Durchsteppen steigt der Speicher jetzt auch nicht mehr an. Aber wenn ich die Anwendung laufen lasse wird der Speicher noch immer zugemüllt.

    Weitere Ideen?
    Um das ganze mal etwas genauer zu beschreiben hier nochmal ein wenig mehr an Infos. Das Programm soll im endeffekt nichts anderes tun, als alle Einträge die in der Datenbank (Access Datei ) vorhanden sind durchzurattern und zu überprüfen, ob der Datensatz in der MySQL Datenbank vorhanden ist und wenn nicht den Datensatz eintragen.

    Folgendes habe ich bereits versucht:

    Grundprogram (das mit dem Fehler ) arbeitet mit folgenden Geschwindigkeiten (lesend ca. 1000 QPS / schreibend ca. 200 QPS)

    Speicheranstieg 8kB / Durchlauf

    Programm auf einem anderen Rechner gestartet:

    Grundprogram auf einem anderen Rechner (lesend ca. 1000 QPS /schreibend ca 200 QPS)

    Speicheranstieg 8kB / Durchlauf

    Habe das programm so verändert, das bei jedem Schleifendurchlauf der Cabbage Collector angestubst wird aufzuräumen

    VB.NET-Quellcode

    1. System.GC.Collect()


    Damit scheint das ursprüngliche Problem gelöst zu sein bzw der Arbeitsspeicher steigt nicht mehr.
    Allerdings ist das von der Geschwindigkeit alles andere als Toll

    (lesend / schreibend ca 30 QPS)



    kannst du vlt. ein klein Sample machen, was den Fehler reproduziert?
    kleine Datenbank, wo in einer Schleife immer nur die Anzahl einer doofen Tabelle abgefragt wird.


    sorry ich weis grade nicht so wirklich was du damit meinst... Ein Beispielprojekt mit einer kleinen Access Datei dazu? Kann ich gerne machen aber dazu wird auch der MySQL Server benötigt um das korrekt nachvollzieren zu können.
    Ähm Ignorieren ??

    Habe ich was verpasst?

    ich hatte doch geschrieben :


    sorry ich weis grade nicht so wirklich was du damit meinst... Ein Beispielprojekt mit einer kleinen Access Datei dazu? Kann ich gerne machen aber dazu wird auch der MySQL Server benötigt um das korrekt nachvollzieren zu können.


    Wenn du also das Projekt so haben willst kann ich das gerne machen und hochladen.
    Hmm mir gefällt das nicht sonderlich ... so wie ich das sehe räumt mir der Garbbage Collerctor den Speicher zwar wieder frei aber ist warscheinlich zu langsam. Oder nicht hoch genug frequentiert.

    Wenn ich ihn immer wieder anstubse den Speicher frei zu geben kann ich das Problem umgehen aber lösen tue ich das damit nicht.

    @ErfinderDesRades

    Ich habe allerdings noch eine Frage. Du hast geschrieben ein Menory Leak in ADO.NET. Ich bin sicherlich kein Datenbankexperte aber liegt da denn überhaupt das Problem? Der Speicher wird zugemüllt wenn ich die Antwort vom MySQL Server bekomme. Wäre dann nicht der MySQL Connector schuld?
    Ich kenne es eigentlich gar nicht anders, dass man bei größeren Datenmengen Probleme mit dem Speicher bekommt. Wobei sich meine Erfahrungen dabei eher auf Java stützen (ebenfalls mit MySQL), aber von der Problematik ist das mit dem GC ja ähnlich.

    Der GC ist halt nicht Perfekt und wenn der Speicher beim Aufruf bei dir freigegeben wird, würde ich nicht zögern, den alle paar tausend querys mal aufzurufen (vielleicht i Mod 20000 = 19999 für den Anfang?). Das sollte nicht so sehr in Gewicht schlagen.

    Wenn du dir sorgen um die Performance machst:
    Hast du die Sache schon mal mit Prepared Statements versucht? (Auch gegen ungewollte SQL-Syntaxfehler bei deinem CommandText.)
    Oft hilft auch eine Transaktion (die man allerdings auch alle paar tausend querys commiten muss). Falls du einen Index auf der Datei hast, kann es sich auch lohnen den vorher zu entfernen und nachher wieder anzulegen.

    Was ich an der Geschichte nicht so ganz raffe ist, dass ich kein neues Objekt oder eine Instanz eines neuen Objekts erstelle [...]
    Du erzeugst in jedem Schleifeindurchlauf gleich mehrere Objekte ; )

    Cell schrieb:

    Der Speicher wird zugemüllt wenn ich die Antwort vom MySQL Server bekomme. Wäre dann nicht der MySQL Connector schuld?
    Stimmt - das ist eiglich eher ein MySql-Fehler - nicht der Connector-Kram, sondern die MySqlData.dll.

    Imo sollte man dem wirklich nachgehen, weil obwohl ich kein MySql habe, liebe ich MySql, einfach weils M$ Monopol-Macht bisserl infrage stellt. Und das wär schon blöd, wenn ein popeliges .ExecuteScalar iwann outOfMemory geht.

    jdfs. zu GC.Collect habich gelesen, das sei ganz sinnlos, und würde nur die Effizienz des GC runtersetzen. Weil dessen Auto-Collection weiß besser, wann zu collecten ist.
    GC.Collect dürfte also keinesfalls das Problem lösen können, denn wenn ein Collect das Problem lösen kann, dann müsste ja das Auto-Collect des GC ebenso helfen (wie gesagt: effizienter).

    Oder habichs falsch verstanden: Deine Schleife endet doch iwann in einer OutOfMemory-Exception, odr?
    Imo sollte man dem wirklich nachgehen, weil obwohl ich kein MySql habe, liebe ich MySql, einfach weils M$ Monopol-Macht bisserl infrage stellt. Und das wär schon blöd, wenn ein popeliges .ExecuteScalar iwann outOfMemory geht.


    Stimmt vielleicht sollte man sich damit mal an Oracle wenden...

    jdfs. zu GC.Collect habich gelesen, das sei ganz sinnlos, und würde nur die Effizienz des GC runtersetzen. Weil dessen Auto-Collection weiß besser, wann zu collecten ist.
    GC.Collect dürfte also keinesfalls das Problem lösen können, denn wenn ein Collect das Problem lösen kann, dann müsste ja das Auto-Collect des GC ebenso helfen (wie gesagt: effizienter).


    Das mag grundsätzlich richtig sein aber trotzdem wird der Speicher nicht mehr zugemüllt wenn ich den GC bei jedem schleifendurchlaf sage er soll mal aufräumen. Vielleicht treffen deine Informationen nur bedingt zu. Vielleicht kann der GC aufgrund der Schleife nicht so Arbeiten wie er es vielleicht sonst tun würde? Daher muss man ihn dann anstubsen und ihn quasi in der Schleife die möglichkeit geben mal aufzuräumen.

    Es ist ja auch nicht so, dass der GC garnicht arbeitet. Wenn ich das Hochrechne mit den 8KB / Durchlauf müsste der Speicher bei 1000 QPS nach wenigen Minuten voll sein aber das ist nicht der Fall. Wie ich schon beschrieben habe, dauert es satte 600.000 Datensätze bis der Speicher voll ist.


    Oder habichs falsch verstanden: Deine Schleife endet doch iwann in einer OutOfMemory-Exception, odr?


    Stimmt genau.

    Ich habe heute beobachten können, dass zuerst die Geschwindigkeit nachlässt, und danach kommt irgendwann die MemoryOutOfRange Exception. Ich werde am Montag wenn ich wieder in der Firma bin alle paar Tausend durchläufe den GC anzustoßen. Mal sehen was dann passiert.

    Wenn du dir sorgen um die Performance machst:
    Hast du die Sache schon mal mit Prepared Statements versucht? (Auch gegen ungewollte SQL-Syntaxfehler bei deinem CommandText.)
    Oft hilft auch eine Transaktion (die man allerdings auch alle paar tausend querys commiten muss). Falls du einen Index auf der Datei hast, kann es sich auch lohnen den vorher zu entfernen und nachher wieder anzulegen.


    Das klingt interesannt. Das werde ich mir auf jeden Fall zu gemüte führen.

    Du erzeugst in jedem Schleifeindurchlauf gleich mehrere Objekte ; )


    Erklärst du mir das bitte auch? Ich dachte ich würde nur die Inhalte mit neuen Daten füttern

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Cell“ ()

    Dein Ansatz ist grauenhaft, aber in eine OutOfMemory darf das nicht laufen.
    Wie gesagt: ich kanns nicht reproduzieren, hier mein Versuch mit Access:

    VB.NET-Quellcode

    1. Dim cmd As New OleDbCommand
    2. Dim conn As OleDbConnection
    3. Private Function GetArticleCount(istart As Integer, infinite As Boolean) As Integer
    4. For i = istart To istart + 10
    5. cmd.CommandText = "SELECT Count(*) FROM Article Where ArticleName = 'Chai'"
    6. cmd.Connection = conn
    7. GetArticleCount = CInt(cmd.ExecuteScalar)
    8. cmd.CommandText = String.Concat("Insert into Category (CategoryID, CategoryName, Description) Values (", i, ",'CatName", i, "', 'CatDescript", i, "')")
    9. cmd.ExecuteNonQuery()
    10. Threading.Thread.Sleep(1)
    11. Next
    12. If Not infinite Then Exit Function
    13. For i = 0 To Integer.MaxValue
    14. cmd.CommandText = "SELECT Count(*) FROM Article Where ArticleName = 'Chai'"
    15. cmd.Connection = conn
    16. GetArticleCount = CInt(cmd.ExecuteScalar)
    17. Threading.Thread.Sleep(1)
    18. Next
    19. End Function
    zunächst mal wird immer abwechselnd Count abgefragt, und dann eine Zeile zugefügt. Bei Infinite wird annähernd unendlich oft die Anzahl abgefragt. Alles bleibt cool, bisserl Prozessorlast halt.

    zunächst mal wird immer abwechselnd Count abgefragt, und dann eine Zeile zugefügt. Das entspricht deinem Verhalten, wenn keine DGV-Entsprechung in deiner DB gefunden wird - das kann also nur eine endliche Anzahl von Zufügungen ergeben. Danach und bei Infinite halt endloses Count-Abfragen.

    Zum Testen habich 2 Test-MenüItems drangemacht: der erste mit Infinite=False zeigt das korrekte Funktionieren der Methode.
    Dateien

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Erklärst du mir das bitte auch? Ich dachte ich würde nur die Inhalte mit neuen Daten füttern
    Vorweg: Ich hab da auch nur gefährliches Habwissen, also ggf. noch mal selber recherchieren (z.B.: msdn.microsoft.com/en-us/library/hh156531.aspx):

    Ja, das stimmt. Aber aus theoretischer Sicht wird bei jeder Stringkonkatenation ein neuer String, also ein neues Objekt auf dem Heap, angelegt. Ebenso das konvertieren eines Integer in einen String (i & " / " & DataGridView1.Rows.Count).
    cmd.ExecuteScalar gibt ebenfalls ein (neues) Objekt zurück und intern passiert da sicher auch einiges.
    Der Compiler optimiert das im ersten Fall vielleicht direkt mit einem String.Concat o.ä. und im zweiten Fall auch irgendwie.
    Du überschreibst zwar immer wieder deine Variablen, das bedeutet aber nicht, dass die ursprünglichen Objekte (z.B. CommandText) freigegeben werden. Und so wird "neuer" Speicher auf dem Heap bei jedem Schleifendruchlauf belegt.

    Der GC läuft, wenn du es nicht mit gcConcurrent unterbunden hast, parallel in einem Thread. Und nur der GC kann meines Wissens nach Speicher wieder freigeben (oder?). Und da der GC (vereinfacht gesagt) auch dafür sorgt, dass es keine Lücken im Heap gibt, muss er beim aufräumen auch einiges umkopieren, was im Gegensatz zum anlegen von neuen Objekten meist relativ lange dauert. Daher kommt der GC bei dir wahrscheinlich nicht hinterher mit dem aufräumen, wenn du ihn nicht mal manuell ansteuerst. Zudem springt er auch nur und ein paar Bedingungen an. (@ErfinderDesRades: Bei dir könnte das Problem eventuell am Sleep liegen?)

    Es könnte auch helfen, wenn du mal etwas mit GCSettings.LatencyMode rumspielst.
    rumspielen
    wie gesagt, was ich gelesen habe, sagte: "Spiel nicht mittm GC rum, der ist bereits hochoptimiert".

    3daycliff schrieb:

    Du überschreibst zwar immer wieder deine Variablen, das bedeutet aber nicht, dass die ursprünglichen Objekte (z.B. CommandText) freigegeben werden. Und so wird "neuer" Speicher auf dem Heap bei jedem Schleifendruchlauf belegt.
    nein, genau dafür ist der GC da, das kanner, und das kanner gut (etwa die verfallenen CommandTexte aufräumen). Das machter vlt. nicht sofort, sondern hebt sich das für eine Weile auf, aber diese Verzögerung ist Bestandteil des fabelhaften GC-Algos.
    Der GC ist auch die Ursache, dass im Taskmanager .Net-Proggis immer so riesen-Speicher zu beanspruchen scheinen. In Wirklichkeit halb so wild, denn wenn der Speicher im System knapper wird, dann collected der GC auch häufiger, und also der im TM angezeigte Riesen-Verbrauch ist eiglich nur virtuell.
    Oder anners gesagt: Der TM kann den Speicherverbrauch von .Net-Proggis nicht realistisch anzeigen.
    "hochoptimiert"? Für was ist er optimiert? Für Serveranwendungen? Für Echtzeitanwendungen? Für Desktopanwendungen?
    Der GC ist im allgemeinen vielleicht gut, aber perfekt oder fabelhaft kann er nicht für jeden Fall sein ; )
    Und wenn hier ein manueller Aufruf eine OOM-Exception verhindert, muss ja irgendwas im Argen liegen.

    @Cell: Um der Sache (mit dem GC) auf den Grund zu gehen:
    msdn.microsoft.com/de-de/library/vstudio/ee851764.aspx
    msdn.microsoft.com/de-de/magazine/cc163528.aspx
    Ich bin wieder in der Firma und damit auch wieder am Testen.

    Offensichtlich hatte ErfinderDesRades recht was den GC angeht. Ich habe jetzt den GC einmal testweise alle 20.000 Schleifendurchläufe angesprochen und dann nochmal alle 5.000 Schleifendurchläufe. Dabei habe ich festgestellt, dass das ansprechen des GC so gut wie garnichts gebracht hat, denn der Speicher müllt sich nach wie vor bei beiden Varianten nahezu gleich zu.

    Nach ca 600.000 Datensätzen bekomme ich die entsprechende OoM Ausnahme.

    Nach Stundenlangem Suchen im Internet bin ich dann auf folgende Aussage gestoßen.


    Der Garbage Collector sorgt dafür, dass der Speicherplatz nicht mehr referenzierter Objekte freigegeben wird. Es gibt aber auch Objekte, die ihrerseits Referenzen auf externe Fremdressourcen halten. Dabei kann es sich zum Beispiel um Datenbankverbindungen oder geöffnete Dateien handeln. Solche Fremdressourcen werden vom Garbage Collector nicht verwaltet und konsequenterweise auch nicht freigegeben.


    Damit ist klar das es nichts bringt den GC aufzurufen, weil der garnicht zuständig ist. Einzige Möglichkeit die ich jetzt noch sehe (um nicht wirklich auf eine andere DB umsteigen zu müssen ) ist das komplette Objekt raus zu werfen. Dann würde die Zuständigkeit wieder dem GC zufallen und der würde den Speicher dann wieder freigeben.

    So viel zur Theorie. In der Praxis müsste ich dann aber die Datenbankverbindung jedes mal wieder neu aufbauen. Also mein Grundgedanke war das so umzuschreiben, dass nicht mehr als maximal 100.000 Datensätze / Verbindung verarbeitet werden. Bin mal auf eure Meinungen dazu gespannt.

    Dein Ansatz ist grauenhaft, aber in eine OutOfMemory darf das nicht laufen.
    Wie gesagt: ich kanns nicht reproduzieren, hier mein Versuch mit Access:


    Um dir eine Möglichkeit zum testen zu geben werde ich heute im laufe des Tages ( warscheinlich eher Nachmittags) eine MySQL Datenbank für dich bereit zu stellen auf die du zugreifen kannst. Die Zugangsdaten würde ich dann per PM verschicken.

    Du schreibst außerdem, mein Anstz ist grauenhaft. Warscheinlich hast du damit auch recht, aber der Code ist aus der Not heraus entstanden, weil ich im Internet leider nirgens eine Lösung finden konnte die mir erlaubt das DataSet Datensatz für Datensatz bzw Spaltenweise durchzugehen. Wenn du mit erklären kannst wie man es RICHTIG macht wäre ich sehr dankbar.
    ein Dataset hat weder Datensätze noch spalten.
    Es hat Tabellen - DataTables, die haben Datensätze (DataRows) und Spalten.
    Spaltenweises durchlaufen ist bei Datensätzen prinzipiell nicht vorgesehen, also auch nicht bei DataRows.

    Angenommen ein KundenBestellung-Datensatz: Da ist die eine Spalte String, die nächste eine Datum, dann vmtl. Decimal, und Int32 kommt auch vor.
    Diese ganzen grundverschiedenen Datentypen über einen Kamm scheren zu wollen - das wird wohl nix.

    Aber Datensatzweise durchlaufen ist das allereinfachste: zb mit ForEach

    Achso - grundsätzliches zu Ado.Net: Es verarbeitet die Daten DB-Unabhängig. Also im Client existiert ein typisiertes Dataset als DB-Cache, mit allen erforderlichen (zunächstmal leeren) typisierten DataTables.
    Zur Verarbeitung lädt man einen Haufen Daten in die DataTables, und dann kannman die DB auch schon wieder vergessen, weil alles weitere findet nu im typisierten Dataset statt.

    Zum Speichern muss man sich eine Methode proggen, die alle Änderungen in die DB zurückschreibt. Dazu DataAdapter verwenden (ebenso zur Befüllung), und die berücksichtigen auch die Änderungsverfolgung des Datasets, also es wird wirklich nur minimaler Traffic erzeugt.

    Ziel des ganzen: den Sql-Krampf aus den .Net-Sprachen heraushalten, und stattdessen Daten mit verwaltetem Code verarbeiten, insbesondere mit Databinding.

    Im Datenbank-Tutorial-Bereich habich so einige Tuts eingestellt, die wenigsten beschäftigen sich mit Datenbanken - weil die sind irrelevant und austauschbar.
    Wesentlich ist, den Feature-Reichtum typisierter Datasets für sich zu nutzen zu lernen: Filter, Sortieren, berechnete Spalten, Databinding, Linq2Dataset.

    Edit: Was erklär ich dir eiglich groß? Du hast ja nichtmal das Sample runnergeladen. :cursing:

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Also zuerst mal stimmt ich habe das Sample noch nicht runter geladen gehabt. Wenn es den Eindruck gemacht hat, das ich mich nicht oder nur halbherzig dafür interessiere tut es mir wirklich leid. Es ist leider so das wir bzw ich Montags in der Firma immer Besprechungen ohne Ende und ich nur in den Pausen dazwischen dazu komme etwas zu tun. Da ist das ganze dann wohl ein wenig unter gegangen.

    So nun zu deinem Post:


    ein Dataset hat weder Datensätze noch spalten.
    Es hat Tabellen - DataTables, die haben Datensätze (DataRows) und Spalten.
    Spaltenweises durchlaufen ist bei Datensätzen prinzipiell nicht vorgesehen, also auch nicht bei DataRows.

    Angenommen ein KundenBestellung-Datensatz: Da ist die eine Spalte String, die nächste eine Datum, dann vmtl. Decimal, und Int32 kommt auch vor.
    Diese ganzen grundverschiedenen Datentypen über einen Kamm scheren zu wollen - das wird wohl nix.


    OK das wusste ich bereits .... hab mich nur Saublöd ausgedrückt



    Aber Datensatzweise durchlaufen ist das allereinfachste: zb mit ForEach


    Genau das habe ich bisher noch nie gemacht bzw nirgendwo gesehen... Abgesehen von deinem Sample jetzt das ich mittlerweile Heruntergeladen habe. Vielen Dank dafür!


    Achso - grundsätzliches zu Ado.Net: Es verarbeitet die Daten DB-Unabhängig. Also im Client existiert ein typisiertes Dataset als DB-Cache, mit allen erforderlichen (zunächstmal leeren) typisierten DataTables.
    Zur Verarbeitung lädt man einen Haufen Daten in die DataTables, und dann kannman die DB auch schon wieder vergessen, weil alles weitere findet nu im typisierten Dataset statt.

    Zum Speichern muss man sich eine Methode proggen, die alle Änderungen in die DB zurückschreibt. Dazu DataAdapter verwenden (ebenso zur Befüllung), und die berücksichtigen auch die Änderungsverfolgung des Datasets, also es wird wirklich nur minimaler Traffic erzeugt.

    Ziel des ganzen: den Sql-Krampf aus den .Net-Sprachen heraushalten, und stattdessen Daten mit verwaltetem Code verarbeiten, insbesondere mit Databinding.


    Das habe ich bereits ebenfalls schon bei dem Datenbank in 10 Minuten Tut gesehen.


    Im Datenbank-Tutorial-Bereich habich so einige Tuts eingestellt, die wenigsten beschäftigen sich mit Datenbanken - weil die sind irrelevant und austauschbar.
    Wesentlich ist, den Feature-Reichtum typisierter Datasets für sich zu nutzen zu lernen: Filter, Sortieren, berechnete Spalten, Databinding, Linq2Dataset.


    Das werde ich mir bei der nächten Gelegenheit mal gründlich durchlesen.