Inclusion de modifications d’une base de données dans une transaction (VB)
par Scott Mitchell
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
, UPDATE
et 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 :
- Indique le début d’une transaction.
- Exécutez les instructions SQL qui composent la transaction.
- En cas d’erreur dans l’une des instructions de l’étape 2, restaurez la transaction.
- 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’objetRollback
SqlTransaction
. Si toutes les instructions se terminent correctement, un appel à la méthode de l’objet SqlTransaction
Commit
à 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
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.
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.
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
, UpdateCommand
ou 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 , UpdateCommand
ou 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
, UpdateCommand
et 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.
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. BeginTransaction
ouvre 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
. CommitTransaction
et RollbackTransaction
appelez les méthodes et Rollback
les méthodes de l’objet Transaction
Commit
, 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 Adapter
Update
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 , UpdateCommand
et 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 laUpdateWithTransaction
mé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 , CategoryID
une 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.
Figure 5 : Configurer ObjectDataSource pour utiliser la ProductsBLL
méthode Class s GetProducts
(Cliquer pour afficher l’image en taille réelle)
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 ProductID
de , ProductName
, CategoryID
et 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.
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).
Figure 8 : Les produits sont affichés dans un GridView paginable (cliquer pour afficher l’image en taille réelle)
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.
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
, CommitTransaction
et 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 :
- Transactions simplifiées :
System.Transactions
- TransactionScope et DataAdapters
- Utilisation des transactions de base de données Oracle dans .NET
À 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.