Update a single row in database
I have 1 parent table and 2 child tables. I've added 4 new rows to the parent DataTable and a with each new parent row I've added a couple of new rows to each child DataTable. I would like to loop through the set of added rows in the parent table to update the changes (row additions) in the underlying 3 tables of the MS Access dB.
Here is my code:
Code:
CType(dsMain.Relations("Task_ActionItems").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.Cascade
CType(dsMain.Relations("Task_TaskNotes").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.Cascade
For Each rowTask As DataRow In dsMain.Tables("Tasks").Select(vbNullString, vbNullString, DataViewRowState.Added)
newTask(0) = rowTask
Debug.WriteLine("Tasks = " & CStr(dsMain.Tables("Tasks").Select("", "", DataViewRowState.Added).Count))
iTasks += daTasks.Update(newTask) '...persists the DataTable change to the MS Access dB table
Debug.WriteLine("Tasks = " & CStr(dsMain.Tables("Tasks").Select("", "", DataViewRowState.Added).Count))
actionRows = dsMain.Tables("ActionItems").Select("TaskID = " & CLng(rowTask("RecID")))
If actionRows.Count > 0 Then
iActionItems += daActionItems.Update(actionRows) '...persists the DataTable change to the MS Access dB table
End If
noteRows = dsMain.Tables("TaskNotes").Select("TaskID = " & CLng(rowTask("RecID")))
If noteRows.Count > 0 Then
iTaskNotes += daTaskNotes.Update(noteRows) '...persists the DataTable change to the MS Access dB table
End If
Next rowTask
CType(dsMain.Relations("Task_ActionItems").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.Cascade
CType(dsMain.Relations("Task_TaskNotes").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.Cascade
Since there are 4 new rows in the parent table, the code makes 4 trips through the For Each...Next loop. The Debug.Writeline statements show the count of parent table rows having their DataViewRowState property = "Added" immediately before/after calling the DataAdapter.Update method for the parent table. Since I'm calling the parent table's DataAdapter.Update method and passing it an array of DataRows (newTask) that only has a single DataRow (newTask(0)), I would expect that the first and second Debug.Writeline statements would show the count of parent table rows having their DataViewRowState.Added to decrease by 1 with each trip through the For Each...Next loop.
What is actually happening is that on the first trip through the loop the first Debug.Writeline statement shows "Tasks = 4", the second Debug.Writeline statement shows "Tasks = 0" in the Immediate Window, and the value of iTasks = 1. On the second trip through the loop the first Debug.Writeline statement shows "Tasks = 0", the second Debug.Writeline statement shows "Tasks = 0" in the Immediate Window, and the value of iTasks = 0. It appears that all 4 new parent table rows are updated in the Accesss dB on the first pass through the loop.
What can I do/change to get the parent table's DataAdapter.Update method to only update 1 row per trip through the loop?
Re: Update a single row in database
Quote:
Originally Posted by
Mark@SF
What can I do/change to get the parent table's DataAdapter.Update method to only update 1 row per trip through the loop?
You seem not to understand how data adapters work. The whole point is that they save ALL changes in one go. Trying to make them not do so is nonsensical. Why do you think doing so would be a god idea? If you have a parent table and two child tables then you simply call Update once each on three data adapters, the parent before the children. That's it, that's all. No loops.
Re: Update a single row in database
Having said that, if there is some OTHER reason why you'd want to update one row at a time, then you can't use the Update method. You still have the option to do the work yourself, though. The RowState of the datarow will tell you whether it has been added or not, so you could iterate through the rows looking for a RowState of Added, and performing an INSERT query yourself when one is encountered. The dataadapter won't help you with that, though, so it's rarely done. I've never done that, myself. If I have data in a datatable, then I let the dataadapter do the work.
Re: Update a single row in database
Quote:
Originally Posted by
Shaggy Hiker
The RowState of the datarow will tell you whether it has been added or not, so you could iterate through the rows looking for a RowState of Added, and performing an INSERT query yourself when one is encountered.
You can call the GetChanges method of the DataTable to get the rows with just one or more particular RowState values.
Re: Update a single row in database
My dB table ("Tasks") has a nested relationship to itself. The table has a PrimaryKey ("RecID") and a ForeignKey ("ParentID").
For example, I add 3 records to the Tasks DataTable (father, son, and grandson Tasks). Then I call the daTasks.Update method and pass it the Tasks DataTable.
Code:
'...only updates the "father" task in a "father-son-grandson" set of tasks (i.e., iTasks = 1)
iTasks = daTasks.Update(dsMain.Tables("Tasks"))
Is my only option to use an "INSERT INTO..." SQL command to add each of these records, one-by-one, starting with the "father", then the "son", and finally the "grandson" Task?
Re: Update a single row in database
Quote:
Originally Posted by
Mark@SF
My dB table ("Tasks") has a nested relationship to itself. The table has a PrimaryKey ("RecID") and a ForeignKey ("ParentID").
Why do we have to wait for a later post to be informed of the actual reason that you want to do something unconventional? You should be providing all relevant information in the original post and the reason for doing something that normally shouldn't be done is relevant information.
Regardless, I suspect that you may still be able to do it with a single call. I have never tested this scenario so I don't know for sure but I would suggest that you test it out. Check out this thread for a way to retrieve auto-generated IDs when inserting into an Access database. You would normally do this when inserting related data from a DataSet into two database tables. You may find that it works just as well when the relation is between a table and itself. If so, a single call to Update can be made on a data adapter as normal, assuming that all foreign key values appear after the corresponding primary key in the DataTable.
Note that the Update method basically does a loop through the DataTable and saves each row individually anyway, but it does it in a far more efficient way than you would do it. Similarly, the Fill method uses a data reader under the hood.
Re: Update a single row in database
My apologies for not giving more information in my original post. When I wrote it I wan’t sure what might be the problem and I thought that the level of detail in that post was going to be too much. At this point, I’m still not sure what is causing the dt.Tasks.Update method to only add the ”father”” record to the Access dB table. After the Update method finishes, the DataViewRowState.Added values for the other 2 pending record additions (“son” and “grandson”) have been changed to DataViewRowState.Unchanged.
I’ve tried attacking this from several angles with the daTasks.Update method:
* passing the entire Tasks DataTable: dsMain.Tables("Tasks")
* passing only the newly added rows: dsMain.Tables(“Tasks”).Select(“”, ””, DataViewRowState.Added)
* passing only the newly added rows in sorted order: dsMain.Tables(“Tasks”).Select(“”, ”ParentID”, DataViewRowState.Added)
* passing only one row at a time: dsMain.Tables(“Tasks”).Select(“RecID = “ & lngRecID)
You’re right, my skill level on this topic is not where I’d like it to be. I’m hoping to learn more and get this code working properly.
Thanks again for your assistance (and that of others).
Re: Update a single row in database
I'll do a bit of experimentation to see what works. There are only two options that I would consider. The first would be the best option but I'm not sure that it would work, but the second option will definitely work. I'll get back to you, but it may be a few hours as I'm off out.
Re: Update a single row in database
I am using the “SELECT @@IDENTITY” command to retrieve the auto-number value (PrimaryKey field) for newly added DataRows in the DataTable.
Re: Update a single row in database
Sorry for the double entries. The website is giving me error messages that I need to wait 30 seconds between posts and asking me to try again in 30 seconds. When I do that, it posts 2 replies. :blush:
Re: Update a single row in database
Quote:
After the Update method finishes (see post #5), the DataViewRowState.Added values for the other 2 pending record additions (“son” and “grandson”) have been changed to DataViewRowState.Unchanged.
Before I call the daTasks.Update method there are 3 DataRows with DataViewRowState.Added values. After the Update method is called there are 0 DataRows with DataViewRowState.Added values. All 3 DataRows now have DataViewRowState.Unchanged values.
I examined this further by watching the daTasks.RowUpdating event as each DataRow is updated (on a row-by-row basis via a single call of the daTasks.Update method as shown in post #5). Before the first trip through the daTasks RowUpdating event, all 3 DataRows have DataViewRowState.Added values. After the first trip through the daTasks RowUpdating event, all 3 DataRows have DataViewRowState.Unchanged values. When the second and third trips through the daTasks RowUpdating event occur, the DataAdapter doesn't find any DataRows with DataViewRowState.Added values. This seems to be the reason why only the “father” DataRow is getting added to the Access dB.
Having discovered this, I went back and examined the Relations between my DataTables. I have the following Relation that creates the nested relationship for the Tasks DataTable:
Code:
'Task and Subtasks
Dim fkc As ForeignKeyConstraint
dsMain.Relations.Add("Task_Subtasks", dsMain.Tables("Tasks").Columns("RecID"), dsMain.Tables("Tasks").Columns("ParentID"))
fkc = dsMain.Relations("Task_Subtasks").ChildKeyConstraint
fkc.DeleteRule = Rule.Cascade
fkc.UpdateRule = Rule.Cascade
fkc.AcceptRejectRule = AcceptRejectRule.Cascade
Temporarily changing the fkc.AcceptRejectRule to "None" fixes the problem (note that in the code snippet below I've included the "sort" argument in the Select command to guarantee that the parent Tasks are added before the child Tasks):
Code:
CType(dsMain.Relations("Task_Subtasks").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.None
iTasks = daTasks.Update(dsMain.Tables("Tasks").Select("", "ParentID, RecID", DataViewRowState.Added)) '...this table has a "nested" relationship with itself
CType(dsMain.Relations("Task_Subtasks").ChildKeyConstraint, ForeignKeyConstraint).AcceptRejectRule = AcceptRejectRule.Cascade
Now the value of iTasks is 3 after the Update method is called and the count of DataRows having DataViewRowState.Added values decrements by 1 with each trip through the daTasks RowUpdating event. :)
As per Microsoft https://docs.microsoft.com/en-us/dot...tframework-4.8:
Quote:
By default, AcceptChanges is called implicitly after an update.
As per Microsoft https://docs.microsoft.com/en-us/dot...tframework-4.8:
Quote:
Changes to a DataTable are not final until you call the AcceptChanges method. When either AcceptChanges or RejectChanges is called on a row in the parent table, the AcceptRejectRule value determines whether or not changes are propagated to corresponding rows in the child table.
I was temporarily changing the fkc.AcceptRejectRule to "None" for my "normal" parent/child table relationships when adding new rows via the daTables DataAdapter to the Access dB table (e.g. the Tasks table and the TaskNotes table). Having said that, I failed to recognize that even though there is only 1 "real" Tasks table, the daTasks.Update method was executing across 3 "logical" tables ("father" Tasks table, "son" Tasks table, and "grandson" Tasks table). When the new row for the "father" Task was added to the Access dB table, the AcceptRejectRule = AcceptRejectRule.Cascade was propagating changes to the 2 child Tasks DataTables -- the "son" and "grandson" Tasks DataTables (setting the DataViewRowState value on every Added DataRow in the child DataTables to "Unchanged").
What can be confusing about this? First, I had to recognize that the "real" Tasks table is actually multiple "logical" tables (not sure if this is the correct way to describe this). Second, I had to get a better understanding of how a DataAdapter works (using the DataAdapter.RowUpdating event to follow the daTasks.Update method as it iterated row-by-row through the Tasks DataTable was really helpful), and finally I had to better understand the workings and differences between the DataTable object (memory-resident) and the database table object (disc-resident). This last point included not only syntactical differences ("DataRow" objects and "record" objects), but also the role of the DataRow's DataViewRowState values that are used by the DataAdapter. Finally, the key/relevant information that I needed to get up-to-speed on this problem was scattered throughout *a lot* of Microsoft technical webpages and finding/organizing it was challenging :(.
So, yes JMC was correct in his assessment (see post #2) :)...
Quote:
You seem not to understand how data adapters work.
...but I've learned a lot from this episode.
Before I mark this post as "complete", I'd like to hear back from JMC (to confirm that I've diagnosed this problem correctly) and anyone else who might have something helpful to add.
Thanks.
Re: Update a single row in database
How about posting the actual Select command for daTasks. Just for clarity.
Re: Update a single row in database
Here's the Select command:
Code:
daTasks.Update(dsMain.Tables("Tasks").Select("", "ParentID, RecID", DataViewRowState.Added))
My underlying database is Microsoft Access and its AutoNumber field values are assigned as long integers in serial fashion (1, 2, 3, 4, etc.). So the PrimaryKey and ForeignKey values reflect the order of entry. Therefore including a "sort" criteria in the Select statement ensures that the "father" record in the Access dB is added before the "son" record and the "son" record is added before the "grandson" record, etc. The "father" records don't have a ParentID value (DataRows that have ParentID = dbNull.Value will be sorted to the top of the array of DataRow objects) and they will be added to the Access dB first. Likewise, "son" DataRows will have a ParentID value that is less than "grandson" DataRows . This will be true for every level in the Task tree.
Re: Update a single row in database
Quote:
Originally Posted by
Mark@SF
Sorry for the double entries. The website is giving me error messages that I need to wait 30 seconds between posts and asking me to try again in 30 seconds. When I do that, it posts 2 replies. :blush:
That's been an issue for some time, with those in a position to fix it showing no will to do so. The problem is that the site tries to submit your post twice and the second fails because it is within the 30 second limit. That's a good thing, because you don't want that second submission made. If you get that message and you know that you haven't tried to post twice in 30 seconds, don't go and submit the same post again. Note that this only happens with the Post Quick Reply option. If you hit Go Advanced first then you won't have the issue.
Re: Update a single row in database
Thanks, JMC. Good to know and I'll keep it in mind for future postings.
Re: Update a single row in database
Code:
daTasks.Update(dsMain.Tables("Tasks").Select("", "ParentID, RecID", DataViewRowState.Added))
That is not the dataadapters SelectCommand. I'm talking about the Select command the dataadapter uses to Fill the datatable.
Re: Update a single row in database
wes -
Sorry, I didn't understand your request..
The daTasks.InsertCommand is:
Code:
Dim insertData As New OleDb.OleDbCommand("INSERT INTO [Tasks] (AddDate, ModDate, ProjectID, TaskName, TaskDescription, StartDate, DueDate, DoneDate, Status, State, Priority, ParentID, CategoryID, OwnerID, TreeNodeLevel, ToDoItem, RepeatCycle, DueDateOffsetDays) " & _
"VALUES (@AddDate, @ModDate, @ProjectID, @TaskName, @TaskDescription, @StartDate, @DueDate, @DoneDate, @Status, @State, @Priority, @ParentID, @CategoryID, @OwnerID, @TreeNodeLevel, @ToDoItem, @RepeatCycle, @DueDateOffsetDays)", con)
Here the parameters that are used by the InsertCommand:
Code:
insertData.Parameters.Add("@AddDate", OleDb.OleDbType.Date).SourceColumn = "AddDate"
insertData.Parameters.Add("@ModDate", OleDb.OleDbType.Date).SourceColumn = "ModDate"
insertData.Parameters.Add("@ProjectID", OleDb.OleDbType.BigInt).SourceColumn = "ProjectID"
insertData.Parameters.Add("@TaskName", OleDb.OleDbType.Char, 255).SourceColumn = "TaskName"
insertData.Parameters.Add("@TaskDescription", OleDb.OleDbType.LongVarWChar, 65536).SourceColumn = "TaskDescription"
insertData.Parameters.Add("@StartDate", OleDb.OleDbType.Date).SourceColumn = "StartDate"
insertData.Parameters.Add("@DueDate", OleDb.OleDbType.Date).SourceColumn = "DueDate"
insertData.Parameters.Add("@DoneDate", OleDb.OleDbType.Date).SourceColumn = "DoneDate"
insertData.Parameters.Add("@Status", OleDb.OleDbType.Boolean).SourceColumn = "Status"
insertData.Parameters.Add("@State", OleDb.OleDbType.UnsignedTinyInt).SourceColumn = "State"
insertData.Parameters.Add("@Priority", OleDb.OleDbType.UnsignedTinyInt).SourceColumn = "Priority"
insertData.Parameters.Add("@ParentID", OleDb.OleDbType.BigInt).SourceColumn = "ParentID"
insertData.Parameters.Add("@CategoryID", OleDb.OleDbType.BigInt).SourceColumn = "CategoryID"
insertData.Parameters.Add("@OwnerID", OleDb.OleDbType.BigInt).SourceColumn = "OwnerID"
insertData.Parameters.Add("@TreeNodeLevel", OleDb.OleDbType.UnsignedTinyInt).SourceColumn = "TreeNodeLevel"
insertData.Parameters.Add("@ToDoItem", OleDb.OleDbType.Boolean).SourceColumn = "ToDoItem"
insertData.Parameters.Add("@RepeatCycle", OleDb.OleDbType.UnsignedTinyInt).SourceColumn = "RepeatCycle"
insertData.Parameters.Add("@DueDateOffsetDays", OleDb.OleDbType.BigInt).SourceColumn = "DueDateOffsetDays"
Here's howI define the DataAdapter's InsertCommand:
Code:
daTasks.InsertCommand = insertData
Here's some other details from the procedure I use to initialize the DataAdapter for the Tasks DataTable:
Code:
'...delegate for handling RowUpdated event of DataAdapter (needed to get the Identity column value (@@IDENTITY) of the added record)
AddHandler daTasks.RowUpdated, AddressOf Row_Updated
daTasks.MissingSchemaAction = MissingSchemaAction.AddWithKey
Finally, here's the field definition information for the Tasks table in the Access dB:
Code:
'...MS Access database table "Tasks" field information:
' RecID field is the auto-number field
' AddDate field is required, DefaultValue = Now()
' ModDate field is required, DefaultValue = Now()
' ProjectID field is required
' TaskName field is required
' TaskDescription field is not required
' StartDate field is not required
' DueDate field is not required
' DoneDate field is not required
' Status field is not required, DefaultValue = Yes
' State field is required, DefaultValue = 0
' Priority field is required, DefaultValue = 1
' ParentID field is not required
' CategoryID field is not required
' OwnerID field is not required
' TreeNodeLevel field is not required
' ToDoItem field is not required, DefaultValue = Yes
' RepeatCycle field is required, DefaultValue = 0
' DueDateOffsetDays field is not required
Re: Update a single row in database
Since jmc mentioned using "SELECT @@IDENTITY" to get the autonumber field's value when a new DataRow is added to the Tasks DataTable (post #6 above), here's the code from my Row_Updated procedure (the delegate for handling the RowUpdated event of the daTasks DataAdapter):
Code:
Try
If e.Row.RowState = DataRowState.Added Then
Using cmd As New OleDb.OleDbCommand("SELECT @@IDENTITY", con)
If e.Status = UpdateStatus.Continue AndAlso e.StatementType = StatementType.Insert Then
lngNewRecID = Convert.ToInt64(cmd.ExecuteScalar)
'...by convention, all tables that have a PrimaryKey also have the "RecID" (auto-number) field that is used as the PrimaryKey
If lngNewRecID <> 0 Then
e.Row("RecID") = lngNewRecID '...updates the RecID field of newly added record (overwrites any existing value)
End If
End If
End Using
End If
Catch ex As Exception
HandleError(ex, True)
End Try
Re: Update a single row in database
lol
Well that's a lot of interesting code but it still isn't the dataadapter SelectCommand.
Code:
Dim da As New OleDb.OleDbDataAdapter("Select userid from users", My.Settings.waterConnectionString)
"Select userid from users" that is the SelectCommand text.
The reason I'm asking is I not sure if the selectcommand targets just one table or multiple tables using a JOIN command like
Code:
"Select users.userid, table2.propid from users INNER JOIN Table2 On users.userid=table2.userid"
Does each table involved has it's own dataadapter.
Re: Update a single row in database
Sorry for the "code dump" :)
Here is the declaration statement for the daTasks DataAdapter:
Code:
Public daTasks As New OleDb.OleDbDataAdapter
I declare all the DataAdapter objects in my project as global objects . These declarations are done in a code module that includes procedures to initialize each DataAdapter object including its SelectCommand, DeleteCommand, InsertCommand, and UpdateCommand (the first 4 code snippets in post #19 are from the procedure that initializes the daTasks DataAdapter's InsertCommand).
*I apologize for posting the InsertCommand details instead of the SelectCommand details.*
The daTasks.SelectCommand does not use a JOIN command.
Code:
Dim selectData As New OleDb.OleDbCommand("SELECT * FROM [Tasks] ORDER BY TaskName", con)
daTasks.SelectCommand = selectData
Each of the tables in my project have their own DataAdapter.
Re: Update a single row in database
Ok,
So if the parent and child dataadapter all only process one database table then the link that jmc provided you in post #6 will show you how to add new rows to parent/child datatable that have an Autonumber field.
Here is jmc's code,
Code:
Imports System.Data.OleDb
Public Class Form1
#Region " Fields "
''' <summary>
''' Contains the tables and the relation between them.
''' </summary>
Private data As New DataSet
''' <summary>
''' Connects to the database.
''' </summary>
Private connection As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\ParentChild.mdb;User Id=admin;Password=;")
''' <summary>
''' Retrieve data from the database and save changes back to the database.
''' </summary>
''' <remarks>
''' The WithEvents keyword is required so that the adapters can be included in the Handles clause of their RowUpdated event handlers.
''' </remarks>
Private WithEvents parentDataAdapter As New OleDbDataAdapter("SELECT * FROM Parent",
connection) With {.MissingSchemaAction = MissingSchemaAction.AddWithKey}
Private WithEvents childDataAdapter As New OleDbDataAdapter("SELECT * FROM Child",
connection) With {.MissingSchemaAction = MissingSchemaAction.AddWithKey}
''' <summary>
''' Generate the InsertCommand, UpdateCommand and DeleteCommand for the adapters when their Update method is called.
''' </summary>
Private parentCommandBuilder As New OleDbCommandBuilder(Me.parentDataAdapter) With {.QuotePrefix = "[",
.QuoteSuffix = "]"}
Private childCommandBuilder As New OleDbCommandBuilder(Me.childDataAdapter) With {.QuotePrefix = "[",
.QuoteSuffix = "]"}
#End Region 'Fields
#Region " Event Handlers "
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Try
LoadSchema()
ConfigureAutoIncrements()
ConfigureRelation()
LoadData()
BindData()
Catch ex As Exception
MessageBox.Show(ex.ToString(), "Error")
End Try
End Sub
''' <summary>
''' Raised after a parent row is saved to the database.
''' </summary>
''' <param name="sender">
''' The adapter that saved the row.
''' </param>
''' <param name="e">
''' The data for the event.
''' </param>
''' <remarks>
''' This event handler is used to retrieve an auto-generated ID from the database after a row is inserted and update the corresponding row in the local data set.
''' </remarks>
Private Sub parentDataAdapter_RowUpdated(sender As Object, e As OleDbRowUpdatedEventArgs) Handles parentDataAdapter.RowUpdated
'We are only interested in new records.
If e.StatementType = StatementType.Insert Then
'Get the last ID auto-generated by the database.
Using command As New OleDbCommand("SELECT @@IDENTITY", Me.connection)
'Update the ID of the local row.
e.Row("ParentID") = CInt(command.ExecuteScalar())
End Using
End If
End Sub
''' <summary>
''' Raised after a child row is saved to the database.
''' </summary>
''' <param name="sender">
''' The adapter that saved the row.
''' </param>
''' <param name="e">
''' The data for the event.
''' </param>
''' <remarks>
''' This event handler is used to retrieve an auto-generated ID from the database after a row is inserted and update the corresponding row in the local data set.
''' </remarks>
Private Sub childDataAdapter_RowUpdated(sender As Object, e As OleDbRowUpdatedEventArgs) Handles childDataAdapter.RowUpdated
'We are only interested in new records.
If e.StatementType = StatementType.Insert Then
'Get the last ID auto-generated by the database.
Using command As New OleDbCommand("SELECT @@IDENTITY", Me.connection)
'Update the ID of the local row.
e.Row("ChildID") = CInt(command.ExecuteScalar())
End Using
End If
End Sub
''' <summary>
''' Raised when the user clicks the Save button on the parent tool bar.
''' </summary>
''' <param name="sender">
''' The Save button.
''' </param>
''' <param name="e">
''' The data for the event.
''' </param>
''' <remarks>
''' Saves all parent and child data.
''' </remarks>
Private Sub SaveToolStripButton_Click(sender As System.Object, e As System.EventArgs) Handles SaveToolStripButton.Click
If Me.Validate() Then
Me.parentBindingSource.EndEdit()
Me.childBindingSource.EndEdit()
Me.parentDataAdapter.Update(Me.data, "Parent")
Me.childDataAdapter.Update(Me.data, "Child")
End If
End Sub
#End Region 'Event Handlers
#Region " Methods "
''' <summary>
''' Build the data set schema without actually retrieving any data.
''' </summary>
Private Sub LoadSchema()
Me.parentDataAdapter.FillSchema(Me.data, SchemaType.Source, "Parent")
Me.childDataAdapter.FillSchema(Me.data, SchemaType.Source, "Child")
End Sub
''' <summary>
''' Configure the auto-increment properties of the ID columns of both tables.
''' </summary>
Private Sub ConfigureAutoIncrements()
ConfigureAutoIncrement(Me.data.Tables("Parent").Columns("ParentID"))
ConfigureAutoIncrement(Me.data.Tables("Child").Columns("ChildID"))
End Sub
''' <summary>
''' Configure the auto-increment properties of a column.
''' </summary>
''' <remarks>
''' The column will be configured to generate local IDs that will never class with those generated by the database.
''' Database IDs will start at 1 and increase sequentially while local IDs will start at 0 and decrease sequentially.
''' </remarks>
Private Sub ConfigureAutoIncrement(column As DataColumn)
With column
'The column should automatically generate local IDs.
.AutoIncrement = True
'The first local ID value generated should be 0.
'The database IDs will start at 1, so local IDs will not clash.
.AutoIncrementSeed = 0
'The local IDs should each be 1 less than the previous.
'The database IDs will each be 1 more than the previous, so local IDs will not clash.
.AutoIncrementStep = -1
End With
End Sub
''' <summary>
''' Configure the foreign key relation between the ParentID columns in the Parent and Child tables.
''' </summary>
''' <remarks>
''' The relation is set to cascade updates, which means that when the local ID in a parent
''' row is replaced with the database ID, all related child rows will be updated as well.
''' </remarks>
Private Sub ConfigureRelation()
Me.data.Relations.Add("ParentChild",
Me.data.Tables("Parent").Columns("ParentID"),
Me.data.Tables("Child").Columns("ParentID"),
True).ChildKeyConstraint.UpdateRule = Rule.Cascade
End Sub
''' <summary>
''' Retrieve the data from the database.
''' </summary>
''' <remarks></remarks>
Private Sub LoadData()
Me.parentDataAdapter.Fill(Me.data, "Parent")
Me.childDataAdapter.Fill(Me.data, "Child")
End Sub
''' <summary>
''' Displays the data in the grids.
''' </summary>
''' <remarks>
''' The child grid is bound such that it will only display the records related to the selected parent record.
''' </remarks>
Private Sub BindData()
Me.parentBindingSource.DataSource = Me.data.Tables("Parent")
Me.childBindingSource.DataMember = "ParentChild"
Me.childBindingSource.DataSource = Me.parentBindingSource
Me.parentDataGridView.DataSource = Me.parentBindingSource
Me.childDataGridView.DataSource = Me.childBindingSource
End Sub
#End Region 'Methods
End Class
It's an excellent demo, what part are you having trouble with?