Änderungen nach DataAdapter-Update sind nicht in der Datenbank gespeichert

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von matthiasH.

    Änderungen nach DataAdapter-Update sind nicht in der Datenbank gespeichert

    Hallo Programmierer-Kollegen,

    ich habe eine Anwendung, bei der Projekte verwaltet werden.
    Bei der Auswahl eines Projektes werden alle notwendigen Tabellen-Datensätze für dieses Projekt ins Dataset geladen.(per DataAdapter, komplett Standard).
    Am Ende der Bearbeitung werden die Daten wieder in die Datenbank zurück geschrieben (DataAdapter.Update).

    Folgende Konstellation bringt mich an den Rand meines Wissens und in den Wahnsinn:
    • Ein User fügt dem Projekt einen neuen sog. Plantermin ein und speichert.
      Der Plantermin ist sauber in der Datenbank vorhanden.
    • Dann wird eine Rechnung aufgerufen.
      Dort werden alle noch nicht abgerechneten Plantermine des Projektes aufgelistet und können dann per Checkbox der Rechnung zugefügt werden.
    • Der User checked zwei Plantermine: einen, der bereits vorher in der DB vorhanden war und den "neuen" Plantermin.
    • Dann wird erneut gespeichert:
      In der Tabelle im Dataset sind beide Zeilen geändert (d.h. Checkbox und ein Change-Datum, das ich setzte).
      Beide Zeilen sind auch in bei DataRowState.Modified enthalten und laufen in den DA-Update, der auch keinen Fehler verursacht.
    • In der Datenbank sind die Änderungn des „alten“ Plantermins vorhanden, der neue Plantermin steht aber unverändert in der Datenbank !!!!
    Ich habe keine Idee, an was das liegt bzw. wo ich nach dem Fehler suchen soll.
    Geschweige denn, was ich tun soll, um dieses Verhalten zu korrigieren …

    Hat mir irgend jemand eine schicke Idee oder Tipps … Ich poste auch gerne Code, weiss nur nicht genau, was - also welchen Teil - genau ich posten soll…
    Bin gespannt... und bedanke mich schon mal für Eure Mühen...!

    Beste Grüße aus dem Süden der Republik,
    Matthias
    Naja, richtiges Abspeichern ist eine Wissenschaft für sich.
    Die Reihenfolge der abzuspeichernden Tabellen ist entscheidend, und ebenso, ob die Db Löschweitergabe eingerichtet hat - dann muss entsprechend listig auch mit den DataAdaptern umgesprungen werden.
    Kleine Lösung: Dataset->Db
    Grosse Lösung: allgemeine Zugriffs-Lösung für: MySql, Access, SqlCe, SqlServer, DatasetOnly
    Eventuell könnte Dir auch das RowUpdating oder RowUpdated Event weiterhelfen - siehe:

    stackoverflow.com/questions/29…of-sqldataadapter-c-sharp

    Wenn das Event aber für Deine entsprechende Daten-Row nicht aufgerufen wird, dann liegt das Problem woanders - dann würde ich mir den Status der DataRow nochmals genauer ansehen, auch wenn Du schreibst, dass der Status "DataRowState.Modified" ist.

    Irgendwo ein "table.AcceptChanges()" zuviel oder zu wenig gesetzt, dies wäre auch noch eine Möglichkeit.
    Hallo Ihr beiden,

    vielen Dank für die Rückmeldung.

    ErfinderDesRades, Deine Posts sind interessant. Hatte auch mal vor, so etwas "Allgemeines" zu basteln, bin aber irgendwie an der Reihenfolge der Tabellen gescheitert. Also daran, dass die Tabellen dann in der richtigen Reihenfolge geupdated werden.

    Aber zurück zu meinem Problem:
    • EndEdit mache ich, so dass es daran nicht liegen sollte. Habe auch nochmals ausprobiert, die Checkboxen in anderer Reihefolge zu checken... Gleiches Ergebnis.
    • Das RowUpdate-Event wird auch für die neue Zeile aufgerufen.
    • Am Ende der ganzen Update-Prozedur mache ich ein AcceptChanges.
      Das habe ich mal weggelassen ! Dann bekomme ich auch beim neuen Datensatz tatsächlich auch die Änderung in die Datenbank.
      ABER: der neue Datensatz wird dann bei jeder Änderung erneut in die Datenbank geschrieben.
    Darin scheint das Problem zu liegen. Wenn ich AcceptChanges ausführe, dann sind alle weiteren Änderungen futsch.
    Wenn ich das nicht ausführe, dann ist der hinzugefügte Datensatz weiterhin als "Added" vorhanden und wird bei jedem Speichern wieder zurückgeschrieben.

    Ursprünglich hatte ich vorgesehen, dass der User gar nicht selber speichern kann, sondern dass nur gespeichert wird, wenn das Projekt wieder "entladen" wird.
    Dann habe ich mich entschlossen, dem User aber doch die Möglichkeit zum speichern zu geben. Es gibt auch ein paar Konstellationen, bei denen das besser ist, deshalb würde ich daran eigentlich nichts ändern wollen.

    Ist es vielleicht so, dass ich mir nach dem Database-Update dann einfach die Daten nochmals aus der Datenbank laden muss ?!
    Oder wie macht Ihr so etwas ?!

    Nochmals vielen Dank im Voraus! Das hat mich jetzt schon weiter gebracht....
    Grüße, Matthias
    wie Hans im Glück sagt: Von AcceptChanges() komplett die Finger weg lassen. AcceptChanges wird normalerweise von den DataAdaptern übernommen - k.A., ob du die iwie ungeschickt konfiguriert hast, oder ob du immer erst Kopien deiner Tabellen ziehst oder whatever.
    Jdfs freut mich, dass dir das Tabellen-Reihenfolgen-Problem bekannt ist - kennt glaub nicht jeder. Und ich habs halt gelöst, mittels sog. "topologischer Sortierung".

    matthiasH schrieb:

    Wenn ich das nicht ausführe, dann ist der hinzugefügte Datensatz weiterhin als "Added" vorhanden und wird bei jedem Speichern wieder zurückgeschrieben.


    Wegen einem ähnlichem Problem habe ich in einem Programm - ca. vor einem Jahr - dann die Daten nach dem Wegschreiben erneut eingelesen - danach funktionierten weitere Änderungen dann problemlos.

    Dies ist natürlich nicht unbedingt eine "saubere" Lösung, da ja eigentlich dadurch zuviel und an sich unnötiger Datenverkehr entsteht.
    Hallo zusammen,

    ich hab's !!!

    Dass der hinzugefügte Datensatz nach der erneuten Änderung nicht in die Datenbank zurück geschrieben wurde lag daran, dass der Update-Command nicht geriffen hat... und ein Concurreny-Problem produziert hat, das ich nicht mitbekommen habe, weil der DA auf ContinueUpdateOnError eingestellt war.... Peinlich !!!

    Dann habe mir beim RowUpdated-Event mal den Update-Command angeschaut und gesehen, dass der richtig wild aussieht...
    Dann habe ich mich mal näher mit dem CommandBuilder beschäftigt und gesehen, dass ich durch die "ConflictOption" eingreifen kann. Die sind jetzt auf "OverwriteChanges" eingestellt, da ich beim Aufruf eines Projektes durch ein Flag das Projekt sperre, so dass es niemand anderes aufrufen kann.
    Dann funktioniert auch das Update des neu hinzugefügten Datensatzes....

    Was ich aber weiterhin nicht verstehe: Nach dem DA-Update stehen die Datensätze weiterhin auf "Added" oder "Modified".... nur wenn ich das "AcceptChanges" selber mache, ändert sich der Zustand.... Das sollte direkt nach dem DA-Update (natürlich erst ganz zum Schluss, wenn das Delete durch ist) ja auch kein Problem sein...
    Aber kann mir jemand sagen, an was das liegen kann ? (AcceptChangesOnUpdate ist gesetzt)

    Nochmals danke an alle... In der letzten E-Mail habe ich nur den ersten beiden Antwortern gedankt...!
    Beste Grüße
    Matthias
    Hallo ErfinderDesRades,

    kein Problem....

    Hier richte ich den DA ein (es gibt natürlich noch mehr Projekt-Tabellen, die werden aber analog eingerichtet:

    VB.NET-Quellcode

    1. oDA_PP = New OleDb.OleDbDataAdapter("SELECT * FROM tbl_plannedPeriods WHERE pp_projectID = @pj_ID", oDBConn)
    2. With oDA_PP
    3. .SelectCommand.Parameters.Add("@pj_ID", OleDbType.Integer)
    4. End With
    5. With oDS.Tables(tbl_plannedPeriods).Columns("pp_ID")
    6. .AutoIncrementSeed = -1
    7. .AutoIncrementStep = -1
    8. End With
    9. oCB_PP = New OleDbCommandBuilder(oDA_PP)
    10. With oCB_PP
    11. .SetAllValues = False
    12. .ConflictOption = ConflictOption.OverwriteChanges
    13. End With


    Und hier ist eine Unterroutine, die pro Tabelle aufgerufen wird und Added und Modified updated.
    (ReplaceTempID ersetzt den temporär vergebene PrimaryKey durch den "richtigen" in der DB. Oft sieht man, dass beim RowUpdate abgefragt wird, ob es Insert ist und nur dann wird der Code durchlaufen. Ich fand es irgendwie schicker. Das RowUpdated-Event nur für das Update der Added Rows zu setzten...)

    VB.NET-Quellcode

    1. Public Sub doUpdate(myDataTable As DataTable, myDataAdapter As OleDbDataAdapter, shortCut As String, tableName As String)
    2. Dim updateDate As Date = Now
    3. If Not IsNothing(myDataTable.GetChanges(DataRowState.Added)) Then
    4. For Each oRow As DataRow In myDataTable.GetChanges(DataRowState.Added).Rows
    5. With myDataTable.Select(shortCut & "_ID=" & oRow.Item(shortCut & "_ID"))(0)
    6. .Item(shortCut & "_locked") = ""
    7. .Item(shortCut & "_lastChange") = updateDate
    8. .Item(shortCut & "_lastChanger") = sUser
    9. End With
    10. Next
    11. AddHandler myDataAdapter.RowUpdated, AddressOf replaceTempID
    12. myDataAdapter.Update(myDataTable.GetChanges(DataRowState.Added))
    13. RemoveHandler myDataAdapter.RowUpdated, AddressOf replaceTempID
    14. End If
    15. If Not IsNothing(myDataTable.GetChanges(DataRowState.Modified)) Then
    16. For Each oRow As DataRow In myDataTable.GetChanges(DataRowState.Modified).Rows
    17. With myDataTable.Select(shortCut & "_ID=" & oRow.Item(shortCut & "_ID"))(0)
    18. .Item(shortCut & "_locked") = ""
    19. .Item(shortCut & "_lastChange") = updateDate
    20. .Item(shortCut & "_lastChanger") = sUser
    21. .EndEdit()
    22. End With
    23. Next
    24. myDataAdapter.Update(myDataTable.GetChanges(DataRowState.Modified))
    25. End Sub

    Sehe nicht den Fehler, aber so einiges kommt mir sehr krude vor.
    Am meisten dieses:

    VB.NET-Quellcode

    1. With myDataTable.Select(shortCut & "_ID=" & oRow.Item(shortCut & "_ID"))(0)
    Was bezweckst du damit?
    Mir scheint, da selektierst du diejenige DataRow, die denselben shortCutID hat wie oRow. Ich vermute fast, da wird nix anneres bei rauskommen als oRow selbst - oder?
    Hallo ErfinderDesRades,

    danke für die Rückmeldung. Würde mich schon interessieren, was Du anders machen würdest....

    Zu dem von Dir angesprochenen Konstrukt... Da gebe ich Dir recht, ist echt krumm.
    Das war aber total komisch. Ich habe das auch erst so gehabt, wie Du geschrieben hast.
    Dann war es aber so, dass die Änderungen der 3 Felder nicht durchgeführt wurden.... Dann war eben die Idee, den Datensatz nochmals explizit in der Tabelle zu selektieren und direkt in der Tabelle zu ändern.
    Ich werde das nochmals ausprobieren....

    Und wie gesagt, wenn Du sonst noch ein paar Tips übrig hast. Bin gespannt...
    zunächstmal DataTable.Getchanges ist Mist. Das ergibt immer eine neue DataTable, wenn du die updatest, ist die neue geupdated, die alte aber nicht.
    Update die Rows der DataTable, die du hast, keine anderen:

    VB.NET-Quellcode

    1. Public Sub doUpdate(tb As DataTable, adp As OleDbDataAdapter, shortCut As String)
    2. Dim updateDate As Date = Date.Now
    3. Dim rowsToUpdate = tb.Select("", "", DataViewRowState.Added)
    4. If rowsToUpdate.Length > 0 Then
    5. For Each rw In rowsToUpdate
    6. rw(shortCut & "_locked") = ""
    7. rw(shortCut & "_lastChange") = updateDate
    8. rw(shortCut & "_lastChanger") = sUser
    9. Next
    10. AddHandler adp.RowUpdated, AddressOf replaceTempID
    11. adp.Update(rowsToUpdate)
    12. RemoveHandler adp.RowUpdated, AddressOf replaceTempID
    13. End If
    14. rowsToUpdate = tb.Select("", "", DataViewRowState.ModifiedCurrent)
    15. If rowsToUpdate.Length > 0 Then
    16. For Each rw In rowsToUpdate
    17. rw(shortCut & "_locked") = ""
    18. rw(shortCut & "_lastChange") = updateDate
    19. rw(shortCut & "_lastChanger") = sUser
    20. Next
    21. adp.Update(rowsToUpdate)
    22. End If
    23. End Sub

    Dann würde ich sowieso eine Routine bauen, die einfach alles updated - alle Tabellen, alle Rowstates.
    Kann ich hier am Beispiel nicht vorführen, weil deine Routine trägt ja auch Daten ein, das ginge für alle Tabellen natürlich nur, wenn alle Tabellen auch diese Spalten haben
    Hallo ErfinderDesRades,

    danke für die Rückmeldung. Das ist echt interessant...! Ich bringe mir das alles selber bei und es gibt so viele Quellen im Internet...
    Da ist viel sehr hilfreiche Information dabei, manchmal verwirrt es aber auch.

    Ich habe das mal umgebaut und jetzt klappt das auch mit dem automatischen AcceptChanges bei dem Update !!

    Ein Projekt hat 5 Tabellen. Ich rufe erst die doUpdate.Routine für alle Tabellen auf (hierarchisch von oben nach unten) und dann kommt die delete-Routine, die entsprechend in der anderen Reihenfolge aufgerufen wird... Die doUpdate-Routine wird aber schon von einem zentralen Update-Unterprogramm aufgerufen...

    Also nochmals vielen herzlichen Dank !!
    Und noch ein schönes WE !
    Gruss, Matthias