Partager via


Suppression par lots (C#)

par Scott Mitchell

Télécharger le PDF

Découvrez comment supprimer plusieurs enregistrements de base de données dans une seule opération. Dans la couche Interface utilisateur, nous nous appuyons sur un GridView amélioré créé dans un didacticiel antérieur. Dans la couche d’accès aux données, nous encapsulons les opérations de suppression multiples au sein d’une transaction pour vous assurer que toutes les suppressions réussissent ou que toutes les suppressions sont restaurées.

Présentation

Le tutoriel précédent a exploré comment créer une interface d’édition par lots à l’aide d’un GridView entièrement modifiable. Dans les situations où les utilisateurs modifient couramment de nombreux enregistrements à la fois, une interface d’édition par lots nécessite beaucoup moins de postbacks et de commutateurs de contexte clavier à souris, ce qui améliore l’efficacité de l’utilisateur final. Cette technique est également utile pour les pages où il est courant pour les utilisateurs de supprimer de nombreux enregistrements en une seule fois.

Toute personne qui a utilisé un client de messagerie en ligne est déjà familiarisée avec l’une des interfaces de suppression par lots les plus courantes : une case à cocher dans chaque ligne d’une grille avec un bouton Supprimer tous les éléments vérifiés correspondant (voir la figure 1). Ce didacticiel est plutôt court, car nous avons déjà effectué tous les travaux difficiles dans les didacticiels précédents dans la création de l’interface web et d’une méthode pour supprimer une série d’enregistrements en tant qu’opération atomique unique. Dans le didacticiel Ajout d’une colonne GridView de cases à cocher, nous avons créé un GridView avec une colonne de cases à cocher, et dans le didacticiel Enveloppement des modifications de base de données dans une transaction, nous avons créé une méthode dans la BLL qui utiliserait une transaction pour supprimer une List<T> valeur ProductID. Dans ce tutoriel, nous allons tirer parti de et fusionner nos expériences précédentes pour créer un exemple fonctionnel de suppression par lots.

Chaque ligne inclut une case à cocher

Figure 1 : Chaque ligne inclut une case à cocher (Cliquez pour afficher l’image de taille complète)

Étape 1 : Création de l’interface de suppression par lot

Étant donné que nous avons déjà créé l’interface de suppression de lot dans le didacticiel Ajout d’une colonne GridView de cases à cocher , nous pouvons simplement la copier au BatchDelete.aspx lieu de la créer à partir de zéro. Commencez par ouvrir la BatchDelete.aspx page dans le BatchData dossier et la CheckBoxField.aspx page du EnhancedGridView dossier. Dans la CheckBoxField.aspx page, accédez à la vue Source et copiez le balisage entre les balises, comme illustré dans la <asp:Content> figure 2.

Copier le balisage déclaratif de CheckBoxField.aspx dans le Presse-papiers

Figure 2 : Copier le balisage déclaratif au Presse-papiers (Cliquez pour afficher l’image de taille complète)

Ensuite, accédez à la vue Source dans BatchDelete.aspx et collez le contenu du presse-papiers dans les balises <asp:Content>. Copiez et collez également le code de la classe de code-behind CheckBoxField.aspx.cs à la classe de code-behind dans BatchDelete.aspx.cs (le gestionnaire d'événements pour le bouton DeleteSelectedProducts, la méthode Click, et les gestionnaires d'événements pour les boutons ToggleCheckState et Click). Après avoir copié ce contenu, la classe code-behind de la BatchDelete.aspx page doit contenir le code suivant :

using System;
using System.Data;
using System.Configuration;
using System.Collections;
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;
public partial class BatchData_BatchDelete : System.Web.UI.Page
{
    protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
    {
        bool atLeastOneRowDeleted = false;
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null && cb.Checked)
            {
                // Delete row! (Well, not really...)
                atLeastOneRowDeleted = true;
                // First, get the ProductID for the selected row
                int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
                // "Delete" the row
                DeleteResults.Text += string.Format
                    ("This would have deleted ProductID {0}<br />", productID);
                //... To actually delete the product, use ...
                //ProductsBLL productAPI = new ProductsBLL();
                //productAPI.DeleteProduct(productID);
                //............................................
            }
        }
        // Show the Label if at least one row was deleted...
        DeleteResults.Visible = atLeastOneRowDeleted;
    }
    private void ToggleCheckState(bool checkState)
    {
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null)
                cb.Checked = checkState;
        }
    }
    protected void CheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(true);
    }
    protected void UncheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(false);
    }
}

