Fragensteller
Master/Detail - Problem beim Einfügen

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
- Bearbeitet Robert BreitenhoferModerator Donnerstag, 7. Juni 2012 08:16 Formatierung
- Typ geändert Robert BreitenhoferModerator Freitag, 29. Juni 2012 07:56 Keine Rückmeldung des Fragenstellender
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
-
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 -
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
- Bearbeitet Robert BreitenhoferModerator Donnerstag, 7. Juni 2012 08:14 Formatierung
-
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
-
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
-
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. -
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
-
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. -
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
:-)
-
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 -
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
-
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 Master2. New Child3. Update Master4. Update Child5. Delete Child6. Delete MasterBei 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 -
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
-
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_OnRowUpdatedZu 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 -
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,
RobertRobert Breitenhofer, MICROSOFT
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. -
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
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.