DataBinding die Xte, ID-Wert aktualisieren

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

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von Volker Bunge.

    DataBinding die Xte, ID-Wert aktualisieren

    Hallo zusammen,

    ich habe mir eine kleine Test-Access-ACCDB Datei erstellt. 2 Tabellen (Personen, Telefonnr). Beide haben ein Autowert ID-Feld als Long Integer.

    Personen_______Telefonnr
    ID_____________ID
    Vorname_______PersonenID (auch Zahl, Long Integer)
    Nachname_____Vorwahl
    ______________Durchwahl

    Dann habe ich diese Datenbank in mein Projekt eingebunden. Dann noch schnell eine Form mit 3 Buttons (Btn_Neue_Person, Btn_Speichern und Btn_Aktualisieren angelegt und den Anzeigetext geändert. Dann habe ich mir den DataSet-Designer geöffnet und eine Beziehung (Join) von Personen.ID nach Telefon.PersonenID gezogen, noch schnell den Punkt bei 'Sowohl Bezeichungs- als auch Fremdschlüsseleinschränkung aktiviert (Cascade, Cascade, None wurden ja automatisch eingestellt). Dann zurück in die Form und dort die Datetnquellen geöffnet. Als erstes dann die Personen-Tabelle als DGV auf die Form gezogen und dann die verknüpfte Tabelle 'Telefonnr' auch als DGV rüber gezogen. Weitere Einstellungen habe ich nicht mehr vorgenommen.

    Vor den ersten Start habe ich ein paar Testdaten in beide Tabellen eingetragen, damit ich zu mindestens das Join-Ergebnis sehen kann.

    Danach das ganze mit unten stehenden Code gestartet.

    Ergebnis
    1. Links in dem Personen-DGV stehen alle Datensätze vollständig drin. Auch die Telefonnr. für den ersten Datensatz werden angezeigt.
    2. Wechsel ich nun den Datensatz, dann ändert sich auch die Anzeige im Telefon-DGV
    3. Ändere ich nun im DGV den Vornamen bzw. Nachnamen und verlasse den Datensatz, werden diese Änderungen auch in der Access-Tabelle angezeigt (ggf. F5 drücken)
    4. Möchte ich nun einen neuen Datensatz eintragen, drücke ich Btn_Neue_Person und trage dort den Vornamen ein. Die ID ist -1.

    Soweit alles gut und schön.

    Möchte ich aber auch eine Telefonnr. eintragen, so kann ich dies zwar tun. Es wird auch PersonenID = -1 übernommen, aber wenn ich das ganze abspeichere, dann bekommt zwar Person.ID einen Autowert, dieser wird aber nicht in Telefonnr.PersonenID mit geändert. Dort steht in der Access-Datenbank immer noch -1.

    Jetzt die Frage:
    Wie komme ich direkt nach der Inputbox die ID aktualisiert zurück geliefert und kann somit auch die Telefonnr. eintragen. Denn mache ich die Eingabe einer Telefonnr. die schon einen Wert PersonenID>0 hat, so klappt der Rest einwandfrei.

    All die Beispiele, die ich bisher gesehen habe, fügen keinen neuen Datensatz hinzu in beiden Tabellen. Die meisten Beispiele sind nur für eine Tabelle.

    Über eine kurze Rückmeldung würde ich mich sehr freuen.

    Volker

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Data.Common
    2. Public Class Form1
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. ' Braucht man diese Zeilen?
    5. ' Wenn ja, dann muss man ggf. die Reihenfolge ändern: Erst die Haupttabellen und dann zum Schluss die Untertabellen
    6. 'TODO: Diese Codezeile lädt Daten in die Tabelle "TestDBDataSet.Personen". Sie können sie bei Bedarf verschieben oder entfernen.
    7. Me.PersonenTableAdapter.Fill(Me.TestDBDataSet.Personen)
    8. 'TODO: Diese Codezeile lädt Daten in die Tabelle "TestDBDataSet.Telefonnr". Sie können sie bei Bedarf verschieben oder entfernen.
    9. Me.TelefonnrTableAdapter.Fill(Me.TestDBDataSet.Telefonnr)
    10. PersonenDataGridView.AllowUserToAddRows = False
    11. ' TelefonnrDataGridView.AllowUserToAddRows = False
    12. End Sub
    13. Sub Speichern()
    14. ' Ist eine extra SUB, damit diese von jeder Stelle ggf. aufgerufen werden kann.
    15. Try
    16. ' Überprüft die Eingaben
    17. Me.Validate()
    18. ' Die Änderungen werden in die DataTable geschrieben
    19. Me.TelefonnrBindingSource.EndEdit()
    20. Me.PersonenBindingSource.EndEdit()
    21. ' und von dort aus in die Access-Datenbank
    22. Me.TableAdapterManager.UpdateAll(Me.TestDBDataSet)
    23. Catch ex As Exception
    24. ' Zeigt ggf. einen Fehler an
    25. MsgBox("Fehler beim speichern der Daten in die Access-Datenbank Fehler:" & vbCrLf & vbCrLf &
    26. ex.Message)
    27. End Try
    28. End Sub
    29. Private Sub PersonenDataGridView_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles PersonenDataGridView.CellEnter
    30. ' Damit bei einem Wechsel innerhalb des DataGridViews gleich die ggf. vorhandenen Änderungen gesichert werden,
    31. ' wird hier erst einmal der Wert der ersten Spalte (ID) ermittelt
    32. Dim value As Object = PersonenDataGridView.Item(0, e.RowIndex).Value
    33. ' Wenn es keinen Wert gibt,
    34. If value = Nothing Or value < 0 Then
    35. ' dann passiert hier nichts mehr
    36. Exit Sub
    37. Else
    38. ' andernfalls werden die Änderungen gesichert
    39. Call Speichern()
    40. End If
    41. End Sub
    42. Private Sub TelefonnrDataGridView_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles TelefonnrDataGridView.CellEnter
    43. ' Damit bei einem Wechsel innerhalb des DataGridViews gleich die ggf. vorhandenen Änderungen gesichert werden,
    44. ' wird hier erst einmal der Wert der ersten Spalte (ID) ermittelt
    45. Dim value As Object = TelefonnrDataGridView.Item(0, e.RowIndex).Value
    46. ' Wenn es keinen Wert gibt,
    47. If value = Nothing Or value < 0 Then
    48. ' auf die letzte Datenzeile gehen
    49. 'TelefonnrDataGridView.FirstDisplayedCell = TelefonnrDataGridView.Rows(TelefonnrDataGridView.RowCount - 1).Cells(0)
    50. 'Call Speichern()
    51. ' dann passiert hier nichts mehr
    52. Exit Sub
    53. Else
    54. ' andernfalls werden die Änderungen gesichert
    55. ' Call Speichern()
    56. End If
    57. End Sub
    58. Sub Aktualisieren()
    59. ' Hier soll eigentlich das passieren, was nicht klappt.
    60. ' Ich möchte nach der Eingabe eines Vornamens, dass die angezeigte ID sich auf den Wert ändert, des diese Zeile auch in der Datenbank hat.
    61. ' Aktuell steht dort immer -1 usw.
    62. End Sub
    63. Private Sub Btn_Speichern_Click(sender As Object, e As EventArgs) Handles Btn_Speichern.Click
    64. ' Ruft das Speichern der Daten in die Access-Datenbank auf
    65. Call Speichern()
    66. End Sub
    67. Private Sub Btn_Aktualisieren_Click(sender As Object, e As EventArgs) Handles Btn_Aktualisieren.Click
    68. Call Aktualisieren()
    69. End Sub
    70. Private Sub Btn_Neue_Person_Click(sender As Object, e As EventArgs) Handles Btn_Neue_Person.Click
    71. ' Für das Abfragen des Vornamens habe ich hier eine Variable deklariert,
    72. Dim NeuePerson As String
    73. ' die hier über eine Eingabebox ihren Wert bekommt.
    74. NeuePerson = InputBox("Welchen Vornamen hat die neue Person?", "", "Test")
    75. ' Gültigkeitsabfragen oder ähnliches spare ich mir hier erst einmal
    76. ' Jetzt wird die Eingabe in die Access-Datenbank eingetragen.
    77. TestDBDataSet.Personen.AddPersonenRow(NeuePerson, "")
    78. ' und die neue aktuelle letzte Zeile sowie die zweite Spalte (0 basiert) angesteuert
    79. PersonenDataGridView.CurrentCell = PersonenDataGridView(1, PersonenDataGridView.Rows.Count - 1)
    80. Try
    81. ' Die Änderungen werden jetzt gespeichert
    82. Call Speichern()
    83. ' und die ID soll hier eigentlich aktualisiert werden
    84. Call Aktualisieren()
    85. Catch ex As Exception
    86. MsgBox("Das Hinzufügen hat einen Fehler verursacht Fehler:" & vbCrLf & vbCrLf &
    87. ex.Message)
    88. End Try
    89. End Sub
    90. 'Private Sub Btn_Neue_Telefonnr_Click(sender As Object, e As EventArgs) Handles Btn_Neue_Telefonnr.Click
    91. ' ' Für das Abfragen des Vornamens habe ich hier eine Variable deklariert,
    92. ' Dim NeueTelefonnr As String
    93. ' ' die hier über eine Eingabebox ihren Wert bekommt.
    94. ' NeueTelefonnr = InputBox("Welche Vorwahl. hat die Person?", "", "12345")
    95. ' ' Gültigkeitsabfragen oder ähnliches spare ich mir hier erst einmal
    96. ' Dim PersonID As Int32
    97. ' PersonID = PersonenDataGridView.CurrentRow.Cells(1).Value
    98. ' ' Jetzt wird die Eingabe in die Access-Datenbank eingetragen.
    99. ' TestDBDataSet.Telefonnr.AddTelefonnrRow(PersonID, NeueTelefonnr, "", "")
    100. ' ' und die neue aktuelle letzte Zeile sowie die zweite Spalte (0 basiert) angesteuert
    101. ' TelefonnrDataGridView.CurrentCell = TelefonnrDataGridView(3, TelefonnrDataGridView.Rows.Count - 2)
    102. ' Try
    103. ' ' Die Änderungen werden jetzt gespeichert
    104. ' Call Speichern()
    105. ' ' und die ID soll hier eigentlich aktualisiert werden
    106. ' Call Aktualisieren()
    107. ' Catch ex As Exception
    108. ' MsgBox("Das Hinzufügen hat einen Fehler verursacht Fehler:" & vbCrLf & vbCrLf &
    109. ' ex.Message)
    110. ' End Try
    111. 'End Sub
    112. End Class

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

    Dein Fehler ist bekannt.

    ErfinderDesRades schrieb:

    Die TableAdapterManager.UpdateAll()-Methode funktioniert leider nur bei Datenbanken, die In-/Out-DbParameter unterstützen: SqlServer, MySql, SqLite,...
    Bei anderen Datenbanken schlägt Sie fehl: Access, SqlCe,...
    Der Fehlschlag betrifft "nur" neu eingefügte Datensätze, deren Primkey-Spalte autoIncrementiert. Denn solche Primkeys werden im Dataset nur provisorisch generiert, und beim Abspeichern muss von der DB abgerufen werden, welchen AutoIncrement-Wert diese als den Endgültigen generiert hat.

    Schau mal hier, das ist eine Lösung auch für Access. (Da ist auch das Zitat her)
    Hallo Haudruferzappeltnoch,

    vielen Dank für den Link. Habe mir das Ganze mal durchgelesen und doch noch viele Bahnhöfe gesehen. Werde mir heute Nachmittag mal die Zip-Dateien ansehen und mit denen ein wenig rumspielen.

    Aber schon erstaunlich, dass man für so einen kleinen Auftrag einen so riesigen Aufwand betreiben muss.

    Liegt das jetzt eigentlich an den Datenbanken oder doch er an VB.Net? Denn ohne einen eindeutigen PrimaryKey ist doch ein Programm kpl. Sinnlos. Klar kann man wahrscheinlich das Ganze umgehen, wenn man gleich XML (oder sonst was) nutzt. Aber viele arbeiten halt gerne mit Datenbanken. Da es ja das Problem auch für andere Datenbankformate gibt, ist es doch erstaunlich, dass es mit "Bordmitteln" nicht so einfach geht.

    Da mein zukünftiges Projekt doch mit x Tabellen daher kommt, will ich natürlich auch eine möglichst einfache Handhabung der Tabellennutzung haben.

    Wenn ich es mal getestet habe, dann melde ich mich noch einmal.

    @ErfinderDesRades: Auch an Dich schon einmal hier einen ganz großen Daumen hoch für Deine Mühe bzw. Arbeit.

    Ich hoffe mal, das es am Ende nur ein paar Zeilen sind, die mir die ganze Arbeit erledigen.

    Gruß
    Volker

    ...Jetzt die Frage:
    Wie komme ich direkt nach der Inputbox die ID aktualisiert zurück geliefert und kann somit auch die Telefonnr. eintragen....



    mit "SELECT @@Identity" kannst die denn Autowert ermitteln.

    hier ein Bsp.
    die Tabelle tbl_Student hat zwei felder
    StudentID = Autowert
    StudentName = Text


    VB.NET-Quellcode

    1. Private Sub Button13_Click(sender As System.Object, e As System.EventArgs) Handles Button13.Click
    2. Dim connect As String = "Provider=Microsoft.ACE.OLEDB.12.0;data source=E:\db2010.accdb"
    3. Using conn As New OleDbConnection(connect)
    4. Using cmd As New OleDb.OleDbCommand("INSERT INTO tbl_Student (StudentName) VALUES (@StudentName)", conn)
    5. cmd.Parameters.AddWithValue("@StudentName", txtName.Text)
    6. conn.Open()
    7. Try
    8. cmd.ExecuteNonQuery()
    9. Dim Newid As Integer
    10. cmd.CommandText = "SELECT @@Identity AS [id];"
    11. Newid = Convert.ToInt32(cmd.ExecuteScalar())
    12. 'txtID.Text = Newid
    13. Label1.Text = "Datensatz angelegt mit ID = " & Newid ' <-- dein Autowert
    14. Catch ex As Exception
    15. MessageBox.Show(ex.ToString())
    16. End Try
    17. End Using
    18. End Using
    19. End Sub

    Hallo Kasi, Hallo Haudruferzappeltnoch,

    @Haudruferzappeltnoch: Das ist schon erstaunlich, dass man so eine wichtige Sache vergisst und dann in all den Jahren nicht schafft, dieses Fehler irgendwie zu beheben.

    @Kasi: Auch Dir vielen Dank für Deine Antwort. Sie funktioniert super. Jetzt möchte ich aber noch, dass der neue Datensatz auch in meinem DGV (bzw. später in den Eingabefeldern) richtig angezeigt wird. Wie kann ich also den Inhalt aktualisieren?

    Vielen Dank

    Volker
    Hallo VB1963,

    vielen Dank für den Link. Der hat mich ein Stück nach vorne gebracht.

    Ich habe nun folgenden Code in meiner Form

    Spoiler anzeigen

    VB.NET-Quellcode

    1. ' **************************************************************************
    2. ' GANZ Wichtig: Auch im DataSet-Deisgner muss pro Tabellenadapter eine Ergänzung vorgenommen werden.
    3. ' Ohne diese Ergänzung wird der ID-Wert nicht aktualisiert
    4. ' Quelle: https://www.vb-paradise.de/index.php/Thread/40718-Auslesen-des-neuen-Wertes-der-Schl%C3%BCsselspalte-nach-dem-Erstellen-eines-neuen-Dat/
    5. ' https://www.vb-paradise.de/index.php/Thread/138330-DataBinding-die-Xte-ID-Wert-aktualisieren/#post1192950
    6. ' **************************************************************************
    7. Public Class Form1
    8. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    9. ' Wenn ja, dann muss man ggf. die Reihenfolge ändern: Erst die Haupttabellen und dann zum Schluss die Untertabellen
    10. 'TODO: Diese Codezeile lädt Daten in die Tabelle "TestDBDataSet.Personen". Sie können sie bei Bedarf verschieben oder entfernen.
    11. Me.PersonenTableAdapter.Fill(Me.TestDBDataSet.Personen)
    12. 'TODO: Diese Codezeile lädt Daten in die Tabelle "TestDBDataSet.Telefonnr". Sie können sie bei Bedarf verschieben oder entfernen.
    13. Me.TelefonnrTableAdapter.Fill(Me.TestDBDataSet.Telefonnr)
    14. End Sub
    15. Private Sub PersonenDataGridView_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles PersonenDataGridView.CellEnter
    16. ' Die Änderungen werden direkt abgespeichert
    17. Call DataGridView_Zelle_verlassen(PersonenDataGridView)
    18. End Sub
    19. Private Sub PersonenDataGridView_Leave(sender As Object, e As EventArgs) Handles PersonenDataGridView.Leave
    20. ' Die Änderungen werden direkt abgespeichert
    21. Call DataGridView_Zelle_verlassen(PersonenDataGridView)
    22. End Sub
    23. Private Sub TelefonnrDataGridView_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles TelefonnrDataGridView.CellEnter
    24. ' Die Änderungen werden direkt abgespeichert
    25. ' Wird benötigt, wenn man Bspw. nur einen Wert für den neuen Datensatz anlegt und ohne Enter zu drücken wo anders hingeht.
    26. Call DataGridView_Zelle_verlassen(TelefonnrDataGridView)
    27. End Sub
    28. Private Sub TelefonnrDataGridView_Leave(sender As Object, e As EventArgs) Handles TelefonnrDataGridView.Leave
    29. ' Die Änderungen werden direkt abgespeichert
    30. ' Wird benötigt, wenn man Bspw. nur einen Wert für den neuen Datensatz anlegt und ohne Enter zu drücken wo anders hingeht.
    31. Call DataGridView_Zelle_verlassen(TelefonnrDataGridView)
    32. End Sub
    33. Private Sub Btn_Speichern_Click(sender As Object, e As EventArgs) Handles Btn_Speichern.Click
    34. ' Ruft das Speichern der Daten in die Access-Datenbank auf
    35. Call Speichern()
    36. End Sub
    37. Sub Speichern()
    38. ' Ist eine extra SUB, damit diese von jeder Stelle ggf. aufgerufen werden kann.
    39. Try
    40. ' Überprüft die Eingaben
    41. Me.Validate()
    42. ' Die Änderungen werden in die DataTable geschrieben
    43. Me.TelefonnrBindingSource.EndEdit()
    44. Me.PersonenBindingSource.EndEdit()
    45. ' und von dort aus in die Access-Datenbank
    46. Me.TableAdapterManager.UpdateAll(Me.TestDBDataSet)
    47. Catch ex As Exception
    48. ' Zeigt ggf. einen Fehler an
    49. MsgBox("Fehler beim speichern der Daten in die Access-Datenbank Fehler:" & vbCrLf & vbCrLf &
    50. ex.Message)
    51. End Try
    52. End Sub
    53. Sub DataGridView_Zelle_verlassen(DGV As DataGridView)
    54. ' Wenn kein DataGridView übergeben wird,
    55. If IsNothing(DGV) Then
    56. ' dann hier gleich wieder raus
    57. Exit Sub
    58. End If
    59. ' Es wird geprüft, ob die aktuelle Zeile gleich der Zeile für Neueingaben ist
    60. If DGV.CurrentRow.Index = DGV.NewRowIndex Then
    61. ' wenn dass der Fall ist, dann passiert nichts
    62. Else
    63. ' andernfalls werden die Neueingaben gespeichert
    64. Call Speichern()
    65. End If
    66. End Sub
    67. End Class


    Anschließend habe ich noch meinen DataSet-Designer geöffnet (als die Ansicht, wo man die ganzen Tabellen und Beziehungen sieht). Dann habe ich einen Doppelklick auf den TableAdapter gemacht und folgendes Endergebnis erzeugt.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Data.OleDb
    2. Namespace TestDBDataSetTableAdapters
    3. ' Dieser Teil muss pro Tabellenadapter eingefügt werden
    4. Partial Public Class PersonenTableAdapter
    5. Private _IdentityCommand As New OleDbCommand("SELECT @@IDENTITY")
    6. Private Sub Adapter_RowUpdated(ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs) Handles _adapter.RowUpdated
    7. If e.StatementType = StatementType.Insert Then
    8. _IdentityCommand.Connection = e.Command.Connection
    9. _IdentityCommand.Transaction = e.Command.Transaction
    10. e.Row(e.Row.Table.PrimaryKey(0)) = _IdentityCommand.ExecuteScalar()
    11. End If
    12. End Sub
    13. End Class
    14. ' Ende des Teil für die oben stehende Tabelle
    15. ' Hier kommt auch gleich die nächste Tabelle
    16. ' Der größte Teil wiederholt sich.
    17. Partial Public Class TelefonnrTableAdapter
    18. Private _IdentityCommand As New OleDbCommand("SELECT @@IDENTITY")
    19. Private Sub Adapter_RowUpdated(ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs) Handles _adapter.RowUpdated
    20. If e.StatementType = StatementType.Insert Then
    21. _IdentityCommand.Connection = e.Command.Connection
    22. _IdentityCommand.Transaction = e.Command.Transaction
    23. e.Row(e.Row.Table.PrimaryKey(0)) = _IdentityCommand.ExecuteScalar()
    24. End If
    25. End Sub
    26. End Class
    27. End Namespace
    28. Partial Class TestDBDataSet
    29. End Class


    Ich habe meinen Code auch ein wenig kommentiert, damit ich später noch alles weiss.

    Die ersten Test sahen sehr vielversprechend aus.

    Jetzt ist halt noch eine Frage offen: Kann man den zweiten Teil eigentlich irgendwie kürzen und es für alle Tabellenadapter gelten machen? Weil bei zig Tabellen ist es ja immer das Gleiche.

    Gruß

    Volker