Après avoir copié le balisage déclaratif et le code source, prenez un moment pour le tester BatchDelete.aspx en l’affichant via un navigateur. Vous devez voir une grille affichant les dix premiers produits, où chaque ligne mentionne le nom, la catégorie et le prix du produit, ainsi qu’une case à cocher. Il doit y avoir trois boutons : Vérifier tout, Désactiver tout et supprimer les produits sélectionnés. En cliquant sur le bouton Vérifier tout, toutes les cases à cocher sont sélectionnées, tandis que le bouton Désélectionner tout désactive toutes les cases à cocher. Le fait de cliquer sur Supprimer les produits sélectionnés affiche un message qui répertorie les ProductID valeurs des produits sélectionnés, mais qui ne supprime pas réellement les produits.

L’interface de CheckBoxField.aspx a été déplacée vers BatchDeleting.aspx

Figure 3 : L’interface CheckBoxField.aspx a été déplacée vers BatchDeleting.aspx (Cliquez pour voir l’image en taille réelle)

Étape 2 : Suppression des produits vérifiés à l’aide de transactions

Avec l’interface de suppression par lot correctement copiée sur BatchDeleting.aspx, il ne reste qu'à mettre à jour le code afin que le bouton Supprimer les produits sélectionnés supprime les produits cochés à l’aide de la méthode DeleteProductsWithTransaction dans la classe ProductsBLL. Cette méthode, ajoutée dans le didacticiel Wrapping Database Modifications au sein d'une transaction, accepte comme entrée des valeurs List<T> et supprime chaque ProductID correspondant dans le cadre d'une transaction.

Le gestionnaire d'événements DeleteSelectedProducts Button Click utilise actuellement la boucle suivante foreach pour itérer sur chaque ligne de GridView :

// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
    // Access the CheckBox
    CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
    if (cb != null && cb.Checked)
    {
        // Delete row! (Well, not really...)
        atLeastOneRowDeleted = true;
        // First, get the ProductID for the selected row
        int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
        // "Delete" the row
        DeleteResults.Text += string.Format
            ("This would have deleted ProductID {0}<br />", productID);
        //... To actually delete the product, use ...
        //ProductsBLL productAPI = new ProductsBLL();
        //productAPI.DeleteProduct(productID);
        //............................................
    }
}

Pour chaque ligne, le contrôle Web CheckBox ProductSelector est référencé de façon programmatique. S'il est vérifié, la ligne ProductID est récupérée de la collection DataKeys et la propriété Label DeleteResultsText est mise à jour pour inclure un message indiquant que la ligne a été sélectionnée pour suppression.

Le code ci-dessus ne supprime pas réellement les enregistrements, car l'appel à la méthode de la classe ProductsBLLDelete est mis en commentaire. Si cette logique de suppression devait être appliquée, le code supprimerait les produits, mais pas dans une opération atomique. Autrement dit, si les premières suppressions de la séquence ont réussi, mais qu’une autre a échoué (peut-être en raison d’une violation de contrainte de clé étrangère), une exception serait levée, mais ces produits déjà supprimés resteraient supprimés.

