Udostępnij za pomocą


Usuwanie zbiorcze (C#)

Autor : Scott Mitchell

Pobierz plik PDF

Dowiedz się, jak usunąć wiele rekordów bazy danych w ramach jednej operacji. W warstwie interfejsu użytkownika bazujemy na ulepszonym widoku siatki utworzonym we wcześniejszym samouczku. W warstwie dostępu do danych opakowujemy wiele operacji usuwania w ramach transakcji, aby upewnić się, że wszystkie operacje usuwania kończą się powodzeniem lub wszystkie usunięcia zostaną wycofane.

Wprowadzenie

W poprzednim samouczku przedstawiono sposób tworzenia interfejsu edytowania wsadowego przy użyciu w pełni edytowalnego elementu GridView. W sytuacjach, gdy użytkownicy często edytują wiele rekordów jednocześnie, interfejs edycji wsadowej będzie wymagał znacznie mniej przesyłania danych zwrotnych i przełączania kontekstu z klawiatury na mysz, co poprawia wydajność użytkownika końcowego. Ta technika jest podobnie przydatna w przypadku stron, na których użytkownicy często usuwają wiele rekordów w jednym miejscu.

Każda osoba, która korzystała z klienta poczty e-mail online, zna już jeden z najbardziej typowych interfejsów usuwania partii: pole wyboru w każdym wierszu w siatce z odpowiednim przyciskiem Usuń wszystkie zaznaczone elementy (zobacz Rysunek 1). Ten samouczek jest dość krótki, ponieważ wykonaliśmy już całą ciężką pracę w poprzednich samouczkach podczas tworzenia zarówno interfejsu internetowego, jak i metody usuwania serii rekordów jako pojedynczej operacji niepodzielnej. W samouczku Dodawanie kolumny GridView pól wyboru utworzyliśmy GridView z kolumną pól wyboru, a w samouczku Zawijanie modyfikacji bazy danych w ramach transakcji utworzyliśmy metodę w warstwie biznesowej (BLL), która będzie używać transakcji do usuwania List<T>ProductID wartości. W tym samouczku wykorzystamy i połączymy nasze wcześniejsze doświadczenia, aby stworzyć działający przykład usuwania hurtowego.

Każdy wiersz zawiera pole wyboru

Rysunek 1. Każdy wiersz zawiera pole wyboru (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Krok 1. Tworzenie interfejsu usuwania partii

Ponieważ w samouczku Dodawanie kolumny pól wyboru w widoku siatki utworzyliśmy już interfejs do usuwania wsadowego, możemy go po prostu skopiować do BatchDelete.aspx, zamiast tworzyć go od podstaw. Zacznij od otwarcia strony BatchDelete.aspx w folderze BatchData oraz strony CheckBoxField.aspx w folderze EnhancedGridView. Na stronie CheckBoxField.aspx przejdź do widoku źródła i skopiuj znaczniki między tagami <asp:Content>, jak pokazano na rysunku 2.

Skopiuj deklaratywny znacznik CheckBoxField.aspx do Schowka

Rysunek 2. Kopiowanie deklaratywnego znacznika CheckBoxField.aspx do Schowka (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Następnie przejdź do widoku Źródło w BatchDelete.aspx i wklej zawartość schowka w tagach <asp:Content>. Skopiuj i wklej kod z klasy "code-behind" z CheckBoxField.aspx.cs do klasy "code-behind" w BatchDelete.aspx.cs (procedura obsługi zdarzenia przycisku DeleteSelectedProducts, metoda Click oraz procedury obsługi zdarzeń dla przycisków ToggleCheckState i Click). Po skopiowaniu tej zawartości BatchDelete.aspx klasa s code-behind strony powinna zawierać następujący kod:

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);
    }
}

Po skopiowaniu znaczników deklaracyjnych i kodu źródłowego, poświęć chwilę na sprawdzenie BatchDelete.aspx za pomocą przeglądarki. Powinieneś zobaczyć element GridView, który zawiera listę pierwszych dziesięciu produktów, z każdym wierszem listy zawierającym nazwę, kategorię i cenę produktu wraz z polem wyboru. Powinny istnieć trzy przyciski: Zaznacz wszystko, Odznacz wszystko i Usuń wybrane produkty. Kliknięcie przycisku Zaznacz wszystko powoduje zaznaczenie wszystkich pól wyboru, podczas gdy przycisk Usuń zaznaczenie wszystkich usuwa zaznaczenie wszystkich pól wyboru. Kliknięcie pozycji Usuń wybrane produkty powoduje wyświetlenie komunikatu z listą ProductID wartości wybranych produktów, ale w rzeczywistości nie powoduje usunięcia produktów.

Interfejs z CheckBoxField.aspx został przeniesiony do BatchDeleting.aspx

