Partager via


Insertion par lots (C#)

par Scott Mitchell

Télécharger le PDF

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.

Interface d’insertion par lots

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é DisplayInterfacePanel 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.

Faites glisser un panneau à partir de la boîte à outils vers le Designer

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 IDProductsGrid 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.

Afficher les données retournées à partir de la méthode GetProducts de la classe ProductsBLL

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)

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

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 ProductNamechamps , CategoryNameSupplierName, 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.

GridView Listes les fonctionnalités de tri et de pagination des produits et offres

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.

L’interface d’insertion est composée d’une table à quatre colonnes Seven-Row

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.

Configurer objectDataSource pour utiliser la méthode GetSuppliers de la classe SuppliersBLL

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.

Afficher le champ de données CompanyName et utiliser SupplierID comme valeur

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.

La ligne d’en-tête contient désormais les listes déroulantes Suppliers and Categories

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 ProductName1sur , UnitPrice1, ProductName2, UnitPrice2ProductName3, , UnitPrice3et 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.

L’interface d’insertion inclut désormais des zones de texte pour les noms et les prix des produits

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 CausesValidationfalse.

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 IDStatusLabel, effacez la Text propriété et définissez les propriétés falseet 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.

Placez le contrôle StatusLabel au-dessus des contrôles à deux volets

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 falsevaleur . 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 SuppliersCategories 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.

L’interface d’insertion s’affiche après avoir cliqué sur le bouton Traiter l’expédition du produit

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 ProductsRowUnitPrice ; 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 , ProductsDataTablela 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).

Un nom de produit est requis lors de la saisie d’un prix unitaire

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)

Trois nouveaux légumes ont été ajoutés pour le fournisseur Mayumi s

Figure 14 : Trois nouveaux légumes ont été ajoutés pour les mayumi du fournisseur (cliquez pour afficher l’image en taille réelle)

Les nouveaux produits se trouvent dans la dernière page du GridView

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.