Insertion par lots (C#)
par Scott Mitchell
Découvrez comment insérer plusieurs enregistrements de base de données en une seule opération. Dans la couche d’interface utilisateur, nous étendons GridView pour permettre à l’utilisateur d’entrer plusieurs nouveaux enregistrements. Dans la couche d’accès aux données, nous encapsulons les multiples opérations d’insertion au sein d’une transaction pour nous assurer que toutes les insertions réussissent ou que toutes les insertions sont restaurées.
Introduction
Dans le tutoriel Mise à jour par lots , nous avons examiné la personnalisation du contrôle GridView pour présenter une interface dans laquelle plusieurs enregistrements étaient modifiables. L’utilisateur qui visite la page peut apporter une série de modifications, puis, d’un simple clic sur un bouton, effectuer une mise à jour par lot. Pour les situations où les utilisateurs mettent généralement à jour de nombreux enregistrements en une seule fois, une telle interface peut enregistrer d’innombrables clics et commutateurs de contexte de clavier à souris par rapport aux fonctionnalités d’édition par ligne par défaut qui ont été explorées pour la première fois dans le didacticiel Vue d’ensemble de l’insertion, de la mise à jour et de la suppression de données .
Ce concept peut également être appliqué lors de l’ajout d’enregistrements. Imaginez qu’ici, chez Northwind Traders, nous recevons généralement des expéditions de fournisseurs qui contiennent un certain nombre de produits pour une catégorie particulière. Par exemple, nous pouvons recevoir une expédition de six produits de thé et de café différents de Tokyo Traders. Si un utilisateur entre les six produits un par un via un contrôle DetailsView, il devra choisir plusieurs des mêmes valeurs à plusieurs reprises : il devra choisir la même catégorie (Boissons), le même fournisseur (Tokyo Traders), la même valeur abandonnée (False) et les mêmes unités sur la valeur de la commande (0). Cette entrée de données répétitive prend non seulement du temps, mais elle est sujette à des erreurs.
Avec un peu de travail, nous pouvons créer une interface d’insertion par lots qui permet à l’utilisateur de choisir le fournisseur et la catégorie une fois, d’entrer une série de noms de produits et de prix unitaires, puis de cliquer sur un bouton pour ajouter les nouveaux produits à la base de données (voir figure 1). À mesure que chaque produit est ajouté, ses ProductName
champs et UnitPrice
de données se voient attribuer les valeurs entrées dans les TextBoxes, tandis que ses CategoryID
valeurs et SupplierID
se voient attribuer les valeurs des DropDownLists en haut du formulaire. Les Discontinued
valeurs et UnitsOnOrder
sont définies sur les valeurs codées en dur de false
et 0, respectivement.
Figure 1 : l’interface d’insertion par lots (cliquer pour afficher l’image en taille réelle)
Dans ce tutoriel, nous allons créer une page qui implémente l’interface d’insertion par lots illustrée à la figure 1. Comme pour les deux didacticiels précédents, nous allons encapsuler les insertions dans l’étendue d’une transaction pour garantir l’atomicité. Commençons !
Étape 1 : Création de l’interface d’affichage
Ce tutoriel se compose d’une seule page divisée en deux régions : une région d’affichage et une région d’insertion. L’interface d’affichage, que nous allons créer à cette étape, affiche les produits dans un GridView et inclut un bouton intitulé Traiter l’expédition du produit. Lorsque vous cliquez sur ce bouton, l’interface d’affichage est remplacée par l’interface d’insertion, qui est illustrée dans la figure 1. L’interface d’affichage retourne une fois que vous avez cliqué sur les boutons Ajouter des produits à partir de l’expédition ou Annuler. Nous allons créer l’interface d’insertion à l’étape 2.
Lors de la création d’une page qui a deux interfaces, dont une seule est visible à la fois, chaque interface est généralement placée dans un contrôle Panel Web, qui sert de conteneur pour d’autres contrôles. Par conséquent, notre page aura deux contrôles Panel un pour chaque interface.
Commencez par ouvrir la BatchInsert.aspx
page dans le BatchData
dossier et faites glisser un panneau de la boîte à outils vers le Designer (voir figure 2). Définissez la propriété DisplayInterface
Panel sur ID
. Lors de l’ajout du Panneau au Designer, ses Height
propriétés et Width
sont définies sur 50px et 125px, respectivement. Effacez ces valeurs de propriété du Fenêtre Propriétés.
Figure 2 : Faire glisser un panneau à partir de la boîte à outils vers le Designer (cliquer pour afficher l’image en taille réelle)
Ensuite, faites glisser un contrôle Button et GridView dans le panneau. Définissez la propriété Button sur ID
et sa Text
propriété sur Traiter l’expédition du ProcessShipment
produit. Définissez la propriété gridView sur ID
ProductsGrid
et, à partir de sa balise active, liez-la à un nouvel ObjetDataSource nommé ProductsDataSource
. Configurez ObjectDataSource pour extraire ses données de la ProductsBLL
méthode class s GetProducts
. Étant donné que ce GridView est utilisé uniquement pour afficher des données, définissez les listes déroulantes des onglets UPDATE, INSERT et DELETE sur (Aucun). Cliquez sur Terminer pour terminer l’Assistant Configurer la source de données.
Figure 3 : Afficher les données retournées à partir de la ProductsBLL
méthode Class s GetProducts
(cliquer pour afficher l’image en taille réelle)
Figure 4 : 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 ObjectDataSource, Visual Studio ajoute BoundFields et un CheckBoxField pour les champs de données de produit. Supprimez tous les champs sauf les ProductName
champs , CategoryName
SupplierName
, UnitPrice
, et Discontinued
. N’hésitez pas à effectuer des personnalisations esthétiques. J’ai décidé de mettre en forme le UnitPrice
champ en tant que valeur monétaire, réorganisé les champs et renommé plusieurs valeurs de champs HeaderText
. Configurez également GridView pour inclure la prise en charge de la pagination et du tri en cochant les cases Activer la pagination et Activer le tri dans la balise active de GridView.
Après l’ajout des contrôles Panel, Button, GridView et ObjectDataSource et la personnalisation des champs GridView, le balisage déclaratif de votre page doit ressembler à ce qui suit :
<asp:Panel ID="DisplayInterface" runat="server">
<p>
<asp:Button ID="ProcessShipment" runat="server"
Text="Process Product Shipment" />
</p>
<asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True"
AllowSorting="True" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice">
<ItemStyle HorizontalAlign="Right" />
</asp:BoundField>
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemStyle HorizontalAlign="Center" />
</asp:CheckBoxField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
</asp:Panel>
Notez que le balisage des éléments Button et GridView s’affiche dans les balises d’ouverture et de fermeture <asp:Panel>
. Étant donné que ces contrôles se trouvent dans le DisplayInterface
panneau, nous pouvons les masquer en définissant simplement la propriété Du Visible
panneau sur false
. L’étape 3 examine la modification programmatique de la propriété Panel en Visible
réponse à un clic de bouton pour afficher une interface tout en masquant l’autre.
Prenez un moment pour voir notre progression via un navigateur. Comme le montre la figure 5, vous devez voir un bouton Traiter l’expédition des produits au-dessus d’un GridView qui répertorie les produits dix à la fois.
Figure 5 : GridView Listes les fonctionnalités de tri et de pagination des produits et offres (cliquer pour afficher l’image en taille réelle)
Étape 2 : Création de l’interface d’insertion
Une fois l’interface d’affichage terminée, nous sommes prêts à créer l’interface d’insertion. Pour ce tutoriel, nous allons créer une interface d’insertion qui demande une valeur de fournisseur et de catégorie unique, puis permet à l’utilisateur d’entrer jusqu’à cinq noms de produits et des valeurs de prix unitaires. Avec cette interface, l’utilisateur peut ajouter un à cinq nouveaux produits qui partagent tous la même catégorie et le même fournisseur, mais qui ont des noms de produits et des prix uniques.
Commencez par faire glisser un panneau de la boîte à outils vers le Designer, en le plaçant sous le panneau existantDisplayInterface
. Définissez la ID
propriété de ce Panneau nouvellement ajouté sur InsertingInterface
et définissez sa Visible
propriété sur false
. Nous allons ajouter du code qui définit la InsertingInterface
propriété Panel Visible
sur à true
l’étape 3. Effacez également les valeurs de propriété et Width
de Height
panel.
Ensuite, nous devons créer l’interface d’insertion qui a été illustrée dans la figure 1. Cette interface peut être créée à l’aide de diverses techniques HTML, mais nous en utiliserons une assez simple : une table à quatre colonnes et sept lignes.
Notes
Lors de la saisie de balisage pour les éléments HTML <table>
, je préfère utiliser la vue Source. Bien que Visual Studio dispose d’outils permettant d’ajouter <table>
des éléments via le Designer, le Designer semble trop disposé à injecter des paramètres non masqués dans style
le balisage. Une fois que j’ai créé le <table>
balisage, je reviens généralement à la Designer pour ajouter les contrôles Web et définir leurs propriétés. Lors de la création de tables avec des colonnes et des lignes prédéfinies, je préfère utiliser le code HTML statique plutôt que le contrôle Web table , car tous les contrôles Web placés dans un contrôle Web table sont accessibles uniquement à l’aide du FindControl("controlID")
modèle . Toutefois, j’utilise des contrôles Web de table pour les tables de taille dynamique (celles dont les lignes ou les colonnes sont basées sur une base de données ou des critères spécifiés par l’utilisateur), car le contrôle Web table peut être construit par programmation.
Entrez le balisage suivant dans les <asp:Panel>
balises du InsertingInterface
panneau :
<table class="DataWebControlStyle" cellspacing="0">
<tr class="BatchInsertHeaderRow">
<td class="BatchInsertLabel">Supplier:</td>
<td></td>
<td class="BatchInsertLabel">Category:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertFooterRow">
<td colspan="4">
</td>
</tr>
</table>
Ce <table>
balisage n’inclut pas encore de contrôles Web. Nous allons les ajouter momentanément. Notez que chaque <tr>
élément contient un paramètre de classe CSS spécifique : BatchInsertHeaderRow
pour la ligne d’en-tête où vont les listes DropDownLists du fournisseur et de la catégorie ; BatchInsertFooterRow
pour la ligne de pied de page où vont aller les boutons Ajouter des produits à partir de l’expédition et annuler ; et alterner BatchInsertRow
les valeurs et BatchInsertAlternatingRow
pour les lignes qui contiennent les contrôles TextBox de produit et de prix unitaire. J’ai créé des classes CSS correspondantes dans le Styles.css
fichier pour donner à l’interface d’insertion une apparence similaire aux contrôles GridView et DetailsView que nous avons utilisés tout au long de ces didacticiels. Ces classes CSS sont illustrées ci-dessous.
/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
font-weight: bold;
text-align: right;
}
.BatchInsertHeaderRow td
{
color: White;
background-color: #900;
padding: 11px;
}
.BatchInsertFooterRow td
{
text-align: center;
padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
background-color: #fcc;
}
Une fois ce balisage entré, revenez en mode Création. Cela <table>
doit s’afficher sous la forme d’un tableau de 4 colonnes et de sept lignes dans le Designer, comme l’illustre la figure 6.
Figure 6 : L’interface d’insertion est composée d’une table à quatre colonnes, Seven-Row (cliquer pour afficher l’image en taille réelle)
Nous sommes maintenant prêts à ajouter les contrôles Web à l’interface d’insertion. Faites glisser deux Listes déroulantes de la boîte à outils dans les cellules appropriées de la table 1 pour le fournisseur et l’autre pour la catégorie.
Définissez la propriété Suppliers
du ID
fournisseur DropDownList sur et liez-la à un nouvel ObjetDataSource nommé SuppliersDataSource
. Configurez le nouvel ObjetDataSource pour récupérer ses données à partir de la SuppliersBLL
méthode class s GetSuppliers
et définissez la liste déroulante s de l’onglet UPDATE sur (Aucun). Cliquez sur Terminer pour terminer l'Assistant.
Figure 7 : Configurer ObjectDataSource pour utiliser la SuppliersBLL
méthode Class s GetSuppliers
(Cliquer pour afficher l’image en taille réelle)
Faites en charge que dropDownList Suppliers
affiche le CompanyName
champ de données et utilisez le SupplierID
champ de données comme valeurs ListItem
s.
Figure 8 : Afficher le CompanyName
champ de données et utiliser SupplierID
comme valeur (cliquez pour afficher l’image en taille réelle)
Nommez le deuxième DropDownList Categories
et liez-le à un nouvel ObjetDataSource nommé CategoriesDataSource
. Configurez ObjectDataSource CategoriesDataSource
pour utiliser la méthode de la CategoriesBLL
classe ; GetCategories
définissez les listes déroulantes des onglets UPDATE et DELETE sur (Aucune), puis cliquez sur Terminer pour terminer l’Assistant. Enfin, faites en charge que la liste DropDownList affiche le CategoryName
champ de données et utilise comme CategoryID
valeur.
Une fois ces deux DropDownLists ajoutés et liés à ObjectDataSources correctement configurés, votre écran doit ressembler à la Figure 9.
Figure 9 : La ligne d’en-tête contient maintenant les Suppliers
listes déroulantes et Categories
(cliquez pour afficher l’image en taille réelle)
Nous devons maintenant créer les zones de texte pour collecter le nom et le prix de chaque nouveau produit. Faites glisser un contrôle TextBox de la boîte à outils sur le Designer pour chacune des cinq lignes de nom et de prix du produit. Définissez les ID
propriétés des TextBoxes ProductName1
sur , UnitPrice1
, ProductName2
, UnitPrice2
ProductName3
, , UnitPrice3
et ainsi de suite.
Ajoutez un CompareValidator après chaque textBoxe de prix unitaire, en définissant la ControlToValidate
propriété sur le approprié ID
. Définissez également la Operator
propriété sur GreaterThanEqual
, ValueToCompare
sur 0 et Type
sur Currency
. Ces paramètres indiquent à CompareValidator de s’assurer que le prix, s’il est entré, est une valeur monétaire valide supérieure ou égale à zéro. Définissez la propriété sur Text
*, et ErrorMessage
sur Le prix doit être supérieur ou égal à zéro. En outre, veuillez omettre tous les symboles monétaires.
Notes
L’interface d’insertion n’inclut aucun contrôle RequiredFieldValidator, même si le ProductName
champ dans la Products
table de base de données n’autorise NULL
pas les valeurs. Cela est dû au fait que nous voulons permettre à l’utilisateur d’entrer jusqu’à cinq produits. Par exemple, si l’utilisateur devait fournir le nom du produit et le prix unitaire pour les trois premières lignes, en laissant les deux dernières lignes vides, il suffit d’ajouter trois nouveaux produits au système. Toutefois, étant donné ProductName
que est obligatoire, nous devons case activée par programmation pour nous assurer que si un prix unitaire est entré, une valeur de nom de produit correspondante est fournie. Nous aborderons cette case activée à l’étape 4.
Lors de la validation de l’entrée de l’utilisateur, CompareValidator signale des données non valides si la valeur contient un symbole monétaire. Ajoutez un $ devant chaque zone de texte du prix unitaire pour servir de repère visuel qui indique à l’utilisateur d’omettre le symbole monétaire lors de la saisie du prix.
Enfin, ajoutez un contrôle ValidationSummary dans le InsertingInterface
Panneau, en lui ajoutant la ShowMessageBox
valeur true
et la valeur de sa ShowSummary
propriété .false
Avec ces paramètres, si l’utilisateur entre une valeur de prix unitaire non valide, un astérisque s’affiche en regard des contrôles TextBox incriminés et validationSummary affiche une boîte de message côté client qui affiche le message d’erreur que nous avons spécifié précédemment.
À ce stade, votre écran doit ressembler à la figure 10.
Figure 10 : L’interface d’insertion inclut désormais des zones de texte pour les noms et les prix des produits (cliquez pour afficher l’image en taille réelle)
Ensuite, nous devons ajouter les boutons Ajouter des produits à partir de l’expédition et Annuler à la ligne de pied de page. Faites glisser deux contrôles Button de la boîte à outils dans le pied de page de l’interface d’insertion, en définissant les propriétés Buttons ID
sur et les CancelButton
propriétés et sur Text
Ajouter des produits à partir de l’expédition et Annuler, respectivement.AddProducts
En outre, définissez la CancelButton
propriété du contrôle sur CausesValidation
false
.
Enfin, nous devons ajouter un contrôle Label Web qui affiche status messages pour les deux interfaces. Par exemple, lorsqu’un utilisateur ajoute correctement une nouvelle expédition de produits, nous voulons revenir à l’interface d’affichage et afficher un message de confirmation. Si, toutefois, l’utilisateur fournit un prix pour un nouveau produit, mais laisse le nom du produit désactivé, nous devons afficher un message d’avertissement, car le ProductName
champ est obligatoire. Étant donné que nous avons besoin que ce message s’affiche pour les deux interfaces, placez-le en haut de la page en dehors des panneaux.
Faites glisser un contrôle Label Web de la boîte à outils vers le haut de la page dans le Designer. Définissez la propriété sur ID
StatusLabel
, effacez la Text
propriété et définissez les propriétés false
et EnableViewState
sur Visible
. Comme nous l’avons vu dans les tutoriels précédents, la définition de la EnableViewState
propriété sur false
nous permet de modifier par programmation les valeurs de propriété de Label et de les faire revenir automatiquement à leurs valeurs par défaut lors de la publication suivante. Cela simplifie le code permettant d’afficher un message status en réponse à une action de l’utilisateur qui disparaît lors de la publication suivante. Enfin, définissez la StatusLabel
propriété du CssClass
contrôle sur Warning, qui est le nom d’une classe CSS définie dans Styles.css
qui affiche le texte dans une grande police rouge en italique, en gras.
La figure 11 montre l’Designer Visual Studio après l’ajout et la configuration de l’étiquette.
Figure 11 : Placer le StatusLabel
contrôle au-dessus des contrôles à deux volets (cliquer pour afficher l’image en taille réelle)
Étape 3 : Basculer entre les interfaces d’affichage et d’insertion
À ce stade, nous avons terminé le balisage pour nos interfaces d’affichage et d’insertion, mais nous nous sommes toujours laissés avec deux tâches :
- Basculement entre les interfaces d’affichage et d’insertion
- Ajout des produits de l’expédition à la base de données
Actuellement, l’interface d’affichage est visible, mais l’interface d’insertion est masquée. Cela est dû au fait que la DisplayInterface
propriété de Visible
Panel a la true
valeur (valeur par défaut), tandis que la InsertingInterface
propriété panel a Visible
la false
valeur . Pour basculer entre les deux interfaces, nous devons simplement basculer la valeur de la propriété de Visible
chaque contrôle.
Nous voulons passer de l’interface d’affichage à l’interface d’insertion lorsque l’utilisateur clique sur le bouton Traiter l’expédition du produit. Par conséquent, créez un gestionnaire d’événements pour cet événement Button qui Click
contient le code suivant :
protected void ProcessShipment_Click(object sender, EventArgs e)
{
DisplayInterface.Visible = false;
InsertingInterface.Visible = true;
}
Ce code masque simplement le DisplayInterface
panneau et affiche le InsertingInterface
panneau.
Ensuite, créez des gestionnaires d’événements pour les contrôles Ajouter des produits à partir de l’expédition et Annuler dans l’interface d’insertion. Lorsque vous cliquez sur l’un de ces boutons, nous devons revenir à l’interface d’affichage. Créez Click
des gestionnaires d’événements pour les deux contrôles Button afin qu’ils appellent ReturnToDisplayInterface
, une méthode que nous allons ajouter momentanément. En plus de masquer le InsertingInterface
panneau et d’afficher le DisplayInterface
panneau, la ReturnToDisplayInterface
méthode doit retourner les contrôles Web à leur état de pré-édition. Cela implique de définir les propriétés DropDownLists SelectedIndex
sur 0 et d’effacer les Text
propriétés des contrôles TextBox.
Notes
Réfléchissez à ce qui peut se produire si nous n’avons pas retourné les contrôles à leur état de pré-édition avant de revenir à l’interface d’affichage. Un utilisateur peut cliquer sur le bouton Traiter l’expédition du produit, entrer les produits de l’expédition, puis cliquer sur Ajouter des produits à partir de l’expédition. Cela permet d’ajouter les produits et de renvoyer l’utilisateur à l’interface d’affichage. À ce stade, l’utilisateur peut souhaiter ajouter un autre envoi. En cliquant sur le bouton Traiter l’expédition du produit, ils revenaient à l’interface d’insertion, mais les sélections DropDownList et les valeurs TextBox seraient toujours remplies avec leurs valeurs précédentes.
protected void AddProducts_Click(object sender, EventArgs e)
{
// TODO: Save the products
// Revert to the display interface
ReturnToDisplayInterface();
}
protected void CancelButton_Click(object sender, EventArgs e)
{
// Revert to the display interface
ReturnToDisplayInterface();
}
const int firstControlID = 1;
const int lastControlID = 5;
private void ReturnToDisplayInterface()
{
// Reset the control values in the inserting interface
Suppliers.SelectedIndex = 0;
Categories.SelectedIndex = 0;
for (int i = firstControlID; i <= lastControlID; i++)
{
((TextBox)InsertingInterface.FindControl("ProductName" + i.ToString())).Text =
string.Empty;
((TextBox)InsertingInterface.FindControl("UnitPrice" + i.ToString())).Text =
string.Empty;
}
DisplayInterface.Visible = true;
InsertingInterface.Visible = false;
}
Les deux Click
gestionnaires d’événements appellent simplement la ReturnToDisplayInterface
méthode , bien que nous revenions au gestionnaire d’événements Ajouter des produits à partir de l’expédition Click
à l’étape 4 et ajouterons du code pour enregistrer les produits. ReturnToDisplayInterface
commence par retourner les Suppliers
Categories
et DropDownLists à leurs premières options. Les deux constantes firstControlID
et lastControlID
marquent les valeurs d’index de contrôle de début et de fin utilisées pour nommer le nom de produit et le prix unitaire TextBox dans l’interface d’insertion et sont utilisées dans les limites de la for
boucle qui rétablit les Text
propriétés des contrôles TextBox sur une chaîne vide. Enfin, les propriétés panneaux Visible
sont réinitialisées afin que l’interface d’insertion soit masquée et que l’interface d’affichage s’affiche.
Prenez un moment pour tester cette page dans un navigateur. Lorsque vous visitez la page pour la première fois, vous devez voir l’interface d’affichage, comme illustré dans la figure 5. Cliquez sur le bouton Traiter l’expédition du produit. La page est postbacké et vous devez maintenant voir l’interface d’insertion, comme illustré dans la figure 12. Si vous cliquez sur les boutons Ajouter des produits à partir de l’expédition ou Annuler, vous accédez à l’interface d’affichage.
Notes
Lors de l’affichage de l’interface d’insertion, prenez un moment pour tester CompareValidators sur les TextBoxes de prix unitaire. Vous devriez voir un avertissement de boîte de messages côté client lorsque vous cliquez sur le bouton Ajouter des produits à partir de l’expédition avec des valeurs monétaires ou des prix non valides avec une valeur inférieure à zéro.
Figure 12 : L’interface d’insertion s’affiche après avoir cliqué sur le bouton Traiter l’expédition du produit (cliquez pour afficher l’image en taille réelle)
Étape 4 : Ajout des produits
Pour ce didacticiel, il ne reste plus qu’à enregistrer les produits dans la base de données dans le gestionnaire d’événements Ajouter des produits à partir du Click
bouton Expédition. Pour ce faire, créez un ProductsDataTable
et ajoutez un ProductsRow
instance pour chacun des noms de produits fournis. Une fois ces ProductsRow
s ajoutés, nous allons effectuer un appel à la méthode de classe ProductsBLL
s UpdateWithTransaction
en passant le ProductsDataTable
. Rappelez-vous que la UpdateWithTransaction
méthode, qui a été créée dans le didacticiel Wrapping Database Modifications in a Transaction , transmet à ProductsDataTable
la ProductsTableAdapter
méthode s UpdateWithTransaction
. À partir de là, une transaction ADO.NET est démarrée et TableAdapter émet une INSERT
instruction à la base de données pour chaque ajout ProductsRow
dans le DataTable. En supposant que tous les produits sont ajoutés sans erreur, la transaction est validée, sinon elle est restaurée.
Le code du gestionnaire d’événements Ajouter des produits à partir du bouton d’expédition Click
doit également effectuer un peu de vérification des erreurs. Étant donné qu’aucun élément RequiredFieldValidator n’est utilisé dans l’interface d’insertion, un utilisateur peut entrer un prix pour un produit tout en omettant son nom. Étant donné que le nom du produit est obligatoire, si une telle condition se déroule, nous devons alerter l’utilisateur et ne pas poursuivre les insertions. Le code complet Click
du gestionnaire d’événements est le suivant :
protected void AddProducts_Click(object sender, EventArgs e)
{
// Make sure that the UnitPrice CompareValidators report valid data...
if (!Page.IsValid)
return;
// Add new ProductsRows to a ProductsDataTable...
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
for (int i = firstControlID; i <= lastControlID; i++)
{
// Read in the values for the product name and unit price
string productName = ((TextBox)InsertingInterface.FindControl
("ProductName" + i.ToString())).Text.Trim();
string unitPrice = ((TextBox)InsertingInterface.FindControl
("UnitPrice" + i.ToString())).Text.Trim();
// Ensure that if unitPrice has a value, so does productName
if (unitPrice.Length > 0 && productName.Length == 0)
{
// Display a warning and exit this event handler
StatusLabel.Text = "If you provide a unit price you must also " +
"include the name of the product.";
StatusLabel.Visible = true;
return;
}
// Only add the product if a product name value is provided
if (productName.Length > 0)
{
// Add a new ProductsRow to the ProductsDataTable
Northwind.ProductsRow newProduct = products.NewProductsRow();
// Assign the values from the web page
newProduct.ProductName = productName;
newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue);
newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue);
if (unitPrice.Length > 0)
newProduct.UnitPrice = Convert.ToDecimal(unitPrice);
// Add any "default" values
newProduct.Discontinued = false;
newProduct.UnitsOnOrder = 0;
products.AddProductsRow(newProduct);
}
}
// If we reach here, see if there were any products added
if (products.Count > 0)
{
// Add the new products to the database using a transaction
ProductsBLL productsAPI = new ProductsBLL();
productsAPI.UpdateWithTransaction(products);
// Rebind the data to the grid so that the products just added are displayed
ProductsGrid.DataBind();
// Display a confirmation (don't use the Warning CSS class, though)
StatusLabel.CssClass = string.Empty;
StatusLabel.Text = string.Format(
"{0} products from supplier {1} have been added and filed under " +
"category {2}.", products.Count, Suppliers.SelectedItem.Text,
Categories.SelectedItem.Text);
StatusLabel.Visible = true;
// Revert to the display interface
ReturnToDisplayInterface();
}
else
{
// No products supplied!
StatusLabel.Text = "No products were added. Please enter the product " +
"names and unit prices in the textboxes.";
StatusLabel.Visible = true;
}
}
Le gestionnaire d’événements commence par s’assurer que la Page.IsValid
propriété retourne une valeur de true
. Si elle retourne false
, cela signifie qu’un ou plusieurs des CompareValidators signalent des données non valides. Dans ce cas, nous ne voulons pas essayer d’insérer les produits entrés ou nous nous retrouverons avec une exception lors de la tentative d’affectation de la valeur de prix unitaire entrée par l’utilisateur à la ProductsRow
propriété s UnitPrice
.
Ensuite, une nouvelle ProductsDataTable
instance est créée (products
). Une for
boucle est utilisée pour itérer dans le nom du produit et le prix unitaire TextBox, et les Text
propriétés sont lues dans les variables productName
locales et unitPrice
. Si l’utilisateur a entré une valeur pour le prix unitaire, mais pas pour le nom de produit correspondant, affiche StatusLabel
le message Si vous fournissez un prix unitaire, vous devez également inclure le nom du produit et le gestionnaire d’événements est arrêté.
Si un nom de produit a été fourni, une nouvelle ProductsRow
instance est créée à l’aide de la ProductsDataTable
méthode sNewProductsRow
. Cette nouvelle ProductsRow
propriété instance est ProductName
définie sur le nom de produit actuel TextBox, tandis que les SupplierID
propriétés et CategoryID
sont affectées aux SelectedValue
propriétés des DropDownLists dans l’en-tête de l’interface d’insertion. Si l’utilisateur a entré une valeur pour le prix du produit, elle est affectée à la propriété de l’instance ProductsRow
UnitPrice
; sinon, la propriété n’est pas attribuée, ce qui entraîne une NULL
valeur pour UnitPrice
dans la base de données. Enfin, les Discontinued
propriétés et UnitsOnOrder
sont affectées aux valeurs false
codées en dur et à 0, respectivement.
Une fois que les propriétés ont été affectées au ProductsRow
instance il est ajouté à .ProductsDataTable
À la fin de la for
boucle, nous case activée si des produits ont été ajoutés. Après tout, l’utilisateur peut avoir cliqué sur Ajouter des produits à partir de l’expédition avant d’entrer des noms ou des prix de produits. S’il existe au moins un produit dans , ProductsDataTable
la ProductsBLL
méthode class s UpdateWithTransaction
est appelée. Ensuite, les données sont redirigées vers GridView ProductsGrid
afin que les produits nouvellement ajoutés s’affichent dans l’interface d’affichage. est StatusLabel
mis à jour pour afficher un message de confirmation et est ReturnToDisplayInterface
appelé, masquant l’interface d’insertion et affichant l’interface d’affichage.
Si aucun produit n’a été entré, l’interface d’insertion reste affichée, mais le message Aucun produit n’a été ajouté. Entrez les noms des produits et les prix unitaires dans les zones de texte qui s’affichent.
Les illustrations 13, 14 et 15 montrent les interfaces d’insertion et d’affichage en action. Dans la figure 13, l’utilisateur a entré une valeur de prix unitaire sans nom de produit correspondant. La figure 14 montre l’interface d’affichage après l’ajout de trois nouveaux produits, tandis que la figure 15 montre deux des nouveaux produits ajoutés dans GridView (le troisième se trouve sur la page précédente).
Figure 13 : Un nom de produit est obligatoire lors de la saisie d’un prix unitaire (cliquez pour afficher l’image en taille réelle)
Figure 14 : Trois nouveaux légumes ont été ajoutés pour les mayumi du fournisseur (cliquez pour afficher l’image en taille réelle)
Figure 15 : Les nouveaux produits se trouvent dans la dernière page du GridView (cliquez pour afficher l’image en taille réelle)
Notes
La logique d’insertion par lots utilisée dans ce didacticiel encapsule les insertions dans l’étendue de la transaction. Pour vérifier cela, introduisez délibérément une erreur au niveau de la base de données. Par exemple, au lieu d’affecter la propriété de la nouvelle ProductsRow
instance à la valeur sélectionnée dans le DropDownList, affectez-la Categories
à une valeur telle que i * 5
.CategoryID
Voici i
l’indexeur de boucle et a des valeurs comprises entre 1 et 5. Par conséquent, lors de l’ajout de deux ou plusieurs produits par lot, le premier produit aura une valeur valide CategoryID
(5), mais les produits suivants auront CategoryID
des valeurs qui ne correspondent pas aux CategoryID
valeurs de la Categories
table. L’effet net est que bien que la première INSERT
réussisse, les suivantes échouent avec une violation de contrainte de clé étrangère. Étant donné que l’insertion par lots est atomique, la première INSERT
est restaurée, ce qui rétablit l’état de la base de données avant le début du processus d’insertion par lots.
Résumé
Au cours de ces didacticiels et des deux précédents, nous avons créé des interfaces qui permettent de mettre à jour, de supprimer et d’insérer des lots de données, qui utilisaient tous la prise en charge des transactions que nous avons ajoutée à la couche d’accès aux données dans le didacticiel Wrapping Database Modifications dans un didacticiel Transaction . Pour certains scénarios, ces interfaces utilisateur de traitement par lots améliorent considérablement l’efficacité de l’utilisateur final en réduisant le nombre de clics, de publications et de commutateurs de contexte clavier-souris, tout en conservant l’intégrité des données sous-jacentes.
Ce tutoriel complète notre analyse de l’utilisation des données par lots. L’ensemble suivant de tutoriels explore une variété de scénarios avancés de couche d’accès aux données, notamment l’utilisation de procédures stockées dans les méthodes tableAdapter, la configuration des paramètres de connexion et de commande dans le DAL, le chiffrement des chaînes de connexion, etc.
Bonne programmation !
À 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 réviseurs principaux de ce tutoriel étaient Hilton Giesenow et S ren Jacob Lauritsen. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.