Rysunek 3. Interfejs z CheckBoxField.aspx został przeniesiony do BatchDeleting.aspx (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Krok 2. Usuwanie zaznaczonych produktów przy użyciu transakcji

Po pomyślnym skopiowaniu interfejsu usuwania wsadowego do BatchDeleting.aspx, pozostało tylko zaktualizować kod, aby przycisk Usuń wybrane produkty usuwał zaznaczone produkty, używając metody DeleteProductsWithTransaction w klasie ProductsBLL. Ta metoda, dodana w samouczku Zawijanie modyfikacji bazy danych w ramach transakcji, przyjmuje List<T>ProductID jako dane wejściowe i usuwa każdy odpowiadający ProductID w zakresie transakcji.

Proces obsługi zdarzeń dla przycisku DeleteSelectedProducts używa obecnie następującej pętli Click do iterowania po każdym wierszu w GridView foreach.

// 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);
        //............................................
    }
}

Dla każdego wiersza kontrolka ProductSelector sieci Web CheckBox jest programowo przywoływana. Jeśli jest zaznaczone, wiersz ProductID jest pobierany z kolekcji DataKeys, a właściwość etykiety DeleteResults jest uaktualniana, żeby uwzględnić wiadomość informującą, że wiersz został wybrany do usunięcia.

Powyższy kod nie usuwa żadnych rekordów, ponieważ wywołanie ProductsBLL metody klasy s Delete jest oznaczane jako komentarz. Gdyby ta logika usuwania została zastosowana, kod usunąłby produkty, ale nie w ramach operacji niepodzielnej. Oznacza to, że jeśli pierwsze kilka operacji usuwania w sekwencji zakończyło się pomyślnie, ale późniejsza nie powiodła się (być może z powodu naruszenia ograniczenia klucza obcego), wyjątek zostanie zgłoszony, ale usunięte produkty pozostaną usunięte.

Aby zapewnić niepodzielność, musimy zamiast tego użyć ProductsBLL metody s DeleteProductsWithTransaction klasy. Ponieważ ta metoda akceptuje listę ProductID wartości, musimy najpierw skompilować tę listę z siatki, a następnie przekazać ją jako parametr. Najpierw utworzymy wystąpienie List<T> typu int. W obrębie pętli musimy dodać wartości wybranych produktów foreach do tego ProductID. Po zakończeniu pętli należy przekazać ten List<T> do metody ProductsBLL klasy DeleteProductsWithTransaction. Zaktualizuj procedurę obsługi zdarzeń DeleteSelectedProducts przycisku Click przy użyciu następującego kodu:

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();
    }
}

Zaktualizowany kod tworzy List<T> typu int (productIDsToDelete) i uzupełnia go wartościami ProductID przeznaczonymi do usunięcia. Po pętli foreach, jeśli zaznaczono co najmniej jeden produkt, metoda klasy ProductsBLL jest wywoływana DeleteProductsWithTransaction i przekazuje tę listę. Etykieta DeleteResults jest również wyświetlana, a dane są ponownie powiązane z widokiem siatki (tak aby nowo usunięte rekordy nie pojawiały się już jako wiersze w widoku siatki).

Rysunek 4 przedstawia element GridView po wybraniu kilku wierszy do usunięcia. Rysunek 5 przedstawia ekran natychmiast po kliknięciu przycisku Usuń wybrane produkty. Należy pamiętać, że na rysunku 5 wartości rekordów usuniętych są wyświetlane w etykiecie pod GridView, a te wiersze nie znajdują się już w kontrolce GridView.

Wybrane produkty zostaną usunięte

Rysunek 4. Wybrane produkty zostaną usunięte (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Wartości ProductID usuniętych produktów są wyświetlane pod GridView

Rysunek 5. Usunięte wartości produktów ProductID są wyświetlane pod kontrolką GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)

Uwaga / Notatka

Aby przetestować atomiczność metody DeleteProductsWithTransaction, ręcznie dodaj wpis dla produktu w tabeli Order Details, a następnie spróbuj usunąć ten produkt (wraz z innymi). Podczas próby usunięcia produktu ze skojarzonym zamówieniem otrzymasz naruszenie ograniczenia klucza obcego, ale zwróć uwagę na sposób wycofania innych wybranych produktów.

Podsumowanie

Utworzenie interfejsu usuwania wsadowego obejmuje dodanie kontrolki GridView z kolumną przycisków wyboru oraz kontrolki przycisku internetowego, która po kliknięciu spowoduje usunięcie wszystkich zaznaczonych wierszy jako pojedynczej operacji atomowej. W tym samouczku utworzyliśmy taki interfejs, łącząc pracę wykonaną w dwóch poprzednich samouczkach: Dodawanie kolumny GridView pól wyboru i zawijanie modyfikacji bazy danych w ramach transakcji. W pierwszym samouczku utworzyliśmy GridView z kolumną pól wyboru, a w drugim zaimplementowaliśmy metodę w warstwie logiki biznesowej (BLL), która po przekazaniu zbioru wartości List<T>ProductID skasowała je wszystkie w ramach transakcji.

W następnym samouczku utworzymy interfejs do wsadowego wstawiania.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można go uzyskać pod adresem mitchell@4GuysFromRolla.com.

Specjalne podziękowania

Ta seria samouczków została omówiona przez wielu przydatnych recenzentów. Główni recenzenci dla tego samouczka to Hilton Giesenow i Teresa Murphy. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, napisz do mnie na adres mitchell@4GuysFromRolla.com.