Pour assurer l’atomicité, nous devons plutôt utiliser la méthode ProductsBLL de la classe DeleteProductsWithTransaction. Étant donné que cette méthode accepte une liste de ProductID valeurs, nous devons d’abord compiler cette liste à partir de la grille, puis la transmettre en tant que paramètre. Nous créons d’abord une instance d’un List<T> type int. Dans la foreach boucle, nous devons ajouter les valeurs des produits ProductID sélectionnés à ce List<T>. Après la boucle, ce List<T> doit être passé à la méthode ProductsBLL de la classe DeleteProductsWithTransaction. Mettez à jour le DeleteSelectedProducts gestionnaire d’événements du bouton Click avec le code suivant :

protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
{
    // Create a List to hold the ProductID values to delete
    System.Collections.Generic.List<int> productIDsToDelete = 
        new System.Collections.Generic.List<int>();
    // Iterate through the Products.Rows property
    foreach (GridViewRow row in Products.Rows)
    {
        // Access the CheckBox
        CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
        if (cb != null && cb.Checked)
        {
            // Save the ProductID value for deletion
            // First, get the ProductID for the selected row
            int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
            // Add it to the List...
            productIDsToDelete.Add(productID);
            // Add a confirmation message
            DeleteResults.Text += string.Format
                ("ProductID {0} has been deleted<br />", productID);
        }
    }
    // Call the DeleteProductsWithTransaction method and show the Label 
    // if at least one row was deleted...
    if (productIDsToDelete.Count > 0)
    {
        ProductsBLL productAPI = new ProductsBLL();
        productAPI.DeleteProductsWithTransaction(productIDsToDelete);
        DeleteResults.Visible = true;
        // Rebind the data to the GridView
        Products.DataBind();
    }
}

Le code mis à jour crée un List<T> type int (productIDsToDelete) et le remplit avec les ProductID valeurs à supprimer. Après la foreach boucle, s’il y a au moins un produit sélectionné, la méthode de la classe ProductsBLL est appelée et cette liste lui est passée. L’étiquette DeleteResults est également affichée et les données rebondissent vers GridView (afin que les enregistrements nouvellement supprimés n’apparaissent plus sous forme de lignes dans la grille).

La figure 4 montre gridView après la sélection d’un certain nombre de lignes pour suppression. La figure 5 montre l’écran immédiatement après le clic sur le bouton Supprimer les produits sélectionnés. Notez que dans la figure 5, les ProductID valeurs des enregistrements supprimés sont affichées dans l’étiquette sous GridView et ces lignes ne sont plus dans GridView.

Les produits sélectionnés seront supprimés

Figure 4 : Les produits sélectionnés seront supprimés (cliquez pour afficher l’image de taille complète)

Les valeurs ProductID des produits supprimés sont répertoriées sous GridView

Figure 5 : Les valeurs des produits ProductID supprimés sont répertoriées sous GridView (cliquez pour afficher l’image de taille complète)

Remarque

Pour tester l’atomicité de la DeleteProductsWithTransaction méthode, ajoutez manuellement une entrée pour un produit dans la Order Details table, puis tentez de supprimer ce produit (ainsi que d’autres). Vous recevrez une violation de contrainte de clé étrangère lorsque vous tentez de supprimer le produit qui a une commande associée, mais notez comment les autres suppressions de produits sélectionnés sont annulées.

Résumé

La création d’une interface de suppression de lots implique l’ajout d’un GridView avec une colonne de cases à cocher et un contrôle Web de type Button qui, lorsqu’il est cliqué, supprime toutes les lignes sélectionnées en tant qu’opération atomique unique. Dans ce tutoriel, nous avons créé une telle interface en assemblant des travaux réalisés dans deux didacticiels précédents, Ajout d’une colonne GridView de cases à cocher et Enrobage des modifications de la base de données dans une transaction. Dans le premier tutoriel, nous avons créé un GridView avec une colonne de cases à cocher, et dans le second, nous avons implémenté une méthode dans la BLL qui, lorsqu’on lui passe un ensemble de valeurs List<T>ProductID, les supprime toutes dans le cadre d'une transaction.

Dans le tutoriel suivant, nous allons créer une interface pour effectuer des insertions par lots.

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 en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. On peut le joindre à mitchell@4GuysFromRolla.com.

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