DataSet Änderungsverfolgung / wie neu angelegten Datensatz abgreifen?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    DataSet Änderungsverfolgung / wie neu angelegten Datensatz abgreifen?

    Hallo zusammen.

    Ich hab mir nen kleinen "DataLogger" gebaut. Hiermit betreibe ich eine Änderungsverfolgung in meinem Programm. Man kann also nachschauen, wer hat wann was an einem Datensatz gemacht.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class DataLogger
    2. 'TODO: auch neue Datensätze loggen mit "Erstanlage", dafür muss aber nach dem Speichern irgendwie die korrekte ID rausgefunden werden...
    3. Private Shared _BearbeitungsTyp As String
    4. Private Shared _OldValues As Dictionary(Of DataRow, Object())
    5. Private Shared _RwNew As DataLogRow
    6. Public Sub New(Typ As String, ParamArray Drs As DataRow())
    7. _BearbeitungsTyp = Typ
    8. _OldValues = New Dictionary(Of DataRow, Object())
    9. Drs.ForEach(Sub(dr) _OldValues.Add(dr, dr.ItemArray))
    10. End Sub
    11. ''' <summary>Änderungsverfolgung: Vergleicht die Werte aller DataRows in _OldValues mit ggf. geänderten Werten. Gibt es Änderungen, werden diese in der Tabelle
    12. ''' DataLog mit zusätzlichen Informationen abgespeichert</summary>
    13. Friend Sub CompareValuesAndStoreChanges()
    14. Dim DicNegativeRecordID As New Dictionary(Of DataLogRow, DataRow)
    15. For Each kvp In _OldValues
    16. Dim dr = kvp.Key
    17. Dim oldValues = kvp.Value
    18. Dim columns = dr.Table.Columns
    19. Dim recordID = If(dr.Table.Columns(0).ColumnName = "ID", CInt(dr.Item(0).ToString), 0)
    20. For iCol = 0 To columns.Count - 1
    21. Dbg($"DataLogger: {dr.Table.TableName} Alter Wert: {oldValues(iCol)}")
    22. Dbg($"DataLogger: {dr.Table.TableName} Neuer Wert: {dr.Item(iCol)}")
    23. If dr.Item(iCol).ToString <> oldValues(iCol).ToString Then
    24. _RwNew = Dts.DataLog.AddDataLogRow(dr.Table.TableName, User.UserName, Date.Now, recordID, columns(iCol).ColumnName, oldValues(iCol).ToString, dr.Item(iCol).ToString, _BearbeitungsTyp)
    25. If recordID < 0 Then DicNegativeRecordID.Add(_RwNew, dr)
    26. End If
    27. Next
    28. Next
    29. If DicNegativeRecordID.Count > 0 Then
    30. Dts.SaveDts() 'Speichern ist hier notwendig, damit negative ID's in positive aus der DB umgewandelt werden.
    31. For Each kvp In DicNegativeRecordID
    32. kvp.Key.DatensatzID = CInt(kvp.Value.Item(0).ToString)
    33. Next
    34. End If
    35. End Sub
    36. End Class


    Ein Beispielaufruf (beim Editieren) - in Zeile 8 wird der Logger initialisiert, in Zeile 9 (also nach dem Edit) wird mit alten Werten verglichen und ggf. angepasst:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary>Öffnet einen Dialog zum Bearbeiten des gewählten Datensatzes</summary>
    2. <Extension>
    3. Friend Sub ShowEditDialog(dgv As DataGridView, editdlg As Form)
    4. Dim bs = DirectCast(dgv.DataSource, BindingSource)
    5. Dim dr = DirectCast(bs.Current, DataRowView).Row
    6. Dim rLock As New RecordLock(bs.DataTable, CInt(dr.Item(0)))
    7. If Not rLock.LockRecord Then Return
    8. Dim logger As New DataLogger("Bearbeitet", dr)
    9. If bs.EditCurrent(editdlg) = DialogResult.OK Then logger.CompareValuesAndStoreChanges()
    10. rLock.UnlockRecord()
    11. End Sub


    Das funktioniert wunderbar für bereits vorhandene Datensätze, allerdings nicht für Neue.
    Das Problem ist, dass ich VOR der Neuanlage die neue DataRow nicht kenne - und danach weiß man nicht wirklich, welche die gerade erstellte ist...
    Die folgenden beiden Methoden nutze ich zur Neuanlage von Datensätzen (dahinter stecken Extensions von @ErfinderDesRades):

    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary>Öffnet einen Dialog zur Neuanlage eines Datensatzes</summary>
    2. <Extension>
    3. Public Function ShowCreateNewDialog(dgv As DataGridView, editDlg As Form) As Boolean
    4. Dim bs = DirectCast(dgv.DataSource, BindingSource)
    5. Try
    6. If bs.EditNew(editDlg) = DialogResult.OK Then Return True
    7. Catch ex As Exception
    8. msgEx(ex)
    9. bs.CancelEdit()
    10. Return False
    11. End Try
    12. Return False
    13. End Function
    14. ''' <summary>Öffnet einen Dialog zur Neuanlage eines Datensatzes</summary>
    15. <Extension>
    16. Public Function ShowCreateNewDialog(Of T As {Form, New})(BS As BindingSource) As Boolean
    17. Try
    18. If BS.EditNew(Of T) = DialogResult.OK Then Return True
    19. Catch Ex As Exception
    20. msgEx(Ex)
    21. BS.CancelEdit()
    22. Return False
    23. End Try
    24. Return False
    25. End Function


    Ich müsste hier nach Zeile 6 bzw. nach Zeile 19 die gerade erstellte DataRow abfangen. Kann mir jemand sagen, wie ich das geschickt anstelle?
    Hat die Bindingsource eine Eigenschaft, den zuletzt erstellen Datensatz zu finden?
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    Da Deine Rows sicherlich ne AutoIncrement-Spalte haben, dürfte die mit dem Maximalwert (bei tDS-only per default die mit dem Minimalwert, da es ja ins Negative geht) die Neue sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Da Deine Rows sicherlich ne AutoIncrement-Spalte haben

    ja, haben alle relevanten Tabellen bei mir. Wäre sicher auch ein Weg gewesen, ID.max rauszusuchen. Allerdings ist die ID zum direkt nach der Neuanlage negativ (Dts) und nach dem Speichern eine andere (Wert aus der DB wird übernommen)

    ErfinderDesRades schrieb:

    die neue sollte rw.Rowstate=DataRowstate.Added aufweisen

    Danke, auf die einfache Tatsache bin ich nicht gekommen... :S

    Hab's nu wie folgt gelöst:

    VB.NET-Quellcode

    1. ''' <summary>Öffnet einen Dialog zur Neuanlage eines Datensatzes</summary>
    2. <Extension>
    3. Public Function ShowCreateNewDialog(Of T As {Form, New})(BS As BindingSource) As Boolean
    4. Dim logger = New DataLogger("Erstanlage")
    5. Try
    6. If BS.EditNew(Of T) = DialogResult.OK Then
    7. Dim newRw = BS.DataTable.RowsX.FirstOrDefault(Function(x) x.RowState = DataRowState.Added)
    8. logger.LogNewDataRow(newRw)
    9. Return True
    10. End If
    11. Catch Ex As Exception
    12. msgEx(Ex)
    13. BS.CancelEdit()
    14. Return False
    15. End Try
    16. Return False
    17. End Function


    Und in der Logger-Klasse noch folgendes ergänzt:

    VB.NET-Quellcode

    1. ''' <summary>New-Methode ausschließlich zur Erstanlage von Datensätzen</summary>
    2. Public Sub New(Typ As String)
    3. _BearbeitungsTyp = Typ
    4. End Sub
    5. ''' <summary>Erzeugt einen Eintrag in der DataLog-Tabelle, wann und durch welchen Benutzer der Datensatz erstellt wurde</summary>
    6. Friend Sub LogNewDataRow(dr As DataRow)
    7. Dim DicNegativeRecordID As New Dictionary(Of DataLogRow, DataRow)
    8. Dim recordID = If(dr.Table.Columns(0).ColumnName = "ID", CInt(dr.Item(0).ToString), 0)
    9. Dim rwNew = Dts.DataLog.AddDataLogRow(dr.Table.TableName, User.UserName, Date.Now, recordID, "", "", "", _BearbeitungsTyp)
    10. If recordID < 0 Then
    11. Dts.SaveDts() 'Speichern, damit die ID aus der Datenbank übernommen wird
    12. rwNew.DatensatzID = CInt(dr.Item(0).ToString)
    13. Dts.SaveDts() 'Logger-Eintrag mit geänderter ID speichern
    14. End If
    15. End Sub
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup: