Mise à jour hiérarchique dans un développement .NET Framework

Remarque

Les jeux de données et les classes associées sont des technologies .NET Framework héritées qui datent du début des années 2000. Elles permettent aux applications d’utiliser des données en mémoire pendant que les applications sont déconnectées de la base de données. Elles sont particulièrement utiles aux applications qui permettent aux utilisateurs de modifier des données, et de rendre ces changements persistants dans la base de données. Même si les jeux de données sont une technologie très efficace, nous vous recommandons d’utiliser Entity Framework Core pour les nouvelles applications .NET. Entity Framework offre un moyen plus naturel d’utiliser des données tabulaires en tant que modèles objet. De plus, il présente une interface de programmation plus simple.

La mise à jour hiérarchique désigne le processus d’enregistrement des données mises à jour (d’un jeu de données contenant au moins deux tables associées) dans une base de données tout en conservant les règles d’intégrité référentielle. L’intégrité référentielle désigne les règles de cohérence fournies par les contraintes d’une base de données, qui contrôlent le comportement de l’insertion, de la mise à jour et de la suppression d’enregistrements associés. Par exemple, c’est l’intégrité référentielle qui applique la création d’un enregistrement client avant d’autoriser la création de commandes pour ce client. Pour plus d’informations sur les relations dans les jeux de données, consultez Relations dans les jeux de données.

La fonctionnalité de mise à jour hiérarchique utilise un TableAdapterManager pour gérer les TableAdapters dans un jeu de données typé. Le composant TableAdapterManager est une classe générée par Visual Studio, et non un type .NET. Quand vous faites glisser une table de la fenêtre Sources de données vers une page Windows Form ou WPF, Visual Studio ajoute une variable de type TableAdapterManager au formulaire ou à la page, et vous la voyez dans le concepteur, dans la barre d’état des composants. Pour plus d’informations sur la classe TableAdapterManager, consultez la section Informations de référence sur TableAdapterManager de TableAdapters.

Par défaut, un jeu de données traite les tables associées comme des « relations uniquement », ce qui signifie qu’il n’applique pas de contraintes de clé étrangère. Vous pouvez modifier ce paramètre au moment de la conception en utilisant le Concepteur de jeux de données. Sélectionnez la ligne de relation entre deux tables pour afficher la boîte de dialogue Relation. Les changements que vous faites ici déterminent comment le TableAdapterManager se comporte quand il renvoie les changements des tables associées dans la base de données.

Activer la mise à jour hiérarchique dans un jeu de données

Par défaut, la mise à jour hiérarchique est activée pour tous les nouveaux jeux de données ajoutés ou créés dans un projet. Activez ou désactivez la mise à jour hiérarchique en définissant la propriété Mise à jour hiérarchique d’un jeu de données typé dans le jeu de données sur True ou False :

Paramètre de mise à jour hiérarchique

Créer une relation entre des tables

Pour créer une relation entre deux tables, dans le Concepteur de jeux de données, sélectionnez la barre de titre de chaque table, puis cliquez avec le bouton droit et sélectionnez Ajouter une relation.

Menu Ajouter une relation de la mise à jour hiérarchique

Comprendre les contraintes de clé étrangère, les mises à jour et les suppressions en cascade

Vous devez bien comprendre comment les contraintes de clé étrangère et le comportement en cascade dans la base de données sont créés dans le code du jeu de données généré.

Par défaut, les tables de données d’un jeu de données sont générées avec des relations (DataRelation) qui correspondent aux relations dans la base de données. Toutefois, la relation dans le jeu de données n’est pas générée comme une contrainte de clé étrangère. La DataRelation est configurée comme une Relation uniquement sans UpdateRule ou DeleteRule en vigueur.

Par défaut, les mises à jour en cascade et les suppressions en cascade sont désactivées même si la relation de base de données est définie avec des mises à jour en cascade ou des suppressions en cascade activées. Par exemple, si vous créez un client et une commande, puis que vous essayez d’enregistrer les données, vous pouvez avoir un conflit avec les contraintes de clé étrangère définies dans la base de données. Pour plus d’informations, consultez Désactivation des contraintes pendant le remplissage d’un jeu de données.

Définir l’ordre d’exécution des mises à jour

