Partager via


Inclusion de modifications d’une base de données dans une transaction (VB)

par Scott Mitchell

Télécharger le PDF

Ce tutoriel est le premier d’une série de quatre qui examine la mise à jour, la suppression et l’insertion de lots de données. Dans ce tutoriel, nous apprenons comment les transactions de base de données permettent d’effectuer des modifications par lots en tant qu’opération atomique, ce qui garantit que toutes les étapes réussissent ou que toutes les étapes échouent.

Introduction

Comme nous l’avons vu à partir du didacticiel Vue d’ensemble de l’insertion, de la mise à jour et de la suppression de données, GridView fournit une prise en charge intégrée de la modification et de la suppression au niveau des lignes. En quelques clics, il est possible de créer une interface de modification de données riche sans écrire de ligne de code, tant que vous vous contentez de la modification et de la suppression par ligne. Toutefois, dans certains scénarios, cela est insuffisant et nous devons fournir aux utilisateurs la possibilité de modifier ou de supprimer un lot d’enregistrements.

Par exemple, la plupart des clients de messagerie web utilisent une grille pour répertorier chaque message où chaque ligne inclut une case à cocher ainsi que les informations de l’e-mail (objet, expéditeur, etc.). Cette interface permet à l’utilisateur de supprimer plusieurs messages en les vérifiant, puis en cliquant sur un bouton Supprimer les messages sélectionnés. Une interface d’édition par lots est idéale dans les situations où les utilisateurs modifient généralement de nombreux enregistrements différents. Au lieu de forcer l’utilisateur à cliquer sur Modifier, à apporter sa modification, puis à cliquer sur Mettre à jour pour chaque enregistrement qui doit être modifié, une interface d’édition par lots restitue chaque ligne avec son interface d’édition. L’utilisateur peut rapidement modifier l’ensemble des lignes qui doivent être modifiées, puis enregistrer ces modifications en cliquant sur un bouton Mettre à jour tout. Dans cet ensemble de tutoriels, nous allons examiner comment créer des interfaces pour insérer, modifier et supprimer des lots de données.

Lors de l’exécution d’opérations par lot, il est important de déterminer s’il doit être possible que certaines opérations du lot réussissent alors que d’autres échouent. Prenons l’exemple d’une interface de suppression par lots : que doit-il se passer si le premier enregistrement sélectionné est supprimé correctement, mais que le deuxième enregistrement échoue, par exemple, en raison d’une violation de contrainte de clé étrangère ? La suppression du premier enregistrement doit-elle être restaurée ou est-il acceptable que le premier enregistrement reste supprimé ?

Si vous souhaitez que l’opération de traitement par lot soit traitée comme une opération atomique, une opération dans laquelle toutes les étapes réussissent ou toutes les étapes échouent, la couche d’accès aux données doit être augmentée pour inclure la prise en charge des transactions de base de données. Les transactions de base de données garantissent l’atomicité pour l’ensemble d’instructions INSERT, UPDATEet DELETE exécutées sous l’égide de la transaction et sont une fonctionnalité prise en charge par la plupart des systèmes de base de données modernes.

Dans ce tutoriel, nous allons voir comment étendre la dal pour utiliser des transactions de base de données. Les didacticiels suivants examineront l’implémentation de pages web pour l’insertion, la mise à jour et la suppression d’interfaces par lots. Commençons !

Notes

Lors de la modification de données dans une transaction par lot, l’atomicité n’est pas toujours nécessaire. Dans certains scénarios, il peut être acceptable que certaines modifications de données réussissent et que d’autres du même lot échouent, par exemple lors de la suppression d’un ensemble d’e-mails d’un client de messagerie web. En cas d’erreur de base de données au cours du processus de suppression, il est probablement acceptable que les enregistrements traités sans erreur restent supprimés. Dans ce cas, le DAL n’a pas besoin d’être modifié pour prendre en charge les transactions de base de données. Toutefois, il existe d’autres scénarios d’opération par lots dans lesquels l’atomicité est vitale. Lorsqu’un client déplace ses fonds d’un compte bancaire à un autre, deux opérations doivent être effectuées : les fonds doivent être déduits du premier compte, puis ajoutés au second. Bien que la banque n’ait pas d’esprit que la première étape réussisse, mais que la deuxième étape échoue, ses clients seraient naturellement bouleversés. Je vous encourage à suivre ce tutoriel et à implémenter les améliorations apportées au DAL pour prendre en charge les transactions de base de données, même si vous ne prévoyez pas de les utiliser dans le lot d’interfaces d’insertion, de mise à jour et de suppression que nous allons créer dans les trois didacticiels suivants.

Vue d’ensemble des transactions

La plupart des bases de données prennent en charge les transactions, ce qui permet de regrouper plusieurs commandes de base de données en une seule unité logique de travail. Les commandes de base de données qui composent une transaction sont garanties atomiques, ce qui signifie que toutes les commandes échouent ou toutes réussissent.

En général, les transactions sont implémentées par le biais d’instructions SQL à l’aide du modèle suivant :

  1. Indique le début d’une transaction.
  2. Exécutez les instructions SQL qui composent la transaction.
  3. En cas d’erreur dans l’une des instructions de l’étape 2, restaurez la transaction.
  4. Si toutes les instructions de l’étape 2 se terminent sans erreur, validez la transaction.

Les instructions SQL utilisées pour créer, valider et restaurer la transaction peuvent être entrées manuellement lors de l’écriture de scripts SQL ou de la création de procédures stockées, ou par le biais de moyens programmatiques à l’aide de ADO.NET ou des classes de l’espace deSystem.Transactions noms. Dans ce tutoriel, nous allons examiner uniquement la gestion des transactions à l’aide de ADO.NET. Dans un prochain tutoriel, nous verrons comment utiliser des procédures stockées dans la couche d’accès aux données. Nous allons alors explorer les instructions SQL pour créer, restaurer et valider des transactions. En attendant, consultez Gestion des transactions dans SQL Server procédures stockées pour plus d’informations.

Notes

La TransactionScope classe dans l’espace System.Transactions de noms permet aux développeurs d’encapsuler par programmation une série d’instructions dans l’étendue d’une transaction et inclut la prise en charge des transactions complexes qui impliquent plusieurs sources, telles que deux bases de données différentes ou même des types hétérogènes de magasins de données, tels qu’une base de données Microsoft SQL Server, une base de données Oracle et un service Web. J’ai décidé d’utiliser des transactions ADO.NET pour ce didacticiel au lieu de la TransactionScope classe, car ADO.NET est plus spécifique pour les transactions de base de données et, dans de nombreux cas, est beaucoup moins gourmand en ressources. En outre, dans certains scénarios, la TransactionScope classe utilise Microsoft Distributed Transaction Coordinator (MSDTC). Les problèmes de configuration, d’implémentation et de performances qui entourent MSDTC en font un sujet plutôt spécialisé et avancé et au-delà de la portée de ces tutoriels.

Lorsque vous utilisez le fournisseur SqlClient dans ADO.NET, les transactions sont lancées par le biais d’un appel à la méthode de classeSqlConnection sBeginTransaction, qui retourne un SqlTransaction objet . Les instructions de modification des données qui constituent la transaction sont placées dans un try...catch bloc. Si une erreur se produit dans une instruction du bloc, l’exécution try est transférée au catch bloc où la transaction peut être restaurée via la méthode de l’objetRollbackSqlTransaction. Si toutes les instructions se terminent correctement, un appel à la méthode de l’objet SqlTransactionCommit à la fin du try bloc valide la transaction. L’extrait de code suivant illustre ce modèle.

' Create the SqlTransaction object
Dim myTransaction As SqlTransaction = SqlConnectionObject.BeginTransaction();
Try
    '
    ' ... Perform the database transaction�s data modification statements...
    '
    ' If we reach here, no errors, so commit the transaction
    myTransaction.Commit()
Catch
    ' If we reach here, there was an error, so rollback the transaction
    myTransaction.Rollback()
    Throw
End Try

Par défaut, les TableAdapters d’un DataSet typé n’utilisent pas de transactions. Pour assurer la prise en charge des transactions, nous devons augmenter les classes TableAdapter afin d’inclure des méthodes supplémentaires qui utilisent le modèle ci-dessus pour effectuer une série d’instructions de modification de données dans l’étendue d’une transaction. À l’étape 2, nous allons voir comment utiliser des classes partielles pour ajouter ces méthodes.

Étape 1 : Création des pages web Working with Batched Data

Avant de commencer à explorer comment augmenter le DAL pour prendre en charge les transactions de base de données, prenons d’abord un moment pour créer les pages web ASP.NET dont nous aurons besoin pour ce didacticiel et les trois qui suivent. Commencez par ajouter un nouveau dossier nomméBatchData, puis ajoutez les pages ASP.NET suivantes, en associant chaque page à la Site.master page master.

  • Default.aspx
  • Transactions.aspx
  • BatchUpdate.aspx
  • BatchDelete.aspx
  • BatchInsert.aspx

Ajouter les pages ASP.NET pour les didacticiels SqlDataSource-Related

Figure 1 : Ajouter les pages ASP.NET pour les didacticiels SqlDataSource-Related

Comme pour les autres dossiers, Default.aspx utilise le SectionLevelTutorialListing.ascx contrôle utilisateur pour répertorier les didacticiels dans sa section. Par conséquent, ajoutez ce contrôle utilisateur à Default.aspx en le faisant glisser du Explorateur de solutions vers le mode Création de la page.

Ajoutez le contrôle utilisateur SectionLevelTutorialListing.ascx à Default.aspx

Figure 2 : Ajouter le SectionLevelTutorialListing.ascx contrôle utilisateur à Default.aspx (Cliquer pour afficher l’image en taille réelle)

Enfin, ajoutez ces quatre pages en tant qu’entrées au Web.sitemap fichier. Plus précisément, ajoutez le balisage suivant après la Personnalisation du plan de site <siteMapNode>:

<siteMapNode title="Working with Batched Data" 
    url="~/BatchData/Default.aspx" 
    description="Learn how to perform batch operations as opposed to 
                 per-row operations.">
    
    <siteMapNode title="Adding Support for Transactions" 
        url="~/BatchData/Transactions.aspx" 
        description="See how to extend the Data Access Layer to support 
                     database transactions." />
    <siteMapNode title="Batch Updating" 
        url="~/BatchData/BatchUpdate.aspx" 
        description="Build a batch updating interface, where each row in a 
                      GridView is editable." />
    <siteMapNode title="Batch Deleting" 
        url="~/BatchData/BatchDelete.aspx" 
        description="Explore how to create an interface for batch deleting 
                     by adding a CheckBox to each GridView row." />
    <siteMapNode title="Batch Inserting" 
        url="~/BatchData/BatchInsert.aspx" 
        description="Examine the steps needed to create a batch inserting 
                     interface, where multiple records can be created at the 
                     click of a button." />
</siteMapNode>

Après la mise à jour Web.sitemap, prenez un moment pour afficher le site web des tutoriels via un navigateur. Le menu de gauche inclut désormais des éléments pour l’utilisation des didacticiels sur les données par lots.

Le plan de site inclut désormais des entrées pour les didacticiels Utilisation des données par lot

Figure 3 : Le plan de site inclut désormais des entrées pour les didacticiels Utilisation des données par lot

Étape 2 : Mise à jour de la couche d’accès aux données pour prendre en charge les transactions de base de données

Comme nous l’avons vu dans le premier tutoriel, Création d’une couche d’accès aux données, le Jeu de données typé dans notre DAL est composé de DataTables et TableAdapters. Les DataTables contiennent les données tandis que les TableAdapters fournissent la fonctionnalité permettant de lire les données de la base de données dans les DataTables, de mettre à jour la base de données avec les modifications apportées aux DataTables, etc. Rappelez-vous que les TableAdapters fournissent deux modèles pour la mise à jour des données, que j’ai appelé Batch Update et DB-Direct. Avec le modèle De mise à jour par lots, l’objet TableAdapter reçoit un DataSet, un DataTable ou une collection de DataRows. Ces données sont énumérées et, pour chaque ligne insérée, modifiée ou supprimée, le InsertCommand, UpdateCommandou DeleteCommand est exécuté. Avec le modèle DB-Direct, TableAdapter est passé à la place les valeurs des colonnes nécessaires à l’insertion, à la mise à jour ou à la suppression d’un enregistrement unique. La méthode de modèle DB Direct utilise ensuite ces valeurs passées pour exécuter l’instruction , UpdateCommandou DeleteCommand appropriéeInsertCommand.

Quel que soit le modèle de mise à jour utilisé, les méthodes générées automatiquement par TableAdapters n’utilisent pas de transactions. Par défaut, chaque insertion, mise à jour ou suppression effectuée par TableAdapter est traitée comme une seule opération discrète. Par instance, imaginez que le modèle de DB-Direct est utilisé par un certain code dans la BLL pour insérer dix enregistrements dans la base de données. Ce code appelle la méthode TableAdapter Insert dix fois. Si les cinq premières insertions réussissent, mais que la sixième a entraîné une exception, les cinq premiers enregistrements insérés restent dans la base de données. De même, si le modèle De mise à jour par lots est utilisé pour effectuer des insertions, des mises à jour et des suppressions sur les lignes insérées, modifiées et supprimées dans un DataTable, si les premières modifications ont réussi, mais qu’une autre a rencontré une erreur, les modifications antérieures terminées restent dans la base de données.

Dans certains scénarios, nous voulons garantir l’atomicité d’une série de modifications. Pour ce faire, nous devons étendre manuellement l’objet TableAdapter en ajoutant de nouvelles méthodes qui exécutent les InsertCommand, UpdateCommandet DeleteCommand sous l’égide d’une transaction. Dans Création d’une couche d’accès aux données , nous avons examiné l’utilisation de classes partielles pour étendre les fonctionnalités des DataTables dans le DataSet typé. Cette technique peut également être utilisée avec TableAdapters.

Le DataSet Northwind.xsd typé se trouve dans le App_Code sous-dossier du DAL dossier. Créez un sous-dossier dans le DAL dossier nommé TransactionSupport et ajoutez un nouveau fichier de classe nommé ProductsTableAdapter.TransactionSupport.vb (voir la figure 4). Ce fichier contiendra l’implémentation partielle de qui inclut des méthodes pour effectuer des modifications de données à l’aide ProductsTableAdapter d’une transaction.

Ajoutez un dossier nommé TransactionSupport et un fichier de classe nommé ProductsTableAdapter.TransactionSupport.vb

Figure 4 : Ajouter un dossier nommé TransactionSupport et un fichier de classe nommé ProductsTableAdapter.TransactionSupport.vb

Entrez le code suivant dans le ProductsTableAdapter.TransactionSupport.vb fichier :

Imports System.Data
Imports System.Data.SqlClient
Namespace NorthwindTableAdapters
    Partial Public Class ProductsTableAdapter
        Private _transaction As SqlTransaction
        Private Property Transaction() As SqlTransaction
            Get
                Return Me._transaction
            End Get
            Set(ByVal Value As SqlTransaction)
                Me._transaction = Value
            End Set
        End Property
        Public Sub BeginTransaction()
            ' Open the connection, if needed
            If Me.Connection.State <> ConnectionState.Open Then
                Me.Connection.Open()
            End If
            ' Create the transaction and assign it to the Transaction property
            Me.Transaction = Me.Connection.BeginTransaction()
            ' Attach the transaction to the Adapters
            For Each command As SqlCommand In Me.CommandCollection
                command.Transaction = Me.Transaction
            Next
            Me.Adapter.InsertCommand.Transaction = Me.Transaction
            Me.Adapter.UpdateCommand.Transaction = Me.Transaction
            Me.Adapter.DeleteCommand.Transaction = Me.Transaction
        End Sub
        Public Sub CommitTransaction()
            ' Commit the transaction
            Me.Transaction.Commit()
            ' Close the connection
            Me.Connection.Close()
        End Sub
        Public Sub RollbackTransaction()
            ' Rollback the transaction
            Me.Transaction.Rollback()
            ' Close the connection
            Me.Connection.Close()
        End Sub
    End Class
End Namespace

Le Partial mot clé dans la déclaration de classe indique ici au compilateur que les membres ajoutés dans doivent être ajoutés à la classe dans l’espace ProductsTableAdapter de NorthwindTableAdapters noms. Notez l’instruction Imports System.Data.SqlClient en haut du fichier. Étant donné que TableAdapter a été configuré pour utiliser le fournisseur SqlClient, il utilise en interne un SqlDataAdapter objet pour émettre ses commandes à la base de données. Par conséquent, nous devons utiliser la SqlTransaction classe pour commencer la transaction, puis pour la valider ou la restaurer. Si vous utilisez un magasin de données autre que Microsoft SQL Server, vous devez utiliser le fournisseur approprié.

Ces méthodes fournissent les blocs de construction nécessaires au démarrage, à la restauration et à la validation d’une transaction. Ils sont marqués Public, ce qui leur permet d’être utilisés à partir de ProductsTableAdapter, à partir d’une autre classe dans le DAL ou d’une autre couche de l’architecture, comme la BLL. BeginTransactionouvre l’élément interne SqlConnection de TableAdapter (si nécessaire), commence la transaction et l’affecte à la Transaction propriété et attache la transaction aux objets s SqlCommand internesSqlDataAdapter. CommitTransactionet RollbackTransaction appelez les méthodes et Rollback les méthodes de l’objet TransactionCommit, respectivement, avant de fermer l’objet interneConnection.

