Quantcast
Channel: VBForums
Viewing all articles
Browse latest Browse all 15162

Retrieving the correct Identity number during an Add and cascade to child dataadapter

$
0
0
Been trying to modify a code bank submission by jmc which shows how to do a parent/child form with autonumbers using Access database. I'm using a Sql Server mdf database.
https://www.vbforums.com/showthread....ert&highlight=

The problem is, if I delete the last parent record from the database (say the ID was 61, so in the datatable the last ID is 60). The next time I do an ADD, the parent ID will show as 61 in the datagridview and when I ADD a child the parent ID cascades to the child just fine. But when I do a SAVE, the parent ID is reset to 62 (or some other higher number depending on how many have been deleted) in the parent table and that change is not cascaded to the child table, the child record parent ID will remain 61. Which causes constraint problems

I've tried retrieving the the Scope_Identity during the insert and also tried during the dataadapter rowupdate. Also tried manually setting up the Autoincrement.

I thought that with Sql Server the Next Identity would be retrieved automatically. It does retrieve a number but not the correct one.

I know this is a lot of code. I hope what I'm doing wrong or not doing will be obvious and you wont have to go through all the code.


Code:

Imports System.Data.SqlClient

Public Class Form4

#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 SqlConnection(My.Settings.BooksDBConnectionString)

    ''' <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 SqlDataAdapter("SELECT BookId, bookName From Books",
                                                                connection) With {.MissingSchemaAction = MissingSchemaAction.AddWithKey}
    Private WithEvents childDataAdapter As New SqlDataAdapter("SELECT ReserveId, BookId, ReserveName From Reserve",
                                                                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 sqlCommandBuilder(Me.parentDataAdapter) With {.QuotePrefix = "[",
                                                                                        .QuoteSuffix = "]"}
    Private childCommandBuilder As New sqlCommandBuilder(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()

            'parentDataAdapter.InsertCommand = parentCommandBuilder.GetInsertCommand()
            'parentDataAdapter.InsertCommand.CommandText += "; Select Scope_Identity()"

        Catch ex As Exception
            MessageBox.Show(ex.ToString(), "Error")
        End Try
    End Sub

    Private Sub parentDataAdapter_RowUpdated(sender As Object, e As SqlRowUpdatedEventArgs) 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 SqlCommand("SELECT SCOPE_IDENTITY()", Me.connection)
        '        'Update the ID of the local row.
        '        e.Row("BookID") = 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 SqlRowUpdatedEventArgs) 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 SqlCommand("SELECT @IDENTITY", Me.connection)
        '        Using command As New SqlCommand("SELECT SCOPE_IDENTITY()", Me.connection)
        '            Update the ID of the local row.
        '        e.Row("ReserveID") = 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, "Books")
            Me.childDataAdapter.Update(Me.data, "Reserve")
        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, "Books")
        Me.childDataAdapter.FillSchema(Me.data, SchemaType.Source, "Reserve")
    End Sub

    ''' <summary>
    ''' Configure the auto-increment properties of the ID columns of both tables.
    ''' </summary>
    Private Sub ConfigureAutoIncrements()
        ConfigureAutoIncrement(Me.data.Tables("Books").Columns("BookID"))
        ConfigureAutoIncrement(Me.data.Tables("Reserve").Columns("ReserveID"))
    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("Books").Columns("BookID"),
                              Me.data.Tables("Reserve").Columns("BookID"),
                              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, "Books")
        Me.childDataAdapter.Fill(Me.data, "Reserve")
    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("Books")
        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

Just in case here is the table definition
Code:

CREATE TABLE [dbo].[Books] (
    [BookId]  INT          IDENTITY (1, 1) NOT NULL,
    [BookName] VARCHAR (50) NULL,
    [Author]  VARCHAR (50) NULL,
    [Copies]  INT          NULL,
    PRIMARY KEY CLUSTERED ([BookId] ASC)
);


Viewing all articles
Browse latest Browse all 15162


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>