La définition de l’ordre d’exécution des mises à jour définit l’ordre des insertions, mises à jour et suppressions individuelles nécessaires pour enregistrer toutes les données modifiées dans toutes les tables d’un jeu de données. Quand la mise à jour hiérarchique est activée, les insertions sont effectuées en premier, puis les mises à jour, puis les suppressions. TableAdapterManager fournit une propriété UpdateOrder qui peut être définie pour effectuer d’abord les mises à jour, puis les insertions, puis les suppressions.

Notes

Notez que l’ordre de mise à jour est complètement inclusif. Autrement dit, quand des mises à jour sont effectuées, les insertions, puis les suppressions sont effectuées sur toutes les tables du jeu de données.

Pour définir la propriété UpdateOrder, après avoir fait glisser des éléments de la fenêtre Sources de données vers un formulaire, sélectionnez TableAdapterManager dans la barre d’état des composants, puis définissez la propriété UpdateOrder dans la fenêtre Propriétés.

Créer une copie de sauvegarde d’un jeu de données avant d’effectuer une mise à jour hiérarchique

Quand vous enregistrez des données (en appelant la méthode TableAdapterManager.UpdateAll()), TableAdapterManager tente de mettre à jour les données pour chaque table dans une seule transaction. Si une partie de la mise à jour d’une table échoue, l’intégralité de la transaction est restaurée. Dans la plupart des cas, la restauration renvoie votre application à son état d’origine.

Toutefois, vous pouvez parfois restaurer le jeu de données à partir de la copie de sauvegarde. Cela peut se produire par exemple quand vous utilisez des valeurs d’incrémentation automatique. Par exemple, si une opération d’enregistrement échoue, les valeurs d’incrémentation automatique ne sont pas réinitialisées dans le jeu de données, et le jeu de données continue à créer des valeurs d’incrémentation automatique. Cela crée un écart dans la numérotation qui peut ne pas être acceptable dans votre application. Quand ce comportement pose un problème, TableAdapterManager fournit une propriété BackupDataSetBeforeUpdate qui remplace le jeu de données existant par une copie de sauvegarde en cas d’échec de la transaction.

Notes

La copie de sauvegarde est uniquement en mémoire pendant l’exécution de la méthode TableAdapterManager.UpdateAll. Par conséquent, il n’y a pas d’accès programmatique à ce jeu de données de sauvegarde, car il remplace le jeu de données d’origine, ou sort de l’étendue dès que l’exécution de la méthode TableAdapterManager.UpdateAll est terminée.

Modifier le code d’enregistrement généré pour effectuer la mise à jour hiérarchique

Enregistrez les modifications des tables de données associées du dataset dans la base de données en appelant la méthode TableAdapterManager.UpdateAll et en passant le nom du dataset contenant les tables associées. Par exemple, exécutez la méthode TableAdapterManager.UpdateAll(NorthwindDataset) pour envoyer les mises à jour de toutes les tables de NorthwindDataset à la base de données principale.

Une fois que vous avez déposé des éléments provenant de la fenêtre Sources de données, du code est ajouté automatiquement à l’événement Form_Load pour remplir chaque table (les méthodes TableAdapter.Fill). Du code est également ajouté à l’événement Click du bouton Enregistrer du BindingNavigator pour enregistrer les données du dataset dans la base de données (la méthode TableAdapterManager.UpdateAll).

Le code d'enregistrement généré contient également une ligne de code qui appelle la méthode CustomersBindingSource.EndEdit. Plus particulièrement, elle appelle la méthode EndEdit du premier BindingSource ajouté au formulaire. En d’autres termes, ce code est généré seulement pour la première table déplacée à partir de la fenêtre Sources de données dans le formulaire. L'appel de EndEdit valide toutes les modifications en cours de tous les contrôles liés aux données modifiés. Par conséquent, si un contrôle lié aux données a encore le focus et que vous cliquez sur le bouton Enregistrer, toutes les modifications en attente dans ce contrôle sont validées avant l’enregistrement réel (la méthode TableAdapterManager.UpdateAll).

Notes