Étape 3 : Ajout de méthodes pour mettre à jour et supprimer des données sous l’égide d’une transaction

Une fois ces méthodes terminées, nous sommes prêts à ajouter des méthodes à ou au ProductsDataTable BLL qui exécutent une série de commandes sous l’égide d’une transaction. La méthode suivante utilise le modèle Batch Update pour mettre à jour un ProductsDataTable instance à l’aide d’une transaction. Il démarre une transaction en appelant la BeginTransaction méthode, puis utilise un Try...Catch bloc pour émettre les instructions de modification des données. Si l’appel à la méthode s de l’objet AdapterUpdate génère une exception, l’exécution est transférée vers le catch bloc où la transaction sera restaurée et l’exception sera levée de nouveau. Rappelez-vous que la Update méthode implémente le modèle Batch Update en énumérant les lignes du fourni ProductsDataTable et en effectuant les , UpdateCommandet DeleteCommand s nécessairesInsertCommand. Si l’une de ces commandes génère une erreur, la transaction est restaurée, annulant les modifications précédentes apportées pendant la durée de vie de la transaction. Si l’instruction Update se termine sans erreur, la transaction est validée dans son intégralité.

Public Function UpdateWithTransaction _
    (ByVal dataTable As Northwind.ProductsDataTable) As Integer
    
    Me.BeginTransaction()
    Try
        ' Perform the update on the DataTable
        Dim returnValue As Integer = Me.Adapter.Update(dataTable)
        ' If we reach here, no errors, so commit the transaction
        Me.CommitTransaction()
        Return returnValue
    Catch
        ' If we reach here, there was an error, so rollback the transaction
        Me.RollbackTransaction()
        Throw
    End Try
End Function

Ajoutez la UpdateWithTransaction méthode à la ProductsTableAdapter classe via la classe partielle dans ProductsTableAdapter.TransactionSupport.vb. Vous pouvez également ajouter cette méthode à la classe de ProductsBLL la couche logique métier avec quelques modifications syntaxiques mineures. À savoir, le mot clé dans , et doit être remplacé par Adapter (rappelez-vous que Adapter est le nom d’une propriété dans ProductsBLL de type ProductsTableAdapter).Me.RollbackTransaction()Me.CommitTransaction()Me.BeginTransaction()Me

La UpdateWithTransaction méthode utilise le modèle Batch Update, mais une série d’appels DB-Direct peut également être utilisée dans l’étendue d’une transaction, comme le montre la méthode suivante. La DeleteProductsWithTransaction méthode accepte comme entrée un List(Of T) de type Integer, qui sont les ProductID s à supprimer. La méthode lance la transaction via un appel à BeginTransaction , puis, dans le Try bloc, itère dans la liste fournie en appelant la méthode de modèle Delete DB-Direct pour chaque ProductID valeur. Si l’un des appels à Delete échoue, le contrôle est transféré vers le Catch bloc où la transaction est restaurée et l’exception est levée de nouveau. Si tous les appels réussissent Delete , la transaction est validée. Ajoutez cette méthode à la ProductsBLL classe .

Public Sub DeleteProductsWithTransaction _
    (ByVal productIDs As System.Collections.Generic.List(Of Integer))
    
    ' Start the transaction
    Adapter.BeginTransaction()
    Try
        ' Delete each product specified in the list
        For Each productID As Integer In productIDs
            Adapter.Delete(productID)
        Next
        ' Commit the transaction
        Adapter.CommitTransaction()
    Catch
        ' There was an error - rollback the transaction
        Adapter.RollbackTransaction()
        Throw
    End Try
End Sub

Application de transactions entre plusieurs TableAdapters

Le code lié aux transactions examiné dans ce tutoriel permet de traiter plusieurs instructions sur le ProductsTableAdapter comme une opération atomique. Mais que se passe-t-il si plusieurs modifications apportées à différentes tables de base de données doivent être effectuées de manière atomique ? Par instance, lors de la suppression d’une catégorie, nous pouvons d’abord réaffecter ses produits actuels à une autre catégorie. Ces deux étapes de réaffectation des produits et de suppression de la catégorie doivent être exécutées en tant qu’opération atomique. Toutefois, le ProductsTableAdapter inclut uniquement les méthodes de modification de la Products table et le CategoriesTableAdapter inclut uniquement les méthodes de modification de la Categories table. Par conséquent, comment une transaction peut-elle englober les deux TableAdapters ?

