none
Master/Detail - Problem beim Einfügen RRS feed

  • Allgemeine Diskussion

  • Hallo zusammen, 

    ich bin VB-Anfängerin und habe ein Problem beim Einfügen eines Datensatzes. 

    Habe folgende Anwendung : eine master/ detail- Anwendung, die ich mit dem Designer erstellt habe. 

    (Die Master-Tabelle ist im Detail Ansicht dargestellt und die Detail-Tabelle ist in einem DatagridView zu sehen). Beide Tabelle werden über einen Bindingnavigator gesteuert.

    Das Problem:
    ich versuche ein Sub für das AddNew-Button des Bidingsnavigators zu schreiben das so aussieht: 
    Private Sub BindingNavigatorAddNewItem_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles BindingNavigatorAddNewItem.Click
     
            Dim stamZeile As DataRow
            Dim i As Integer
            Dim j As Integer
            stamZeile = DS.book.NewRow()
     
            If Integer.TryParse(txtEJahr.Text, i) And Integer.Parse(txtBank.Text, _
              j) Then
     
                With stamZeile
     
                    .Item("titel") = txtTitel.Text
                    .Item("sprache") = txtSprache.Text
                    .Item("ort") = txtOrt.Text
                    .Item("isbn") = txtISBN.Text
                    .Item("issn") = txtISSN.Text
                    .Item("szahl") = txtSeiten.Text
                    .Item("ejahr") = i
                    .Item("b_nr") = j
                    .Item("typ") = txtTyp.Text
                End With
     
     
                Me.bookTableAdapter.Insert(txtTitel.Text, txtSprache.Text, _
                  txtOrt.Text, txtISBN.Text, txtISSN.Text, txtSeiten.Text, i, j, _
                  txtTyp.Text) ' an dieser Stelle tritt eine Fehlermeldung auf
     
            End If
     
            DS.book.AcceptChanges()
            DS.book.Rows.Add(stamZeile)
            Me.bookTableAdapter.Update(Me.DS.book)
     
            Dim newID As Integer
            newID = CType(bookTableAdapter.ScalarQuery(), Integer)' hier fange ich 
            ' den                                     Primärschlüssel der 
            ' Master-Tabelle ab, der vom System generiert wurde.
     
            Dim comm As New OracleCommand
            Dim str As String
     
            comm.CommandType = CommandType.Text
     
     
     
            Dim PersV As New DataView
            PersV.Table = DS.author
            Dim persZeile As DataRowView
            PersV.RowStateFilter = DataViewRowState.Added
     
            For Each persZeile In PersV
                Select Case persZeile.Row.RowState
                    Case DataRowState.Added
                        With persZeile
                            txtMnr.Text = newID.ToString
                            str = "INSERT INTO author (STAM_NR, FUNR, NAME," & _
                              "VORNAME) VALUES(.Item('stam_nr'), .Item('funr')," & _
                              ".Item('name'), .Item('vorname'))"
                            comm.CommandText = str
                            comm.ExecuteNonQuery()
                        End With
                End Select
     
            Next
     
         End Sub
    leider verlangt Visual Studio und DB der Primärschlüssel der Master-Tabelle obwohl dieser als Auto-Inkrement Feld definiert wurde. 

    Vielen Dank für Eure Hilfe.

    SF

    Mittwoch, 23. Mai 2012 19:26

