Mise à jour en cascade (Tables Mère/Fille) avec colonnes auto-incrementés

Résolu
BasicZx81 Messages postés 140 Date d'inscription samedi 5 mars 2011 Statut Membre Dernière intervention 13 août 2013 - 12 févr. 2012 à 10:32
BasicZx81 Messages postés 140 Date d'inscription samedi 5 mars 2011 Statut Membre Dernière intervention 13 août 2013 - 13 févr. 2012 à 10:43
Bonjour à tous,
j'ai suivi l'excellent tuto de J.M Rabilloud sur Devellopez.com pourtant je me heurte à un problème :
D'aprés le Tuto il est tout à fait possible de bénéficier des relations entre les tables Mères/Filles dans un Dataset dans le but d'obtenir des mises en jours en cascade dans ce Dataset (Exemple si je supprime un enregistrement de la table mère, tous les enregistrements liés par une clé étrangère de la table fille vont se supprimer automatiquement sans aucune ligne de code). Je peux donc reproduire les contraintes et comportement de la base source dans mon Dataset.

Dans le cas de colonne Auto-incrementé c'est la base de données source qui fourni les numéros correspondant aux clés primaire de chaque table (Mère/Fille) au moment de l'enregistrement du Dataset.
Mon premier essai à fonctionné mais sans les relations et sans les clés primaire, c'est à dire que je laisse les champs auto-incrementé vide et je recupére ces numéros au moment de l'enregistrement grace à la commande "SELECT @@IDENTITY". Donc ce premier essai fonctionne mais sans bénéficier des mises à jours en cascade du Dataset.
A partir de là j'ai un doute, j'espère que vous me corrigerais : Pour créer une relation dans mon Dataset cela m'oblige à créer une clé primaire sur la table Mère (sur la colonne correspondante à la colonne source auto-incrementé), ensuite je declare une relation entre 2 colonnes des tables Mère/Fille. C'est donc ce que j'ai fait lors de mon deuxieme essai. Problème : Après la mise en place des clés primaire les colonnes correspondante n'acceptent plus les champs vide et m'oblige à renseigner les colonnes un numéro quelconque mais ça plante au moment d'enregistrer (je suppose que la source n'accepte pas autre chose que les champs vide puisque c'est elle qui fourni les numéros).
Y a t'il un moyen pour que cela fonctionne comme lors de mon premier essai mais avec les relations et mises à jour en cascade ?

LE CODE POUR CHARGER LE DATASET :
Private sub Load Dataset()
Call OpenConnection()
        'Initialisation de la chaîne contenant l'instruction SQL
        Dim sqlMère As String = "SELECT * FROM MERE"
        DBDataAdapterMère = New OleDbDataAdapter(sqlMère, DBconnection)
        Dim sqlFille As String = "SELECT * FROM FILLE"
        DBDataAdapterFille = New OleDbDataAdapter(sqlFille, DBconnection)

        objDBDataSet.Clear()
        objDBDataSet.EnforceConstraints = False
        'Avec l'aide de la propriété Fill du DataAdapter charger le DataSet
         DBDataAdapterMère.Fill(objDBDataSet, "MERE")
        ' Ajout de la contrainte de clé primaire :
        Dim maTable As DataTable = objDBDataSet.Tables("MERE")
        maTable.Constraints.Add("pk1", maTable.Columns("ID_MERE"), True)

        DBDataAdapterFille.Fill(objDBDataSet, "FILLE")
        maTable = objDBDataSet.Tables("FILLE")
        maTable.Constraints.Add("pk1", maTable.Columns("ID_FILLE"), True)

        ' Ajout de la relation entre la table MERE et FILLE :
        Dim DRelat As New DataRelation("Rl1", objDBDataSet.Tables("MERE").Columns("ID_MERE"), objDBDataSet.Tables("FILLE").Columns("ID_MERE"), True)
        objDBDataSet.Relations.Add(DRelat)
        Dim FKCont As ForeignKeyConstraint = maTable.Constraints.Item(1)
        With FKCont
            .AcceptRejectRule = AcceptRejectRule.Cascade
            .DeleteRule = Rule.Cascade
            .UpdateRule = Rule.Cascade
        End With
        objDBDataSet.EnforceConstraints = True

        Call CloseConnection()