Le Concepteur de jeux de données ajoute seulement le code BindingSource.EndEdit pour la première table déposée dans le formulaire. Par conséquent, vous devez ajouter une ligne de code pour appeler la méthode BindingSource.EndEdit pour chaque table associée dans le formulaire. Dans cette procédure pas à pas, cela signifie que vous devez ajouter un appel à la méthode OrdersBindingSource.EndEdit.

  1. Double-cliquez sur le bouton Enregistrer du BindingNavigator pour ouvrir Form1 dans l’éditeur de code.

  2. Ajoutez une ligne de code pour appeler la méthode OrdersBindingSource.EndEdit après la ligne appelant la méthode CustomersBindingSource.EndEdit. Le code de l’événement Click du bouton Enregistrer doit ressembler à ce qui suit :

    this.Validate();
    this.customersBindingSource.EndEdit();
    this.ordersBindingSource.EndEdit();
    this.tableAdapterManager.UpdateAll(this.northwindDataSet);
    

Outre la validation des modifications d'une table enfant associée avant l'enregistrement des données dans une base de données, vous devez aussi peut-être valider les enregistrements parents récemment créés avant d'ajouter de nouveaux enregistrement enfants à un dataset. En d’autres termes, vous devez peut-être ajouter le nouvel enregistrement parent (Customer) au jeu de données avant que les contraintes de clé étrangère ne permettent d’ajouter de nouveaux enregistrements enfants (Orders) au jeu de données. Pour ce faire, vous pouvez utiliser l'événement BindingSource.AddingNew enfant.

Notes

L’obligation de commiter les nouveaux enregistrements parents dépend du type de contrôle utilisé pour la liaison à votre source de données. Dans cette procédure pas à pas, vous utilisez des contrôles individuels pour la liaison à la table parente. Cela nécessite du code supplémentaire pour commiter le nouvel enregistrement parent. Si les enregistrements parents sont affichés à la place dans un contrôle de liaison complexe comme DataGridView, cet appel de EndEdit supplémentaire pour l’enregistrement parent n’est pas nécessaire. En effet, la fonctionnalité sous-jacente de liaison aux données du contrôle gère la validation des nouveaux enregistrements.

Pour ajouter du code afin de valider les enregistrements parents dans le dataset avant d’ajouter de nouveaux enregistrements enfants

  1. Créez un gestionnaire d'événements pour l'événement OrdersBindingSource.AddingNew.

    • Ouvrez Form1 en mode Création, sélectionnez OrdersBindingSource dans la barre d’état des composants, sélectionnez Événements dans la fenêtre Propriétés, puis double-cliquez sur l’événement AddingNew.
  2. Ajoutez une ligne de code au gestionnaire d’événements qui appelle la méthode CustomersBindingSource.EndEdit. Le code du gestionnaire d'événements OrdersBindingSource_AddingNew doit ressembler à ce qui suit :

    this.customersBindingSource.EndEdit();
    

Informations de référence sur TableAdapterManager

Par défaut, une classe TableAdapterManager est générée quand vous créez un jeu de données qui contient des tables associées. Pour empêcher la génération de la classe, définissez la valeur de la propriété Hierarchical Update du jeu de données sur false. Quand vous faites glisser sur l’aire de conception d’une page Windows Form ou WPF une table qui a une relation, Visual Studio déclare une variable membre de la classe. Si vous n’utilisez pas de liaison de données, vous devez déclarer manuellement la variable.

La classe TableAdapterManager n’est pas un type .NET. Par conséquent, vous ne pouvez pas le rechercher dans la documentation. Il est créé au moment de la conception dans le cadre du processus de création du jeu de données.

Voici les méthodes et propriétés fréquemment utilisées de la classe TableAdapterManager :

Membre Description
Méthode UpdateAll Enregistre toutes les données de toutes les tables de données.
Propriété BackUpDataSetBeforeUpdate Détermine s’il faut créer une copie de sauvegarde du jeu de données avant d’exécuter la méthode TableAdapterManager.UpdateAll.Boolean.
Propriété tableNameTableAdapter Représente un TableAdapter. Le TableAdapterManager généré contient une propriété pour chaque TableAdapter qu’il gère. Par exemple, un jeu de données avec des tables Customers et Orders est généré avec un TableAdapterManager qui contient les propriétés CustomersTableAdapter et OrdersTableAdapter.
Propriété UpdateOrder Contrôle l’ordre des commandes individuelles d’insertion, de mise à jour et de suppression. Définissez-la sur une des valeurs de l’énumération TableAdapterManager.UpdateOrderOption.

Par défaut, UpdateOrder est défini sur InsertUpdateDelete. Cela signifie que les insertions, puis les mises à jour, puis les suppressions sont effectuées pour toutes les tables du jeu de données.