L’une des options consiste à ajouter une méthode au nommé DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) et à CategoriesTableAdapter faire en sorte que cette méthode appelle une procédure stockée qui réaffecte les produits et supprime la catégorie dans l’étendue d’une transaction définie dans la procédure stockée. Nous allons voir comment commencer, valider et restaurer des transactions dans des procédures stockées dans un prochain tutoriel.

Une autre option consiste à créer une classe d’assistance dans le DAL qui contient la DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) méthode . Cette méthode crée une instance de et CategoriesTableAdapter leProductsTableAdapter, puis définit ces deux propriétés TableAdapters Connection sur la même SqlConnection instance. À ce stade, l’un des deux TableAdapters initie la transaction avec un appel à BeginTransaction. Les méthodes TableAdapters pour réaffecter les produits et supprimer la catégorie sont appelées dans un Try...Catch bloc avec la transaction validée ou restaurée si nécessaire.

Étape 4 : Ajout de laUpdateWithTransactionméthode à la couche logique métier

À l’étape 3, nous avons ajouté une UpdateWithTransaction méthode au ProductsTableAdapter dans le DAL. Nous devons ajouter une méthode correspondante à la BLL. Bien que la couche de présentation puisse appeler directement le DAL pour appeler la UpdateWithTransaction méthode, ces tutoriels se sont efforcés de définir une architecture en couches qui isole le DAL de la couche de présentation. Par conséquent, il nous incombe de poursuivre cette approche.

Ouvrez le fichier de ProductsBLL classe et ajoutez une méthode nommée UpdateWithTransaction qui appelle simplement la méthode DAL correspondante. Il doit maintenant y avoir deux nouvelles méthodes dans ProductsBLL: UpdateWithTransaction, que vous venez d’ajouter, et DeleteProductsWithTransaction, qui a été ajoutée à l’étape 3.

Public Function UpdateWithTransaction _
    (ByVal products As Northwind.ProductsDataTable) As Integer
    
    Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
    (ByVal productIDs As System.Collections.Generic.List(Of Integer))
    
    ' Start the transaction
    Adapter.BeginTransaction()
    Try
        ' Delete each product specified in the list
        For Each productID As Integer In productIDs
            Adapter.Delete(productID)
        Next
        ' Commit the transaction
        Adapter.CommitTransaction()
    Catch
        ' There was an error - rollback the transaction
        Adapter.RollbackTransaction()
        Throw
    End Try
End Sub

Notes

Ces méthodes n’incluent pas l’attribut attribué à la DataObjectMethodAttribute plupart des autres méthodes de la ProductsBLL classe, car nous allons appeler ces méthodes directement à partir des classes code-behind ASP.NET pages. Rappelez-vous que DataObjectMethodAttribute permet d’indiquer quelles méthodes doivent apparaître dans l’Assistant Configurer la source de données ObjectDataSource et sous l’onglet (SELECT, UPDATE, INSERT ou DELETE). Étant donné que GridView ne prend pas en charge la modification ou la suppression par lots, nous devrons appeler ces méthodes par programmation plutôt que d’utiliser l’approche déclarative sans code.

Étape 5 : Mise à jour atomique des données de base de données à partir de la couche présentation

Pour illustrer l’effet de la transaction lors de la mise à jour d’un lot d’enregistrements, créons une interface utilisateur qui répertorie tous les produits dans un GridView et inclut un contrôle Button Web qui, une fois cliqué, réaffecte les valeurs des produits CategoryID . En particulier, la réaffectation de catégorie progresse de sorte que les premiers produits se voient attribuer une valeur valide CategoryID , tandis que d’autres se voient attribuer volontairement une valeur inexistante CategoryID . Si nous essayons de mettre à jour la base de données avec un produit dont CategoryID la catégorie ne correspond pas à une catégorie existante , CategoryIDune violation de contrainte de clé étrangère se produit et une exception est levée. Dans cet exemple, nous allons voir que lors de l’utilisation d’une transaction, l’exception levée à partir de la violation de la contrainte de clé étrangère entraîne la restauration des modifications valides CategoryID précédentes. En cas de non-utilisation d’une transaction, toutefois, les modifications apportées aux catégories initiales restent.

Commencez par ouvrir la Transactions.aspx page dans le BatchData dossier et faites glisser un GridView de la boîte à outils vers le Designer. Définissez son ID sur Products et, à partir de sa balise active, liez-le à un nouvel ObjetDataSource nommé ProductsDataSource. Configurez ObjectDataSource pour extraire ses données de la ProductsBLL méthode class s GetProducts . Il s’agit d’un GridView en lecture seule. Définissez donc les listes déroulantes des onglets UPDATE, INSERT et DELETE sur (Aucun), puis cliquez sur Terminer.