End Sub


LE CODE POUR RECUPERER LES NUMEROS AUTO-INCREMENTES DE LA BASE SOURCE :
    Private Sub DBAdapterMère_RowUpdate(ByVal sender As Object, ByVal e As System.Data.OleDb.OleDbRowUpdatedEventArgs)
        If e.StatementType = StatementType.Insert Then
            Dim MaComRecup As New OleDb.OleDbCommand("SELECT @@IDENTITY", e.Command.Connection)
            'MaComRecup.Transaction = MaTransaction
            e.Row.Item("ID_MERE") = MaComRecup.ExecuteScalar
            e.Row.AcceptChanges()
        End If
    End Sub


LE CODE POUR ENREGISTRER LE DATASET :
    Public Sub SaveDataset()
        Call OpenConnection()

        Dim bldr10 = New OleDbCommandBuilder(DBDataAdapterMère)
        Dim MéreDelete As OleDbCommand = bldr10.GetDeleteCommand()
        Dim MéreInsert As OleDbCommand = bldr10.GetInsertCommand
        Dim MéreUpdate As OleDbCommand = bldr10.GetUpdateCommand

        Dim bldr20 = New OleDbCommandBuilder(DBDataAdapterFille)
        Dim FilleDelete As OleDbCommand = bldr20.GetDeleteCommand()
        Dim FilleInsert As OleDbCommand = bldr20.GetInsertCommand
        Dim FilleUpdate As OleDbCommand = bldr20.GetUpdateCommand

        ' On instancie une transaction (si besoin)
        'MaTransaction = DBconnection.BeginTransaction()

        'MèreDelete.Transaction = MaTransaction
        'MèreInsert.Transaction = MaTransaction
        'MèreUpdate.Transaction = MaTransaction
        'FilleDelete.Transaction = MaTransaction
        'FilleInsert.Transaction = MaTransaction
        'FilleUpdate.Transaction = MaTransaction


        Try
            ' IL faut effectuer les commandes dans l'ordre :
            ' 1 - Envoyer les enregistrements ajoutés à la table mère :
            DBDataAdapterMére.Update(objDBDataSet.Tables("MERE").Select(Nothing, Nothing, DataViewRowState.Added))
            ' 2 - Envoyer les enregistrements ajoutés à la table fille :
            DBDataAdapterFille.Update(objDBDataSet.Tables("FILLE").Select(Nothing, Nothing, DataViewRowState.Added))
            ' 3 - Envoyer les enregistrements modifiés à la table mère :
            DBDataAdapterMére.Update(objDBDataSet.Tables("MERE").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))
            ' 4 - Envoyer les enregistrements modifiés à la table fille :
            DBDataAdapterFille.Update(objDBDataSet.Tables("FILLE").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))
            ' 5 - Envoyer les enregistrements supprimés à la table fille :
            DBDataAdapterFille.Update(objDBDataSet.Tables("FILLE").Select(Nothing, Nothing, DataViewRowState.Deleted))
            ' 5 - Envoyer les enregistrements supprimés à la table Mére :
            DBDataAdapterMére.Update(objDBDataSet.Tables("MERE").Select(Nothing, Nothing, DataViewRowState.Deleted))
     

            'MaTransaction.Commit()
            objDBDataSet.AcceptChanges()
            MessageBox.Show("L'enregistrement à réussi.", "Enregistrement réussi.", MessageBoxButtons.OK, MessageBoxIcon.Information)
        Catch ex As DBConcurrencyException
            'MaTransaction.Rollback()
            objDBDataSet.RejectChanges()
            MessageBox.Show("Modifications rejetées par la base.", "Echec de l'enregistrement.", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        Call CloseConnection()
    End Sub

1 réponse

BasicZx81 Messages postés 140 Date d'inscription samedi 5 mars 2011 Statut Membre Dernière intervention 13 août 2013
13 févr. 2012 à 10:43
Après la mise en place des clés primaire les colonnes correspondante n'acceptent plus les champs vide et m'oblige à renseigner les colonnes avec un numéro quelconque mais ça plante au moment d'enregistrer (je suppose que la source n'accepte pas autre chose que les champs vide puisque c'est elle qui fourni les numéros).


Je me réponds à moi-même, ça pourra servir à d'autres personnes.
La solution est de créer des colonnes auto-incrémentées aussi dans le Dataset, cela pour plusieurs raisons :
- Pour éviter de laisser des champs vide (ceux-ci ne sont plus acceptés dés lors que l'on veux paramétrer une relation entre 2 tables qui utilisent forcement des clés primaire dans son fonctionnement.)
- Ensuite pour le fonctionnement interne de l'application qui à besoin de ces numéros de clés pour pouvoir liées les enregistrements de 2 tables. (Ces numéros ne sont que provisoire puisque au moment d'enregistrer ils sont remplacés et attribués par la base source)

Changer la base et l’incrémentation des colonnes IncrementSeed et IncrementStep à -1 (Ne me demandé pas pourquoi il faut mettre des valeurs négative je n'ai pas encore compris pourquoi mais il parait qu'il y a une raison et comme cela fonctionne je le laisse comme ça).
PS : j'espère que le code est assez propre, il y peut-être d'autre facon de faire mais l'essentiel est d'arriver au résultat souhaité.

Le bout de code qui sert à créer les colonnes incrémentées et les relations nécessaires :
   Private Sub InitDataSet()
        ' Création d'un Dataset Typé au demarrage de l'application
        ' On crée juste les colonnes et relations dont on à besoin
        objDBDataSet = New DataSet

        Dim MaTable As New DataTable("MERE")
        MaTable.Columns.Add("ID_MERE", Type.GetType("System.Int32")).AutoIncrement = True
        With MaTable.Columns("ID_MERE")
            .AutoIncrementSeed = -1
            .AutoIncrementStep = -1
            .Unique = True
            .AllowDBNull = False
        End With
       objDBDataSet.Tables.Add(MaTable)

        MaTable = New DataTable("FILLE")
        MaTable.Columns.Add("ID_FILLE", Type.GetType("System.Int32")).AutoIncrement = True
        With MaTable.Columns("ID_FILLE")
            .AutoIncrementSeed = -1
            .AutoIncrementStep = -1
            .Unique = True
            .AllowDBNull = False
        End With
        
        MaTable.Columns.Add("ID_MERE", Type.GetType("System.Int32"))

        objDBDataSet.Tables.Add(MaTable)

        ' Ajout de la relation entre la table MERE et FILLE :
        Dim DRelat As New DataRelation("Rl1", objDBDataSet.Tables("MERE").Columns("ID_MERE"), objDBDataSet.Tables("FILLE").Columns("ID_MERE"), True)
        objDBDataSet.Relations.Add(DRelat)
        Dim FKCont As ForeignKeyConstraint = MaTable.Constraints.Item(1)
        With FKCont
            .AcceptRejectRule = AcceptRejectRule.Cascade
            .DeleteRule = Rule.Cascade
            .UpdateRule = Rule.Cascade
        End With

        
    End Sub


Remplacer le code du LoadDataSet par celui-ci :
    Public Sub LoadDataSet()

        Call OpenConnection()
        'Initialisation de la chaîne contenant l'instruction SQL
        Dim sqlMERE As String = "SELECT * FROM " & MERE
        DBDataAdapterMére = New OleDbDataAdapter(sqlMERE, DBconnection)
        Dim sqlFILLE As String = "SELECT * FROM " & FILLE
        DBDataAdapterFille = New OleDbDataAdapter(sqlFILLE, DBconnection)
        
        objDBDataSet.Clear()
        objDBDataSet.EnforceConstraints = False
        'Avec l'aide de la propriété Fill du DataAdapter charger le DataSet
        DBDataAdapterMére.Fill(objDBDataSet, "MERE")
        DBDataAdapterTaches.Fill(objDBDataSet, "FILLE")

        objDBDataSet.EnforceConstraints = True

        Call CloseConnection()

        AddHandler DBDataAdapterMére.RowUpdated, AddressOf DBAdapterMére_RowUpdate
       
    End Sub
3
Rejoignez-nous