Alle Antworten

  • Hallo,

    also ich habe keine Erfahrung mit Oracle. Hab nur SQLServer Express 2008.  Einfach mal damit versucht das Beispiel zumindest teilweise nachzuvollziehen. Der folgende Code fügt die Datensätze korrekt der Tabelle hinzu und zeigt sie im DataGridView an. (Ich habe offen gestanden die SQLDataAdapter.Update Methode noch nicht ausprobiert sondern immer SQLCommands bevorzugt.)


    Public Class Form1
      Private mobjSqlConnection As System.Data.SqlClient.SqlConnection
      Private mstrSqlConnectionString As String = "der Connectionsstring..."

      Private bookTableAdapter As SqlClient.SqlDataAdapter
      Private DS As New DataSet
      Dim book As DataTable
      Private mobjSqlCommand As SqlClient.SqlCommand


      Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        mobjSqlConnection = New System.Data.SqlClient.SqlConnection(mstrSqlConnectionString)
        mobjSqlConnection.Open()

        mobjSqlCommand = mobjSqlConnection.CreateCommand()

        bookTableAdapter = New SqlClient.SqlDataAdapter("Select * From book", mobjSqlConnection)

        bookTableAdapter.Fill(DS, "book")
        book = DS.Tables("book")
        DataGridView1.DataSource = book
      End Sub


      Private Sub Button1_Click() Handles Button1.Click
        Dim i As Integer
        Dim j As Integer
        Dim newID As Integer
        Dim stamZeile As DataRow

        If Integer.TryParse(txtEJahr.Text, i) And Integer.TryParse(txtBank.Text, j) Then
          ' Update der Datenbank.
          mobjSqlCommand.CommandText = "Insert Into book(titel, isbn, ejahr, b_nr) " & _
                                        "values (" & _
                                        "'" & txtTitel.Text & "', " & _
                                        "'" & txtISBN.Text & "', " & _
                                        i & ", " & _
                                        j & ")"
          mobjSqlCommand.ExecuteNonQuery()

          ' Holen des durch Autoincrement erzeugten neuen ID.
          ' Wenn man davon ausgeht das die ISBN eindeutiger Schlüssel für einen Datensatz in books ist. Mit
          ' entsprechender Prüfung.
          ' Wenn nur eine einzige Anwendung die Datenbank updatet würde sich auch
          ' select max(id) from book eignen.
          mobjSqlCommand.CommandText = "Select ID from book where isbn = '" & txtISBN.Text & "'"
          newID = CInt(mobjSqlCommand.ExecuteScalar())

          ' Update von der book DataTable um nicht alles neu lesen zu müssen.
          stamZeile = book.NewRow()
          With stamZeile
            .Item("ID") = newID
            .Item("titel") = txtTitel.Text
            .Item("isbn") = txtISBN.Text
            .Item("ejahr") = i
            .Item("b_nr") = j
          End With
          book.Rows.Add(stamZeile)

          DataGridView1.Refresh()
        End If
      End Sub


      Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        If Me.mobjSqlCommand IsNot Nothing Then
          Me.mobjSqlCommand.Dispose()
          Me.mobjSqlCommand = Nothing
        End If

        If DS IsNot Nothing Then
          DS.Dispose()
          DS = Nothing
        End If

        If bookTableAdapter IsNot Nothing Then
          bookTableAdapter.Dispose()
          bookTableAdapter = Nothing
        End If

        If mobjSqlConnection IsNot Nothing Then
          mobjSqlConnection.Close()
          mobjSqlConnection = Nothing
        End If
      End Sub
    End Class


    • Bearbeitet Markus222 Donnerstag, 24. Mai 2012 18:07
    Donnerstag, 24. Mai 2012 18:01
  • Hallo SF,

    das war wohl doch eher eine Sache mit den Bindings. War daher mit meiner Antwort nicht zufrieden. Da ich mich mit den Bindings nicht auskenne, hab ich dann noch mal bei Michael Koflers Visual Basic 2008 nachgesehen.

    Code muss man nur für das Speichern in der Datenbank schreiben. Für den AddNewItem Button benötigt man beim Binding Navigator keinen Code!

    Man kann zum Speichern den Speichern Button des Binding Navigators verwenden. Falls der im Designer nicht da ist dann auf den BindingNavigator mit der rechten Maustaste clicken und Insert Standard Items auswählen. Die nicht benötigten mit der Delete Taste löschen.

    Im Beispiel war mir das zu unbequem und ich habe einfach das BindingNavigator1.ItemClicked Event genommen.
    (Hier müsste man wenn wichtig vielleicht prüfen ob die Daten tatsächlich geändert wurden.)

    Also die eigentlich wichtige Prozedur im Beispiel ist nur SaveToolStripButton_Click (oder BindingNavigator1_Item_Clicked). Wie gesagt, für SQL Server Express 2008 und Windows Forms. Aber vielleicht gehts so ja auch mit Oracle.


    Public Class Form1
      Private mobjSqlConnection As System.Data.SqlClient.SqlConnection
      Private mstrSqlConnectionString As String = "der Connectionsstring..."

      Private bookTableAdapter As SqlClient.SqlDataAdapter
      Private DS As New DataSet
      Dim book As DataTable
      Dim mobjBindingSource As BindingSource
      Dim mobjSqlCommandBuilder As SqlClient.SqlCommandBuilder


      Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        mobjSqlConnection = New System.Data.SqlClient.SqlConnection(mstrSqlConnectionString)
        mobjSqlConnection.Open()

        bookTableAdapter = New SqlClient.SqlDataAdapter("Select * From book", mobjSqlConnection)

        bookTableAdapter.Fill(DS, "book")
        book = DS.Tables("book")

        mobjBindingSource = New BindingSource
        mobjBindingSource.DataSource = book

        txtTitel.DataBindings.Add("Text", mobjBindingSource, "titel")
        txtISBN.DataBindings.Add("Text", mobjBindingSource, "isbn")
        txtEJahr.DataBindings.Add("Text", mobjBindingSource, "ejahr")
        txtBank.DataBindings.Add("Text", mobjBindingSource, "b_nr")

        mobjSqlCommandBuilder = New SqlClient.SqlCommandBuilder(bookTableAdapter)

        BindingNavigator1.BindingSource = mobjBindingSource
      End Sub


      Private Sub SaveToolStripButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveToolStripButton.Click
        Try
          mobjBindingSource.EndEdit()
          bookTableAdapter.Update(book)

        Catch ex As Exception
          MsgBox(ex.Message)
        End Try
      End Sub


      Private Sub BindingNavigator1_Item_Clicked(ByVal sender As Object, ByVal e As ToolStripItemClickedEventArgs) _
                  Handles BindingNavigator1.ItemClicked
        Try
          mobjBindingSource.EndEdit()
          bookTableAdapter.Update(book)

        Catch ex As Exception
          MsgBox(ex.Message)
        End Try
      End Sub


      Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        If mobjSqlCommandBuilder IsNot Nothing Then
          mobjSqlCommandBuilder.Dispose()
          mobjSqlCommandBuilder = Nothing
        End If

        If mobjBindingSource IsNot Nothing Then
          mobjBindingSource.Dispose()
          mobjBindingSource = Nothing
        End If

        If DS IsNot Nothing Then
          DS.Dispose()
          DS = Nothing
        End If

        If bookTableAdapter IsNot Nothing Then
          bookTableAdapter.Dispose()
          bookTableAdapter = Nothing
        End If

        If mobjSqlConnection IsNot Nothing Then
          mobjSqlConnection.Close()
          mobjSqlConnection = Nothing
        End If
      End Sub

    End Class
    Donnerstag, 24. Mai 2012 22:03
  • Hallo Markus vielen lieben Dank für deine Antwort und besonders die Mühe ich werde es probieren und  nachher berichten.

    bis dahin DANKE 


    SF

    Freitag, 25. Mai 2012 22:02
  • Hi Markus,

    da bin ich wieder.

    ich bin es leider noch nicht so mit deinem Beispiel nicht weitergekommen. Vielleicht liegt es daran, dass ich einiges nicht verastanden habe.

    ich habe 2 TableAdapter (book und Author) und ich kann es immer noch nicht die BookId (autoinkremente Spalte) in die Tabelle

    Author übernehmen (als Fremdschlüssel).

    Zu deinem 2. Beitrag erlaube ich mir ein paar Fragen:

    Hier ist nur von der Tab. book die Rede heißt, dass ich die Tabelle Author beim Einfügen nicht anfassen soll?

    Beide Sub für die Click-Event sind identisch ist es so gewollt? wenn ja wozu bräuchte ich die Funktion " BindingNavigator1_Item_Clicked" ?

    Ausserdem hatte ich eine Funktion für das Save-buttton des Binding Navigator, die so aussaht:

     

    Private Sub bookBindingNavigatorSaveItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSave.Click
    Me.validate()
    Me.bookBidingSource.EndEdit()
    Me.authorBindingSource.EndEdit()
    Me.TableAdapterManager.UpdateAll(Me.DS)
    end Sub

    Danke für deine Hilfe


    SF

    Mittwoch, 30. Mai 2012 08:20
  • Hallo SF,

    im ersten Beitrag schriebst du doch:

    Me.bookTableAdapter.Insert(txtTitel.Text, txtSprache.Text, _
                  txtOrt.Text, txtISBN.Text, txtISSN.Text, txtSeiten.Text, i, j, _
                  txtTyp.Text) ' an dieser Stelle tritt eine Fehlermeldung auf
     

    meine Versuche bezogen sich nur darauf. Ich dachte es geht dir um diese Fehlermeldung.

    Damit ich es nachvollziehen kann bräuchte ich den kompletten Code. Falls machbar.


    • Bearbeitet Markus222 Mittwoch, 30. Mai 2012 17:21
    Mittwoch, 30. Mai 2012 17:20
  • Ergänzung: Ein Klick im BindingNavigator auf AddNew Item positioniert auf einen neuen Datensatz.

    Der Benutzer kann nun Werte eingeben. Der Click darauf heisst nicht Speichern in der Datenbank!

    Das Speichern ist ein anderer Vorgang. Wie oben beschrieben.

    Eventuell ist es gut wenn du dich intensiv mit diesem Steuerelement und seinen Funktionen beschäftigst wenn
    du es wirklich verwenden möchtest.

    Ich würde so etwas niemals verwenden.

    Mittwoch, 30. Mai 2012 19:46
  • Hi Markus,

    hier ist der Code:

    Imports Oracle.DataAccess.Client
    Public Class Form1
        Private Sub BookBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click
            Me.Validate()
            Me.BookBindingSource.EndEdit()
            Me.AuthorBindingSource.EndEdit()
            Me.TableAdapterManager.UpdateAll(Me.DS)
    End Sub
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            lbl.Text = Date.Today
            lblZeit.Text = TimeOfDay
        End Sub
    Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoad.Click
           Me.AuthorTableAdapter.Fill(Me.DS.Author)
           Me.BookTableAdapter.Fill(Me.DS.Book)
        End Sub
    Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click
            Me.AuthorTableAdapter.Update(Me.DS.Author)
              Me.BookTableAdapter.Update(Me.DS.Book)
        End Sub
    Private Sub btnClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClear.Click
            DS.Clear()
        End Sub
    Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSearch.Click
            Try
                Me.BookTableAdapter.FillByNr(Me.DS.Book, txtBookID.Text)
                Me.AuthorTableAdapter.FillByNr(Me.DS.Author)
            Catch ex As System.Exception
                System.Windows.Forms.MessageBox.Show(ex.Message)
            End Try
        End Sub
    	Private Sub BindingNavigatorAddNewItem_Click(ByVal sender As System.Object, _
    	  ByVal e As System.EventArgs) Handles BindingNavigatorAddNewItem.Click
    	 
    	        Dim stamZeile As DataRow
    	        Dim i As Integer
    	        Dim j As Integer
    	        stamZeile = DS.book.NewRow()
    	 
            If Integer.TryParse(txtEJahr.Text, i) And Integer.Parse(txtBank.Text, _
    	          j) Then
    	 
    	            With stamZeile
    	 
    	                .Item("titel") = txtTitel.Text
    	                .Item("sprache") = txtSprache.Text
    	                .Item("ort") = txtOrt.Text
    	                .Item("isbn") = txtISBN.Text
    	                .Item("issn") = txtISSN.Text
    	                .Item("szahl") = txtSeiten.Text
    	                .Item("ejahr") = i
    	                .Item("b_nr") = j
    	                .Item("typ") = txtTyp.Text
    	            End With
    	 
    	 
    	            Me.bookTableAdapter.Insert(txtTitel.Text, txtSprache.Text, _
    	              txtOrt.Text, txtISBN.Text, txtISSN.Text, txtSeiten.Text, i, j, _
    	              txtTyp.Text) ' an dieser Stelle tritt eine Fehlermeldung auf
    	 
    	        End If
    	 
    	        DS.book.AcceptChanges()
    	        DS.book.Rows.Add(stamZeile)
    	        Me.bookTableAdapter.Update(Me.DS.book)
    	 
    	        Dim newID As Integer
    	        newID = CType(bookTableAdapter.ScalarQuery(), Integer)' hier fange ich 
    	        ' den                                     Primärschlüssel der 
    	        ' Master-Tabelle ab, der vom System generiert wurde.
    	 
    	        Dim comm As New OracleCommand
    	        Dim str As String
    	 
    	        comm.CommandType = CommandType.Text
    	 
    	 
    	 
    	        Dim PersV As New DataView
    	        PersV.Table = DS.author
    	        Dim persZeile As DataRowView
    	        PersV.RowStateFilter = DataViewRowState.Added
    	 
    	        For Each persZeile In PersV
    	            Select Case persZeile.Row.RowState
    	                Case DataRowState.Added
    	                    With persZeile
    	                        txtMnr.Text = newID.ToString
    	                        str = "INSERT INTO author (STAM_NR, FUNR, NAME," & _
    	                          "VORNAME) VALUES(.Item('stam_nr'), .Item('funr')," & _
    	                          ".Item('name'), .Item('vorname'))"
    	                        comm.CommandText = str
    	                        comm.ExecuteNonQuery()
    	                    End With
    	            End Select
    	 
    	        Next
    	 
        End Sub
    End Class

    dass die Daten erst nach dem Click auf dem Speichern Button erst in die DB persistent sind dass ist mir klar. Auch die Tatsache, dass ich nur den Code für das Speichern Button und nicht für AddNew  schreiben soll.

    Frage ist: soll ich einfach den Code aus meinem ersten Beitrag auskommentieren  bzw. löschen und folgender Code beibehalten?

    Private Sub bookBindingNavigatorSaveItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSave.Click
    Me.validate()
    Me.bookBidingSource.EndEdit()
    Me.authorBindingSource.EndEdit()
    Me.TableAdapterManager.UpdateAll(Me.DS)
    end Sub

    du schriebst: Ich würde so etwas niemals verwenden.

    gibts es eine bessere Vorgehensweise? bzw. einen Workaround?

    Wie gesagt mit VB habe ich nicht so viel Erfahrung.

    Danke


    SF


    • Bearbeitet soniaf612 Donnerstag, 31. Mai 2012 11:46
    Donnerstag, 31. Mai 2012 08:48
  • Hallo SF,

    fürchte dass ich ausserstande bin hier irgendwie sinnvoll Rat zu geben. Bitte um Verzeihung.

    Vielleicht könnte ich es ohne das Binding Zeugs besser nachvollziehen.  Doch dazu müsste man wissen was du konkret vorhast.

    Es sollen wohl Bücher gespeichert werden? Und Autoren? Und für jedes Buch ein separater Autorensatz??? Ist das nicht Redundanz die besser vermieden werden sollte?

    Man würde vielleicht eher eine Büchertabelle nehmen. Und eine Autorentabelle in der jeder Autor nur einmal vorkommt. Und dann eine Art Verbindungstabelle um Bücher und Autoren zu verbinden. Ein Buch kann ja mehrere Autoren haben. (Hier fängt es schon an, falls die Anzahl der möglichen Autoren eines Buches nicht klar zu sagen ist...)

    Also BuchAutor:
    ID
    BuchID
    AutorID

    wobei jede BuchID oder jeder AutorID in dieser Tabelle beliebig oft vorkommen kann halt nur nicht die gleiche Komination aus beiden.

    Donnerstag, 31. Mai 2012 16:01
  • Ergänzung:

    Im Code stehen zum Beispiel Sachen wie:

        txtMnr.Text = newID.ToString
    	                        str = "INSERT INTO author (STAM_NR, FUNR, NAME," & _
    	                          "VORNAME) VALUES(.Item('stam_nr'), .Item('funr')," & _
    	                          ".Item('name'), .Item('vorname'))"
    	                        comm.CommandText = str
    	                        comm.ExecuteNonQuery()
    	                    End With

    :-)

    Donnerstag, 31. Mai 2012 18:47
  • Ich vermute, dass Deine Probleme weniger mit VB verbunden sind als vielmehr mit grundsätzlichem Verständnis der Arbeit mit Datenobjekten bzw. Datensätzen.
     
    Am Anfang sollte man sich zur Bedientechnologie Gedanken machen.
     
    Der Anwender ist gewohnt, dass er zuerst seinen Wunsch kundtut, dass er neue Daten erfassen möchte. Nach dem Erfassen der Daten möchte er die Daten entweder speichern oder auch verwerfen. Beim Speichern erwartet er eine Prüfung der erfassten Daten und bei Problemen eine aussagekräftige Fehlerinformation, ohne dass etwas in der externen Datenbank gespeichert wird.
     
    Die Befehlsschaltfläche “AddNewItem” ist der erste Schritt, mit dem dem Anwender eine leere Erfassungsmaske aufgeblendet wird, ggf. mit voreingestellten Standardwerten. In diesem Schritt sollte noch kein Datensatz gespeichert werden, da es mit den leeren Feldern Probleme geben kann, z.B., wenn leere Zeichenketten nicht zulässig sind oder die Verknüpfungen zu Nachschlagetabellen noch fehlen usw.
     
    Die Befehlsschaltfläche “SaveItem” ist dann der Abschluss, mit dem der Anwender die Ablage in die Datenbank startet. In diesem Schritt sind die ggf. noch offenen Editzustände abzuschließen, das Formular zu prüfen, alle Eingaben zu prüfen und ein Datensatz mit den erfassten Daten abzulegen.
     
    Wenn mit gebundenen Steuerelementen gearbeitet wird, muss mit “AddNewItem” ein leeres Datenobjekt bzw. Datensatz bereitgestellt werden, ohne dass das Objekt in die Menge der Datenobjekte eingefügt wird (z.B. DataRow ist “detached”, d.s. ist nicht in der Rows-Auflistung der DataTable).
     
    Wenn ungebunden gearbeitet wird, müssen mit “AddNewItem” alle betreffenden Steuerelemente zurückgesetzt werden (Inhalte löschen bzw. mit Voreinstellungen belegen).
     
    Erst bei “SaveItem” wird bei gebundenen Steuerelementen das Datenobjekt der Gesamtmenge aller Datenobjekt hinzugefügt. Die dabei möglicherweise auftretenden Fehler sind abzufangen und geeignet dem Anwender mitzuteilen. Bei ungebundenen Steuerelementen wird erst jetzt bei Bedarf ein Datenobjekt erzeugt, gefüllt (wie in Deinem Code) und der Gesamtmenge aller Datenobjekt hinzugefügt oder die erfassten Daten werden ohne Datenobjekt direkt in die externe Datenbank geschrieben.
     
    Beim Hinzufügen eines Datenobjektes zur Gesamtmenge aller Datenobjekt ist zu unterscheiden, ob das Datenobjekt nur in der externen Datenbank abzulegen ist oder auch später zur Nutzung im Projekt benötigt wird. Wenn das Datenobjekt nur in der Datenbank abzulegen ist, dann reicht ein “TableAdapter.Insert”. Wenn jedoch das Datenobjekt später noch im Programm benötigt wird (z.B. zur Anzeige in einer Liste), dann ist das Datenobjekt der Gesamtmenge der Datenobjekte im Programm hinzuzufügen (bei Dir “DS.book.Rows.Add...”). Ein nachfolgendes “...Update” synchronisiert dann die im Programm gehaltenen Daten mit der externen Datenbank. In diesem Fall ist ein direktes “...Insert” nicht erforderlich, da die Update-Methode anhand des Rowstate’s das selbständig macht.
     
    Wenn mit Autowerten gearbeitet wird und die neuen Datensätze über Update gespeichert werden, dann ist das Rücklesen der bei neuen Datensätzen vergebenen Autowerte zu organisieren. Das kann, wenn der Datenbankprovider das angehängte Rücklesen unterstützt, mit dem Update oder auch im OnRowUpdated-Ereignis realisiert werden.
     
    Die AcceptChanges-Methode sollte mit viel Bedacht angewandt werden. Sie setzt u.a. die RowState’s auf “unchanged” zurück und nachfolgende Update-Methode erkennen die bis zum Aufruf der AcceptChanges-Methode gemachten Änderungen nicht mehr. Sowohlö die Fill- als auch die Update-Methoden implizieren bei den Standardeinstellung das AcceptChanges. Damit entfällt bis auf einige spezielle Sonderfälle die Notwendigkeit der Nutzung der AcceptChanges-Methode.
     
    --
    Viele Gruesse
    Peter
    Sonntag, 3. Juni 2012 08:09
  • Vielen Dank Peter,

    für die ausführliche Antwort  und die Erklärungen, die mir sehr viel weiterhelfen.

    Daraus habe ich folgendes entnommen und nachfolgende Änderungen vorgenommen:

    1. Für die Befehlsschaltfläche "AddNewItem", da ich mit gebundenen Steuerelementen arbeite sollte ich nicht mehr als sowas hier schreiben:

    Dim stamZeile As DataRow
    stamZeile = DS.book.NewRow()
    
     Dim persZeile As DataRowView
    persZeile = DS.author.NewRow()
    

    Also ein leeres Datenobjekt bereitstellen.

    2. Für "SaveItem" , da es sich um eine Oracle Datenbank handelt, habe ich eine gespeicherte Prozedur geschrieben um die Autowerten zurückzulesen und anschließen folgender Code für´s "SaveItem":

    Private Sub BookBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click
    
    Try
     
                    Dim comm = New
     OracleCommand("BookInsert")
                    With comm
                        .CommandType = CommandType.StoredProcedure
                        With .Parameters
     
    .Add(New OracleParameter(":bookid", OracleDbType.Int32, 8, _
      ParameterDirection.Output))
     
    .Add(New OracleParameter(":Typ", OracleDbType.Varchar2, 1, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":sprache", OracleDbType.Varchar2, 3, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":ort", OracleDbType.Varchar2, 30, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":Titel", OracleDbType.Varchar2, 255, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":isbn", OracleDbType.Varchar2, 20, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":issn", OracleDbType.Varchar2, 20, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":szahl", OracleDbType.Varchar2, 15, _
      ParameterDirection.Input, ""))
     
    .Add(New OracleParameter(":b_nr", OracleDbType.Int16, 2, _
      ParameterDirection.Input, 0))
     
    .Add(New OracleParameter(":ejahr", OracleDbType.Int16, 4, _
      ParameterDirection.Input, 0))
     
    End With
     
       End With
     
    'stored procedure ausführen
    if e. StatementType = StatementType.Insert Then
         Dim newID As Object = comm.ExecuteScalar()
         
       For Each col As DataColumn In 
             e.Row.Table.Columns
             if col.Autoincrement = True Then 
                 e.Row(col.Ordinal) = newID
                 Exit Sub
         
              End If
         Next
    End If
    
    MsgBox("Status: " & comm.Parameters(":bookid").Value)
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
    
    Me.Validate()
            Me.BookBindingSource.EndEdit()
            Me.AuthorBindingSource.EndEdit()
           DS.book.Rows.Add(stamZeile)
           DS.book.Rows.Add(persZeile)
            Me.TableAdapterManager.UpdateAll(Me.DS)
    End Sub
    

    3. beim Versuch einer OnRowUpdated-Methode zu schreiben bekam ich vom Kompiler die Meldung, dass das Ereignis nicht existiert.

    Jetzt beschäftige ich mich mit der Frage wie ich die Update-Methode für den TableAdapter schreiben soll bzw. wo ich es schreiben/definieren soll. 

    Ich habe ja Zugriff auf jeden TabeAdapter und auf deren Methode. Aber auf dem TableAdapterManager?

    Danke für deine Unterstützung Peter


    SF

    Sonntag, 3. Juni 2012 20:53
  • Mit AddNewItem stellt Du aber 2 leere Datenobjekte bereit, die dann auch beide zu sichern sind. Für ein Master-Detail Szenatio ist das nicht der übliche Weg. Zuerst ist der Master-Datensatz bereitzustellen und erst dann ist die Bearbeitung des Detail-Datensatzes sinnvoll. Es kann ja möglich sein, dass der Detail-Datensatz einem bereits vorhandenen Master-Datensatz unterzuordnen ist.
     
    Wenn Du ein leeres Datenobjekt bereitstellst, dann musst Du das auch binden. Wie sieht denn die Bindung aus?
     
    Wichtig bei Master-Detail Beziehungen mit Autowerten ist auch die konfliktfreie Generierung der Autowerte, was üblicher weise mit negativen ID’s für neue Datensätze im Client realisiert wird. Damit es beim Speichern in der externen Datenbank keine Inkonsitenzen gibt, muss die Reihenfolge der Speicherung eingehalten werden:
     
    1. New Master
    2. New Child
    3. Update Master
    4. Update Child
    5. Delete Child
    6. Delete Master
     
    Bei der Arbeit mit dem TableAdapter macht das automatisch UpdateAll-Methode.
     
    Alle neu vergeben ID’s müssen im gleichen Kontext wie das Insert-Command die Änderungen (Autowert und sonstige Ergebnisse der Insert-Trigger) zurücklesen. Wenn das nicht mit an Insert angehängten Select Anweisungen möglich ist, muss das OnRowUpdated Ereignis des DataAdapters genutzt werden. Der DataAdapter ist im TableAdapter gekapselt.
     
    In Deinem “SaveItem”-Codeschnipsel hast Du den direkten Zugriff auf die externe Datenbank mit der Pufferung im DataSet gemischt. Ich denke, dass das keine gute Lösung ist. Einfacher ist es den TableAdapter mit der StoredProcedure für das Insert zu konfigurieren und dann mit einem UpdateAll alles mit der Datenbank zu synchronisieren.
     
    --
    Viele Gruesse
    Peter
    Montag, 4. Juni 2012 07:47
  • Hallo Peter und nochmal Danke für deine Antwort und die Empfelungen.

    Dazu habe ich ein paar Fragen bzw. Unklarheiten.

    1. Wenn du z.B sagst: "zuerst ist der Master-Datensatz bereitzustellen und erst dann ist die Bearbeitung des Detail-Datensatzes sinnvoll"

    meinst du ich müsste im "AddNewItem" nur sowas haben?

    Dim stamZeile As DataRow
    stamZeile = DS.book.NewRow()

    2. So sieht die Bindung aus:

    Dim stamZeile As DataRow
    stamZeile = DS.book.NewRow()
    If Integer.TryParse(txtEJahr.Text, i) And Integer.Parse(txtB_nr.Text, j) Then
    With stamZeile
    	 
    	           .Item("titel") = txtTitel.Text
    	           .Item("sprache") = txtSprache.Text
    	           .Item("ort") = txtOrt.Text
    	           .Item("isbn") = txtISBN.Text
    	           .Item("issn") = txtISSN.Text
    	           .Item("szahl") = txtSeiten.Text
    	           .Item("ejahr") = i
    	           .Item("b_nr") = j
    	           .Item("typ") = txtTyp.Text
    End With
    DS.book.Rows.Add(stamZeile)
           
    Dim persZeile As DataRow
    persZeile = DS.author.NewRow()
    With persZeile 
                        .Item("sta_nr") = txtStam_nr.Text
    	           .Item("funr") = txtFunr.Text
    	           .Item("name") = txtName.Text
    	           .Item("vorname") = txtVorname.Text
    	           
    End With
    DS.author.Rows.Add(persZeile)

    3. Die Autowerte sind tatsächlich mit negativen ID's im Client erzeugt

    4. zu deiner Empfehlung: "Wenn das nicht mit an Insert angehängten Select Anweisungen möglich ist, muss das OnRowUpdated Ereignis des DataAdapters genutzt werden. Der DataAdapter ist im TableAdapter gekapselt"

    habe ich folgendes geschrieben:

    Private Sub BookTableAdapter_OnRowUpdated(ByVal sender_

    As System.Object, ByVal e As System.EventArgs) Handles BookTableAdapter.OnRowUpdated End Sub

    erhalte ich eine Fehlermeldung nämlich, dass das Ereignis "OnRowUpdated" nicht gefunden wurde. Was mache ich da falsch?

    5. Das "SaveItem" Codeschnipsel habe ich wie folgt geändert

    Private Sub BookBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click
    Try
     
                   'stored procedure ausführen
    if e. StatementType = StatementType.Insert Then
     'zuvor habe ich den TableAdapter mit der 
     'Stored Procedure konfiguriert
         Dim newID As Object = BookTableAdapter.Insert()
         
       For Each col As DataColumn In 
             e.Row.Table.Columns
             if col.Autoincrement = True Then 
                 e.Row(col.Ordinal) = newID
                 Exit Sub
         
              End If
         Next
    End If
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
    Me.Validate()
            Me.BookBindingSource.EndEdit()
            Me.AuthorBindingSource.EndEdit()
           
            Me.TableAdapterManager.UpdateAll(Me.DS)
    End Sub

    Danke für deine Unterstützung


    SF


    • Bearbeitet soniaf612 Mittwoch, 6. Juni 2012 16:28
    Mittwoch, 6. Juni 2012 16:27
  • Ich hatte letzte Woche wenig Zeit, aber jetzt bis zum nächsten Fußballspiel versuche ich mal, Deine Fragen zu beantworten:
     
    Zu 1.
     
    Mit “Master-Datensatz bereitstellen” meine ich die Auswahl des betreffenden Master-Datensatzes. Wenn es diesen Datensatz noch nicht gibt, ist er anzulegen und auch in die Tabelle einzufügen. Das bedeutet, dass nur ein NewRow nicht ausreicht. Nach dem Füllen des neuen Datensatzes muss auch ein DS.book.Rows.Add(stamZeile) folgen. Anstelle eines nicht typsicheren Datensatzes sollte besser aus einem typisierten DataSet ein typsicherer Datensatz neu erstellt werden. Dazu gibt es die Methode NewRowBook oder so ähnlich.
     
    Zu 2.
     
    Was Du mit Bindung bezeichnet, ist keine Bindung, sondern nur eine Belegung/Beschreibung der Eigenschaften (Felder) eines DataRow-Objektes mit Werten.
     
    Wenn Du typsicher (mit typisiertem DataSet, welches vom Designer erzeugt wird) arbeitest, kannst Du direkt auf die Feldeigenschaften zugreifen, z.B. so:
     
    With stamZeile
      .Titel = txtTitel.Text
    ...
     
    Zu 3.
     
    Die Nutzung von negativen Autowerten ist die Voraussetzung für eine fehlerfreie Arbeit im Mehrnutzerbetrieb.
     
    Wichtig in diesem Zusammenhang ist, dass die vom Datenbankserver vergebenen Autowerte beim Insert auch als Fremdschlüsselwerte in die Childsätze eingetragen werden, wenn es zu diesem Zeitpunkt auch Childsätze gibt. Das wird am einfachsten realisiert, indem kaskadiertes Update in den DataRelations im DataSet eingestellt ist.
     
    Zu 4.
     
    Der TableAdapter kapselt einen DataAdapter. Und von diesem kann man das OnRowUpdated-Ereignis abonnieren. Das ist aber nur erforderlich, wenn kein angehängtes Rücklesen untertstützt wird. Das kann man in der xsd des DataSets prüfen, indem man sich die Insert-Anweisung anschaut.
     
    Die Methode zur Ereignisbehandlung kann man beispielsweise so anhängen:
     
    AddHandler BookTableAdapter.Adapter.RowUpdated, AddressOf BookTableAdapter_OnRowUpdated
     
    Zu 5.
     
    In der SaveItem-Methode ist das Setzen des Autowertes nicht mehr sinnvoll. Das sollte entweder vorher gemacht werden oder mit der UpdateAll-Methode und ggf. angehängten RowUpdated-Ereignis, in dem bei allen Inserts die Autowerte zurückgelesen und eingetragen werden.
     
    --
    Viele Gruesse
    Peter
    Samstag, 16. Juni 2012 18:58
  • Hallo soniaf612,

    Ich gehe davon aus, dass die Antworten Dir weitergeholfen haben.
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Dienstag, 19. Juni 2012 06:23
    Moderator
  • Hallo Robert,

    noch nicht

    ich bin teste und setze gerade die Tipps von Peter um bevor ich mich melde

    Danke

    SF


    SF

    Dienstag, 19. Juni 2012 19:02
  • Hallo Robert,

    noch nicht

    ich bin teste und setze gerade die Tipps von Peter um bevor ich mich melde

    Danke

    SF


    SF


    ****************************************************************************************************************
    Dieser Thread wurde mangels weiterer Beteiligung des Fragestellenden ohne bestätigte Lösung abgeschlossen.
    Neue Rückfragen oder Ergänzungen zu diesem Thread bleiben weiterhin möglich.
    ****************************************************************************************************************

    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Freitag, 29. Juni 2012 07:55
    Moderator