Configurer ObjectDataSource pour utiliser la méthode GetProducts de la classe ProductsBLL

Figure 5 : Configurer ObjectDataSource pour utiliser la ProductsBLL méthode Class s GetProducts (Cliquer pour afficher l’image en taille réelle)

Définissez le Drop-Down Listes dans les onglets UPDATE, INSERT et DELETE sur (Aucun)

Figure 6 : Définissez le Drop-Down Listes dans les onglets UPDATE, INSERT et DELETE sur (Aucun) (Cliquez pour afficher l’image en taille réelle)

Après avoir terminé l’Assistant Configurer la source de données, Visual Studio crée BoundFields et un CheckBoxField pour les champs de données de produit. Supprimez tous ces champs à l’exception ProductIDde , ProductName, CategoryIDet CategoryName et renommez les ProductName propriétés et CategoryName BoundFields HeaderText respectivement Product et Category. À partir de la balise active, case activée l’option Activer la pagination. Après avoir apporté ces modifications, le balisage déclaratif de GridView et ObjectDataSource doit ressembler à ce qui suit :

<asp:GridView ID="Products" runat="server" AllowPaging="True" 
    AutoGenerateColumns="False" DataKeyNames="ProductID" 
    DataSourceID="ProductsDataSource">
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="ProductID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="Product" 
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

Ensuite, ajoutez trois contrôles Button Web au-dessus de GridView. Définissez la première propriété Text de Button sur Actualiser la grille, la deuxième sur Modifier les catégories (WITH TRANSACTION) et la troisième sur Modifier les catégories (WITHOUT TRANSACTION) .

<p>
    <asp:Button ID="RefreshGrid" runat="server" Text="Refresh Grid" />
</p>
<p>
    <asp:Button ID="ModifyCategoriesWithTransaction" runat="server"
        Text="Modify Categories (WITH TRANSACTION)" />
</p>
<p>
    <asp:Button ID="ModifyCategoriesWithoutTransaction" runat="server"
        Text="Modify Categories (WITHOUT TRANSACTION)" />
</p>

À ce stade, le mode Création dans Visual Studio doit ressembler à la capture d’écran illustrée dans la figure 7.

La page contient un contrôle Web GridView et trois boutons

Figure 7 : La page contient un contrôle Web GridView et trois boutons (cliquer pour afficher l’image en taille réelle)

Créez des gestionnaires d’événements pour chacun des trois événements Button s Click et utilisez le code suivant :

Protected Sub RefreshGrid_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles RefreshGrid.Click
    
    Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithTransaction_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ModifyCategoriesWithTransaction.Click
    
    ' Get the set of products
    Dim productsAPI As New ProductsBLL()
    Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
    ' Update each product's CategoryID
    For Each product As Northwind.ProductsRow In productsData
        product.CategoryID = product.ProductID
    Next
    ' Update the data using a transaction
    productsAPI.UpdateWithTransaction(productsData)
    ' Refresh the Grid
    Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithoutTransaction_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ModifyCategoriesWithoutTransaction.Click
    
    ' Get the set of products
    Dim productsAPI As New ProductsBLL()
    Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
    ' Update each product's CategoryID
    For Each product As Northwind.ProductsRow In productsData
        product.CategoryID = product.ProductID
    Next
    ' Update the data WITHOUT using a transaction
    Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
    productsAdapter.Update(productsData)
    ' Refresh the Grid
    Products.DataBind()
End Sub

Le gestionnaire d’événements Click refresh Button relie simplement les données à GridView en appelant la Products méthode s GridView DataBind .

Le deuxième gestionnaire d’événements réattribue les produits CategoryID et utilise la nouvelle méthode de transaction de la BLL pour effectuer les mises à jour de la base de données sous l’égide d’une transaction. Notez que chaque produit s CategoryID est arbitrairement défini sur la même valeur que son ProductID. Cela fonctionnera bien pour les premiers produits, car ces produits ont ProductID des valeurs qui correspondent à des s valides CategoryID . Mais une fois que le ProductID s commence à devenir trop grand, ce chevauchement coïncident entre ProductID s et CategoryID s ne s’applique plus.

