Inclusion de modifications d’une base de données dans une transaction (C#)
par Scott Mitchell
Ce didacticiel est le premier 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. Avec quelques clics de la souris, il est possible de créer une interface de modification de données enrichie sans écrire de ligne de code, tant que vous êtes du contenu avec modification et suppression sur une base de 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 dans lequel 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 couramment de nombreux enregistrements différents. Au lieu de forcer l’utilisateur à cliquer sur Modifier, effectuer 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 de 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 l’insertion, la modification et la suppression de lots de données.
Lors de l’exécution d’opérations par lots, il est important de déterminer s’il doit être possible que certaines opérations du lot réussissent alors que d’autres échouent. Envisagez une interface de suppression par lots : que se passe-t-il si le premier enregistrement sélectionné est supprimé avec succès, mais le deuxième é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-elle acceptable pour que le premier enregistrement reste supprimé ?
Si vous souhaitez que l’opération de traitement par lots soit traitée comme une opération atomique, une opération où 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 des INSERT
UPDATE
DELETE
instructions exécutées sous l’parapluie 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 examiner comment étendre le dal pour utiliser des transactions de base de données. Les didacticiels suivants examinent l’implémentation de pages web pour l’insertion par lots, la mise à jour et la suppression d’interfaces. Commençons !
Remarque
Lors de la modification des données dans une transaction par lots, 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 dans le même lot échouent, par exemple lors de la suppression d’un ensemble d’e-mails d’un client de messagerie web. S’il existe une erreur de base de données au milieu du processus de suppression, il est probablement acceptable que ces 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. Il existe cependant d’autres scénarios d’opération par lots, où l’atomicité est vitale. Lorsqu’un client déplace ses fonds d’un compte bancaire vers 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 peut-être pas à l’esprit que la première étape réussisse mais que la deuxième étape échoue, ses clients seraient compréhensiblement bouleversés. Je vous encourage à travailler dans 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 l’insertion, la mise à jour et la suppression des interfaces par lots que nous allons créer dans les trois didacticiels suivants.
Vue d’ensemble des transactions
La plupart des bases de données incluent la prise en charge des 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 comme atomiques, ce qui signifie que toutes les commandes échouent ou que toutes les commandes réussissent.
En général, les transactions sont implémentées via des instructions SQL à l’aide du modèle suivant :
- Indiquez 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 via des moyens programmatiques à l’aide de ADO.NET ou des classes de l’espace System.Transactions
de noms. Dans ce tutoriel, nous allons examiner uniquement la gestion des transactions à l’aide de ADO.NET. Dans un prochain tutoriel, nous allons examiner comment utiliser des procédures stockées dans la couche d’accès aux données, auquel moment nous allons explorer les instructions SQL pour la création, la restauration et la validation des transactions.
Remarque
La TransactionScope
classe de 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 de transactions complexes impliquant 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 ADO.NET transactions 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). La configuration, l’implémentation et les problèmes de performances liés à MSDTC en font une rubrique plutôt spécialisée et avancée et au-delà de l’étendue de ces didacticiels.
Lors de l’utilisation du fournisseur SqlClient dans ADO.NET, les transactions sont lancées par le biais d’un appel à la méthode s de BeginTransaction
classe, qui retourne un SqlTransaction
objet.SqlConnection
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 dans le try
bloc, l’exécution transfère au catch
bloc où la transaction peut être restaurée via la SqlTransaction
méthode de l’objetRollback
. Si toutes les instructions se terminent correctement, un appel à la méthode de l’objetCommit
SqlTransaction
à la fin du try
bloc valide la transaction. L’extrait de code suivant illustre ce modèle. Consultez Maintenance de la cohérence de la base de données avec les transactions.
// Create the SqlTransaction object
SqlTransaction myTransaction = 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;
}
Par défaut, les TableAdapters d’un Jeu de données Typé n’utilisent pas de transactions. Pour prendre en charge les 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 de l’utilisation des pages web de données batched
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 maître.
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
Figure 1 : Ajouter les pages ASP.NET pour les didacticiels liés à SqlDataSource
Comme avec les autres dossiers, Default.aspx
utilisez le SectionLevelTutorialListing.ascx
contrôle utilisateur pour répertorier les didacticiels de sa section. Par conséquent, ajoutez ce contrôle utilisateur en Default.aspx
le faisant glisser de l’Explorateur de solutions vers l’affichage Création de la page.
Figure 2 : Ajouter le contrôle utilisateur à Default.aspx
(Cliquez pour afficher l’image SectionLevelTutorialListing.ascx
de taille complète)
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 de la carte <siteMapNode>
de site :
<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 didacticiels via un navigateur. Le menu de gauche inclut désormais des éléments pour l’utilisation de didacticiels sur les données par lots.
Figure 3 : La carte de site inclut désormais des entrées pour les didacticiels sur l’utilisation de données par lots
É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, La 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 de TableAdapters. Les DataTables contiennent des données tandis que les TableAdapters fournissent la fonctionnalité de lecture des données de la base de données dans les DataTables, pour 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ées Mise à jour batch et DB-Direct. Avec le modèle de mise à jour batch, TableAdapter est passé un DataSet, 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, ou InsertCommand
UpdateCommand
DeleteCommand
exécutée. Avec le modèle DB-Direct, TableAdapter passe à 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 ou UpdateCommand
DeleteCommand
l’instruction 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 exemple, imaginez que le modèle DB-Direct est utilisé par du 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 premiers insertions réussissent, mais que le 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 batch est utilisé pour effectuer des insertions, des mises à jour et des suppressions aux 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, ces modifications antérieures terminées restent dans la base de données.
Dans certains scénarios, nous voulons garantir l’atomicité dans une série de modifications. Pour ce faire, nous devons étendre manuellement TableAdapter en ajoutant de nouvelles méthodes qui exécutent le InsertCommand
, UpdateCommand
et DeleteCommand
s sous le parapluie d’une transaction. Lors de la 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 au sein du DataSet typé. Cette technique peut également être utilisée avec TableAdapters.
Le Jeu de Northwind.xsd
données 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.cs
(voir la figure 4). Ce fichier contiendra l’implémentation partielle des ProductsTableAdapter
méthodes permettant d’effectuer des modifications de données à l’aide d’une transaction.
Figure 4 : Ajouter un dossier nommé TransactionSupport
et un fichier de classe nommé ProductsTableAdapter.TransactionSupport.cs
Entrez le code suivant dans le ProductsTableAdapter.TransactionSupport.cs
fichier :
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
public partial class ProductsTableAdapter
{
private SqlTransaction _transaction;
private SqlTransaction Transaction
{
get
{
return this._transaction;
}
set
{
this._transaction = value;
}
}
public void BeginTransaction()
{
// Open the connection, if needed
if (this.Connection.State != ConnectionState.Open)
this.Connection.Open();
// Create the transaction and assign it to the Transaction property
this.Transaction = this.Connection.BeginTransaction();
// Attach the transaction to the Adapters
foreach (SqlCommand command in this.CommandCollection)
{
command.Transaction = this.Transaction;
}
this.Adapter.InsertCommand.Transaction = this.Transaction;
this.Adapter.UpdateCommand.Transaction = this.Transaction;
this.Adapter.DeleteCommand.Transaction = this.Transaction;
}
public void CommitTransaction()
{
// Commit the transaction
this.Transaction.Commit();
// Close the connection
this.Connection.Close();
}
public void RollbackTransaction()
{
// Rollback the transaction
this.Transaction.Rollback();
// Close the connection
this.Connection.Close();
}
}
}
Le partial
mot clé dans la déclaration de classe indique ici au compilateur que les membres ajoutés doivent être ajoutés à la classe dans l’espace ProductsTableAdapter
NorthwindTableAdapters
de noms. Notez l’instruction using 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 dans la base de données. Par conséquent, nous devons utiliser la SqlTransaction
classe pour commencer la transaction, puis 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 pour démarrer, restaurer et valider une transaction. Ils sont marqués public
, ce qui leur permet d’être utilisés à partir du ProductsTableAdapter
, d’une autre classe dans le DAL ou d’une autre couche de l’architecture, comme la BLL. BeginTransaction
ouvre le fichier interne SqlConnection
de TableAdapter (le cas échéant), commence la transaction et l’affecte à la Transaction
propriété et attache la transaction aux objets internes SqlDataAdapter
SqlCommand
. CommitTransaction
et RollbackTransaction
appelez les Transaction
méthodes et Rollback
les objetsCommit
, respectivement, avant de fermer l’objet interneConnection
.
Étape 3 : Ajout de méthodes pour mettre à jour et supprimer des données sous le parapluie d’une transaction
Avec ces méthodes terminées, nous sommes prêts à ajouter des méthodes à ProductsDataTable
ou à la BLL qui effectuent une série de commandes sous l’parapluie d’une transaction. La méthode suivante utilise le modèle Batch Update pour mettre à jour une 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 de données. Si l’appel à la méthode de l’objet Adapter
Update
entraîne une exception, l’exécution est transférée vers le catch
bloc où la transaction sera restaurée et l’exception re levée. Rappelez-vous que la Update
méthode implémente le modèle batch Update en énumérant les lignes des lignes fournies ProductsDataTable
et en effectuant les opérations nécessaires InsertCommand
, UpdateCommand
et DeleteCommand
s. Si l’une de ces commandes entraîne 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 int UpdateWithTransaction(Northwind.ProductsDataTable dataTable)
{
this.BeginTransaction();
try
{
// Perform the update on the DataTable
int returnValue = this.Adapter.Update(dataTable);
// If we reach here, no errors, so commit the transaction
this.CommitTransaction();
return returnValue;
}
catch
{
// If we reach here, there was an error, so rollback the transaction
this.RollbackTransaction();
throw;
}
}
Ajoutez la UpdateWithTransaction
méthode à la ProductsTableAdapter
classe par le biais de la classe partielle dans ProductsTableAdapter.TransactionSupport.cs
. Vous pouvez également ajouter cette méthode à la classe Business Logic Layer ProductsBLL
avec quelques modifications syntactiques mineures. À savoir, le mot clé dans this.BeginTransaction()
, this.CommitTransaction()
et this.RollbackTransaction()
doit être remplacé par Adapter
(rappelez-vous que Adapter
c’est le nom d’une propriété de ProductsBLL
type ProductsTableAdapter
).
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<T>
type int
, qui sont les ProductID
s à supprimer. La méthode lance la transaction via un appel à BeginTransaction
puis, dans le bloc, effectue une itération dans la try
liste fournie appelant la méthode de modèle Delete
DB-Direct pour chaque ProductID
valeur. Si l’un des appels à Delete
échouer, le contrôle est transféré vers le catch
bloc où la transaction est restaurée et l’exception réexéchée. Si tous les appels réussissent Delete
, la transaction est validée. Ajoutez cette méthode à la ProductsBLL
classe.
public void DeleteProductsWithTransaction
(System.Collections.Generic.List<int> productIDs)
{
// Start the transaction
Adapter.BeginTransaction();
try
{
// Delete each product specified in the list
foreach (int productID in productIDs)
{
Adapter.Delete(productID);
}
// Commit the transaction
Adapter.CommitTransaction();
}
catch
{
// There was an error - rollback the transaction
Adapter.RollbackTransaction();
throw;
}
}
Application de transactions entre plusieurs TableAdapters
Le code lié à la transaction examiné dans ce tutoriel permet de traiter plusieurs instructions par rapport à l’opération ProductsTableAdapter
atomique. Mais que se passe-t-il si plusieurs modifications apportées à différentes tables de base de données doivent être effectuées atomiquement ? Par exemple, lors de la suppression d’une catégorie, nous pouvons d’abord réaffecter ses produits actuels à une autre catégorie. Ces deux étapes réaffectent les produits et suppriment la catégorie doivent être exécutées en tant qu’opération atomique. Toutefois, les ProductsTableAdapter
méthodes incluent uniquement la modification de la Products
table et les CategoriesTableAdapter
seules méthodes permettant de modifier la Categories
table. Comment une transaction peut-elle englober les deux TableAdapters ?
Une option consiste à ajouter une méthode au CategoriesTableAdapter
nom DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
et à ce 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 examiner comment commencer, valider et restaurer des transactions dans des procédures stockées dans un didacticiel ultérieur.
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 l’instance CategoriesTableAdapter
ProductsTableAdapter
, puis définit ces deux propriétés TableAdapters Connection
sur la même SqlConnection
instance. À ce stade, l’un des deux TableAdapters lancerait la transaction avec un appel à BeginTransaction
. Les méthodes TableAdapters pour réaffecter les produits et supprimer la catégorie seraient 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 ProductsTableAdapter
3, nous avons ajouté une UpdateWithTransaction
méthode au fichier 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 didacticiels s’efforcent de définir une architecture en couches qui isole le DAL de la couche Présentation. C’est pourquoi il nous appartient de poursuivre cette approche.
Ouvrez le ProductsBLL
fichier de 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 int UpdateWithTransaction(Northwind.ProductsDataTable products)
{
return Adapter.UpdateWithTransaction(products);
}
public void DeleteProductsWithTransaction
(System.Collections.Generic.List<int> productIDs)
{
// Start the transaction
Adapter.BeginTransaction();
try
{
// Delete each product specified in the list
foreach (int productID in productIDs)
Adapter.Delete(productID);
// Commit the transaction
Adapter.CommitTransaction();
}
catch
{
// There was an error - rollback the transaction
Adapter.RollbackTransaction();
throw;
}
}
Remarque
Ces méthodes n’incluent pas l’attribut DataObjectMethodAttribute
affecté à la plupart des autres méthodes de la ProductsBLL
classe, car nous invoquons ces méthodes directement à partir des classes code-behind des pages ASP.NET. Rappelez-vous qu’il DataObjectMethodAttribute
est utilisé pour marquer les méthodes qui doivent apparaître dans l’Assistant Configurer la source de données objectDataSource et sous quel onglet (SELECT, UPDATE, INSERT ou DELETE). Étant donné que GridView ne prend pas en charge la modification par lots ou la suppression par lots, nous devons 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 que la transaction a lors de la mise à jour d’un lot d’enregistrements, nous allons créer une interface utilisateur qui répertorie tous les produits d’un GridView et inclut un contrôle Web Button qui, en cliquant, réaffecte les valeurs des produits CategoryID
. En particulier, la réaffectation de catégorie progresse afin que les premiers produits soient affectés à une valeur valide CategoryID
, tandis que d’autres ont une valeur inexistante CategoryID
. Si nous tentons de mettre à jour la base de données avec un produit qui CategoryID
ne correspond pas à une catégorie existante, CategoryID
une violation de contrainte de clé étrangère se produit et une exception est levée. Ce que nous allons voir dans cet exemple est que lors de l’utilisation d’une transaction, l’exception levée à partir de la violation de contrainte de clé étrangère entraîne la restauration des modifications valides CategoryID
précédentes. Toutefois, lorsque vous n’utilisez pas de transaction, 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 à partir de la boîte à outils sur le Concepteur. Définissez sa ID
Products
balise active et, à partir de sa balise active, liez-la à un nouvel ObjectDataSource nommé ProductsDataSource
. Configurez ObjectDataSource pour extraire ses données de la ProductsBLL
méthode de GetProducts
classe. Il s’agit d’un GridView en lecture seule. Définissez les listes déroulantes sous les onglets UPDATE, INSERT et DELETE sur (Aucun), puis cliquez sur Terminer.
Figure 5 : Figure 5 : Configurer ObjectDataSource pour utiliser la ProductsBLL
méthode class s GetProducts
(Click pour afficher l’image de taille complète)
Figure 6 : Définir les listes déroulantes dans les onglets UPDATE, INSERT et DELETE sur (Aucun) (Cliquez pour afficher l’image de taille complète)
Une fois l’Assistant Configuration de la source de données terminée, 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
renommez les ProductName
CategoryName
propriétés et BoundFields HeaderText
en Product et Category, respectivement. À partir de la balise active, cochez l’option Activer la pagination. Après avoir apporté ces modifications, le balisage déclaratif 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 Web Button au-dessus de GridView. Définissez la propriété Texte du premier bouton sur Actualiser la grille, les secondes pour modifier les catégories (WITH TRANSACTION) et les troisièmes pour modifier les catégories (SANS 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, l’affichage 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 (cliquez pour afficher l’image de taille complète)
Créez des gestionnaires d’événements pour chacun des trois événements Button et Click
utilisez le code suivant :
protected void RefreshGrid_Click(object sender, EventArgs e)
{
Products.DataBind();
}
protected void ModifyCategoriesWithTransaction_Click(object sender, EventArgs e)
{
// Get the set of products
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Update each product's CategoryID
foreach (Northwind.ProductsRow product in products)
{
product.CategoryID = product.ProductID;
}
// Update the data using a transaction
productsAPI.UpdateWithTransaction(products);
// Refresh the Grid
Products.DataBind();
}
protected void ModifyCategoriesWithoutTransaction_Click(object sender, EventArgs e)
{
// Get the set of products
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Update each product's CategoryID
foreach (Northwind.ProductsRow product in products)
{
product.CategoryID = product.ProductID;
}
// Update the data WITHOUT using a transaction
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
productsAdapter.Update(products);
// Refresh the Grid
Products.DataBind();
}
Le gestionnaire d’événements d’refresh Click
Button réinscrit simplement les données à GridView en appelant la Products
méthode GridView DataBind
.
Le deuxième gestionnaire d’événements réaffecte les produits CategoryID
et utilise la nouvelle méthode de transaction de la BLL pour effectuer les mises à jour de base de données sous l’parapluie d’une transaction. Notez que chaque produit CategoryID
est arbitrairement défini sur la même valeur que son ProductID
. Cela fonctionnera correctement pour les premiers produits, car ces produits ont ProductID
des valeurs qui se produisent pour mapper aux produits valides CategoryID
. Mais une fois que le ProductID
s commence à être trop grand, ce chevauchement coïncident de ProductID
s et CategoryID
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
. Cette Update
méthode n’encapsule pas la série de commandes au sein d’une transaction. Ces modifications sont donc apportées avant la première erreur de violation de contrainte de clé étrangère rencontrée.
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. Cliquez ensuite sur le bouton Modifier les catégories (WITH 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 la figure 9).
Figure 8 : Les produits sont affichés dans un GridView paginable (cliquez pour afficher l’image de taille complète)
Figure 9 : Réaffecter les catégories entraîne une violation de contrainte de clé étrangère (cliquez pour afficher l’image de taille complète)
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 devez voir exactement la même sortie que celle indiquée dans la figure 8. Autrement dit, même si certains des 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înera la même erreur de violation de contrainte de clé étrangère (voir la figure 9), mais cette fois que ces produits dont CategoryID
les valeurs ont été modifiées en valeur légale ne seront pas annulées. 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 une CategoryID
valeur de 1, mais dans la figure 10, elle a été réaffectée à 2.
Figure 10 : Certaines valeurs de produits CategoryID
ont été mises à jour alors que d’autres n’étaient pas (cliquez pour afficher l’image de taille complète)
Résumé
Par défaut, les méthodes de TableAdapter n’encapsulent 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 créent, valident et restaurent 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 créer une série d’instructions de modification de données atomiques. En particulier, nous avons créé la UpdateWithTransaction
méthode dans le ProductsTableAdapter
, qui utilise le modèle Batch Update pour effectuer les modifications nécessaires aux lignes d’un fourni ProductsDataTable
. Nous avons également ajouté la DeleteProductsWithTransaction
méthode à la ProductsBLL
classe dans la BLL, qui accepte une List
valeur ProductID
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 de lots transactionnelles par rapport aux mises à jour par lots qui ont négligé d’utiliser une transaction. Dans les trois tutoriels suivants, nous allons nous appuyer sur la base de ce didacticiel et créer des interfaces utilisateur pour effectuer des mises à jour par lots, des suppressions et des insertions.
Bonne programmation !
Pour aller plus loin
Pour plus d’informations sur les sujets abordés dans ce tutoriel, consultez les ressources suivantes :
- Transactions faciles :
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 en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. Il peut être accessible à mitchell@4GuysFromRolla.com. ou via son blog, qui peut être trouvé à http://ScottOnWriting.NET.
Merci spécial à
Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Les réviseurs principaux 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.