Le troisième Click gestionnaire d’événements met à jour les produits CategoryID de la même manière, mais envoie la mise à jour à la base de données à l’aide de la ProductsTableAdapter méthode par défaut Update s. Cette Update méthode n’encapsule pas la série de commandes dans une transaction, de sorte que ces modifications sont apportées avant que la première erreur de violation de contrainte de clé étrangère rencontrée persiste.

Pour illustrer ce comportement, visitez cette page via un navigateur. Initialement, vous devez voir la première page de données, comme illustré dans la figure 8. Ensuite, cliquez sur le bouton Modifier les catégories (AVEC TRANSACTION). Cela entraîne une publication et une tentative de mise à jour de toutes les valeurs de produits CategoryID , mais entraîne une violation de contrainte de clé étrangère (voir figure 9).

Les produits sont affichés dans un GridView paginable

Figure 8 : Les produits sont affichés dans un GridView paginable (cliquer pour afficher l’image en taille réelle)

La réaffectation des catégories entraîne une violation de contrainte de clé étrangère

Figure 9 : Réaffectation des catégories entraîne une violation de contrainte de clé étrangère (cliquer pour afficher l’image en taille réelle)

Appuyez maintenant sur le bouton Précédent de votre navigateur, puis cliquez sur le bouton Actualiser la grille. Lors de l’actualisation des données, vous devriez voir exactement la même sortie qu’à la figure 8. Autrement dit, même si certains produits CategoryID ont été modifiés en valeurs légales et mis à jour dans la base de données, ils ont été restaurés lorsque la violation de contrainte de clé étrangère s’est produite.

Essayez maintenant de cliquer sur le bouton Modifier les catégories (SANS TRANSACTION). Cela entraîne la même erreur de violation de contrainte de clé étrangère (voir figure 9), mais cette fois les produits dont CategoryID les valeurs ont été modifiées en valeur légale ne seront pas restaurés. Appuyez sur le bouton Précédent de votre navigateur, puis sur le bouton Actualiser la grille. Comme le montre la figure 10, les CategoryID s des huit premiers produits ont été réaffectés. Par exemple, dans la figure 8, Chang avait un CategoryID de 1, mais dans la figure 10, il a été réaffecté à 2.

Certaines valeurs categoryID de certains produits ont été mises à jour, tandis que d’autres ne l’ont pas été

Figure 10 : Certaines valeurs de produits CategoryID ont été mises à jour alors que d’autres ne l’ont pas été (cliquer pour afficher l’image en taille réelle)

Résumé

Par défaut, les méthodes TableAdapter ne wrappent pas les instructions de base de données exécutées dans l’étendue d’une transaction, mais avec un peu de travail, nous pouvons ajouter des méthodes qui vont créer, valider et restaurer une transaction. Dans ce tutoriel, nous avons créé trois méthodes de ce type dans la ProductsTableAdapter classe : BeginTransaction, CommitTransactionet RollbackTransaction. Nous avons vu comment utiliser ces méthodes avec un Try...Catch bloc pour rendre atomique une série d’instructions de modification de données. En particulier, nous avons créé la UpdateWithTransaction méthode dans le ProductsTableAdapter, qui utilise le modèle De mise à jour par lots pour effectuer les modifications nécessaires aux lignes d’un fourni ProductsDataTable. Nous avons également ajouté la DeleteProductsWithTransaction méthode à la ProductsBLL classe dans le BLL, qui accepte un List de ProductID valeurs comme entrée et appelle la méthode Delete de modèle DB-Direct pour chaque ProductID. Les deux méthodes commencent par créer une transaction, puis en exécutant les instructions de modification de données dans un Try...Catch bloc. Si une exception se produit, la transaction est restaurée, sinon elle est validée.

L’étape 5 illustre l’effet des mises à jour par lots transactionnelles par rapport aux mises à jour par lots qui négligent d’utiliser une transaction. Dans les trois didacticiels suivants, nous allons nous appuyer sur les bases de ce didacticiel et créer des interfaces utilisateur pour effectuer des mises à jour, des suppressions et des insertions par lots.

Bonne programmation !

En savoir plus

Pour plus d’informations sur les sujets abordés dans ce didacticiel, consultez les ressources suivantes :

À propos de l’auteur

Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille comme consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Il est accessible à l’adressemitchell@4GuysFromRolla.com . ou via son blog, qui se trouve à l’adresse http://ScottOnWriting.NET.

Remerciements spéciaux à

Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Les principaux réviseurs de ce tutoriel étaient Dave Gardner, Hilton Giesenow et Teresa Murphy. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.