Udostępnij za pomocą


Wstawianie w partiach (C#)

Autor : Scott Mitchell

Pobierz plik PDF

Dowiedz się, jak wstawić wiele rekordów bazy danych w jednej operacji. W warstwie interfejsu użytkownika rozszerzymy kontrolkę GridView, aby umożliwić użytkownikowi wprowadzanie wielu nowych rekordów. W warstwie dostępu do danych opakowujemy wiele operacji wstawiania w ramach transakcji, tak aby upewnić się, że wszystkie wstawienia powiodą się, albo wszystkie wstawienia zostaną wycofane.

Wprowadzenie

W samouczku dotyczącym aktualizacji usługi Batch przyjrzeliśmy się dostosowywaniu kontrolki GridView w celu przedstawienia interfejsu, w którym można edytować wiele rekordów. Użytkownik odwiedzający stronę może wprowadzić serię zmian, a następnie za pomocą jednego kliknięcia przycisku wykonać aktualizację wsadową. W sytuacjach, gdy użytkownicy często aktualizują wiele rekordów naraz, taki interfejs może znacząco ograniczyć liczbę kliknięć i przełączanie z klawiatury na mysz, w porównaniu z domyślnymi funkcjami edycji poszczególnych wierszy, które po raz pierwszy przedstawiono w samouczku Omówienie wstawiania, aktualizowania i usuwania danych.

Tę koncepcję można również zastosować podczas dodawania rekordów. Załóżmy, że w firmie Northwind Traders często otrzymujemy przesyłki od dostawców, którzy zawierają wiele produktów dla określonej kategorii. Na przykład możemy otrzymać przesyłkę sześciu różnych produktów do herbaty i kawy od Firmy Tokyo Traders. Jeśli użytkownik wprowadzi sześć produktów pojedynczo za pomocą kontrolki DetailsView, będzie musiał wybrać wiele tych samych wartości na nowo: będzie musiał wybrać tę samą kategorię (Napoje), tego samego dostawcę (Tokyo Traders), tę samą wartość przerwaną (Fałsz) i te same jednostki w wartości zamówienia (0). Ten powtarzalny wpis danych jest nie tylko czasochłonny, ale także podatny na błędy.

Przy odrobinie pracy możemy utworzyć interfejs wstawiania wsadowego, który umożliwia użytkownikowi wybranie dostawcy i kategorii raz, wprowadzenie serii nazw produktów i cen jednostkowych, a następnie kliknięcie przycisku w celu dodania nowych produktów do bazy danych (zobacz Rysunek 1). Gdy każdy produkt jest dodawany, pola danych ProductName i UnitPrice są przypisywane wartościom wprowadzonym w TextBoxach, podczas gdy wartości CategoryID i SupplierID są przypisywane z list rozwijanych u góry formularza. Wartości Discontinued i UnitsOnOrder są ustawione odpowiednio na zakodowane wartości false i 0.

Interfejs wsadowego wstawiania

Rysunek 1: Interfejs wstawiania batchowego (kliknij, aby wyświetlić obraz pełnowymiarowy)

W tym samouczku utworzymy stronę, która wdraża interfejs do masowego wstawiania, pokazany na rysunku 1. Podobnie jak w przypadku dwóch poprzednich samouczków, opakujemy wstawienia w zakresie transakcji, aby zapewnić niepodzielność. Zacznijmy!

Krok 1. Tworzenie interfejsu wyświetlania

Ten samouczek składa się z jednej strony podzielonej na dwa regiony: region wyświetlania i region wstawiania. Interfejs wyświetlania, który utworzymy w tym kroku, pokazuje produkty w elementy GridView i zawiera przycisk zatytułowany Proces wysyłki produktu. Po kliknięciu tego przycisku interfejs wyświetlania zostanie zastąpiony interfejsem wstawiania, który jest wyświetlany na rysunku 1. Interfejs wyświetlania powraca po kliknięciu przycisków Dodaj produkty z wysyłki lub Anuluj. Utworzymy interfejs wstawiania w kroku 2.

Podczas tworzenia strony, która ma dwa interfejsy, tylko jeden z nich jest widoczny w danym momencie, każdy interfejs jest zwykle umieszczany w kontrolce Panel sieci Web, która służy jako kontener dla innych kontrolek. W związku z tym nasza strona będzie mieć dwa kontrolki Panelu, jeden dla każdego interfejsu.

Rozpocznij od otwarcia BatchInsert.aspx strony w folderze BatchData i przeciągnięcia Panelu z przybornika do obszaru projektowego (zobacz Rysunek 2). Ustaw właściwość ID panelu na DisplayInterface. Podczas dodawania Panelu do Projektanta, jego właściwości Height oraz Width są ustawiane odpowiednio na 50 pikseli i 125 pikseli. Wyczyść te wartości właściwości w oknie Właściwości.

Przeciągnij Panel z przybornika do Projektanta

Rysunek 2: Przeciągnij panel z przybornika do projektanta (Kliknij, aby wyświetlić obraz pełnowymiarowy)

Następnie przeciągnij przycisk i kontrolkę widoku siatki do panelu. Ustaw właściwość ID przycisku na ProcessShipment i jego właściwość Text na Przetwarzanie Wysyłki Produktu. Ustaw właściwość GridView ID na ProductsGrid i, z tagu Smart, powiąż ją z nowym obiektem danych ObjectDataSource o nazwie ProductsDataSource. Skonfiguruj obiekt ObjectDataSource, aby ściągnąć dane z ProductsBLL metody klasy s GetProducts . Ponieważ ten element GridView jest używany tylko do wyświetlania danych, ustaw listy rozwijane na kartach UPDATE, INSERT i DELETE na wartość (Brak). Kliknij przycisk Zakończ, aby ukończyć pracę kreatora konfiguracji źródła danych.

Wyświetl dane zwrócone z metody GetProducts klasy ProductsBLL

Rysunek 3. Wyświetlanie danych zwróconych z ProductsBLL metody klasy GetProducts (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Ustaw listy Drop-Down na kartach UPDATE, INSERT i DELETE na (Brak)

Rysunek 4. Ustawianie list Drop-Down na kartach UPDATE, INSERT i DELETE na wartość (Brak) (Kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po zakończeniu pracy kreatora ObjectDataSource, Visual Studio doda pola BoundFields oraz CheckBoxField do pól danych produktu. Usuń wszystkie pola ProductName, , CategoryName, SupplierNameUnitPrice, i Discontinued . Możesz swobodnie wprowadzać wszelkie dostosowania estetyczne. Postanowiłem sformatować UnitPrice pole jako wartość waluty, zmienić kolejność pól i zmienić nazwę kilku wartości pól HeaderText . Skonfiguruj również kontrolkę GridView tak, aby zawierała obsługę stronicowania i sortowania, zaznaczając pola wyboru Włącz stronicowanie i Włącz sortowanie w tagu inteligentnym GridView.

Po dodaniu kontrolek Panel, Button, GridView i ObjectDataSource oraz dostosowaniu pól GridView znacznik deklaratywny strony powinien wyglądać podobnie do następującego:

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

Należy pamiętać, że kod przycisku i kontrolki GridView pojawia się wewnątrz znaczników otwierających i zamykających <asp:Panel>. Ponieważ te kontrolki znajdują się w DisplayInterface panelu, możemy je ukryć, ustawiając właściwość Panel Visible na false. Krok 3 omawia programowe zmienianie właściwości Panel dla Visible w odpowiedzi na kliknięcie przycisku, aby pokazać jeden interfejs i ukryć drugi.

Znajdź chwilę, aby wyświetlić nasz postęp w przeglądarce. Jak pokazano na rysunku 5, powinien zostać wyświetlony przycisk Przetwarzanie wysyłki produktu nad kontrolką GridView zawierającą listę produktów dziesięć naraz.

Kontrolka GridView wyświetla produkty oraz oferuje możliwości sortowania i stronicowania

Rysunek 5. Widok GridView wyświetla listę produktów i ofert możliwości sortowania i stronicowania (kliknij, aby wyświetlić obraz pełnowymiarowy)

Krok 2. Tworzenie interfejsu wstawiania

Po zakończeniu interfejsu wyświetlania możemy utworzyć interfejs wstawiania. Na potrzeby tego samouczka utwórzmy interfejs wstawiania, który monituje o podanie pojedynczej wartości dostawcy i kategorii, a następnie umożliwia użytkownikowi wprowadzanie maksymalnie pięciu nazw produktów i wartości cen jednostkowych. Dzięki temu interfejsowi użytkownik może dodać jeden do pięciu nowych produktów, które mają tę samą kategorię i dostawcę, ale mają unikatowe nazwy produktów i ceny.

Zacznij od przeciągnięcia Panelu z przybornika do edytora projektów, umieszczając go pod istniejącym DisplayInterface Panelem. ID Ustaw właściwość tego nowo dodanego panelu na InsertingInterface i ustaw jej Visible właściwość na false. Dodamy kod, który w kroku 3 ustawia właściwość InsertingInterface na Visible w Panelu true. Wyczyść również wartości właściwości paneli Height i Width.

Następnie musimy utworzyć interfejs wstawiania, który został pokazany z powrotem na rysunku 1. Ten interfejs można utworzyć za pomocą różnych technik HTML, ale użyjemy dość prostego: czterokolumna, siedmiorzędowa tabela.

Uwaga / Notatka

Podczas wprowadzania znaczników dla elementów HTML <table> wolę używać widoku Źródło. Chociaż program Visual Studio ma narzędzia do dodawania <table> elementów za pośrednictwem projektanta, projektant wydaje się zbyt skłonny do wprowadzania nieproszonych style ustawień do znaczników. Po utworzeniu <table> markup zwykle wracam do Projektanta, aby dodać kontrolki Web i ustawić ich właściwości. Podczas tworzenia tabel ze wstępnie określonymi kolumnami i wierszami preferuję używanie statycznego kodu HTML zamiast kontrolki Table Web, ponieważ wszelkie kontrolki sieci Web umieszczone w kontrolce Table Web mogą być dostępne tylko przy użyciu wzorca . Używam jednak kontrolek sieci Web tabel dla tabel o dynamicznym rozmiarze (tych, których wiersze lub kolumny są oparte na niektórych kryteriach bazy danych lub określonych przez użytkownika), ponieważ kontrolka tabela sieci Web może być konstruowana programowo.

Wprowadź następujące znaczniki w tagach <asp:Panel> panelu InsertingInterface:

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

Ten <table> znacznik nie zawiera jeszcze żadnych kontrolek sieci Web. Dodamy te chwilowo. Należy pamiętać, że każdy <tr> element zawiera określone ustawienie klasy CSS: BatchInsertHeaderRow dla wiersza nagłówka, w którym będą znajdować się listy rozwijane dostawcy i kategorii; BatchInsertFooterRow w wierszu stopki, gdzie zostaną umieszczone przyciski "Dodaj produkty z wysyłki" i "Anuluj"; oraz naprzemiennie BatchInsertRow i BatchInsertAlternatingRow dla wierszy, które będą zawierać pola tekstowe Product i Unit Price. W pliku utworzono odpowiednie klasy Styles.css CSS, aby dać interfejsowi wstawiania wygląd podobny do kontrolek GridView i DetailsView, które zostały użyte w tych samouczkach. Poniżej przedstawiono te klasy CSS.

/*** 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;
}

Po wprowadzeniu tego znacznika wróć do widoku projektowania. To <table> powinno być wyświetlane jako czterokolumnowa, siedmiowierszowa tabela w projektancie, jak pokazano na rysunku 6.

Interfejs wstawiania składa się z tabeli Seven-Row, posiadającej cztery kolumny

Rysunek 6. Interfejs wstawiania składa się z czterech kolumn, Seven-Row tabeli (kliknij, aby wyświetlić obraz pełnowymiarowy)

Teraz możemy dodać kontrolki sieci Web do interfejsu wstawiania. Przeciągnij dwie listy rozwijane z narzędzi do odpowiednich komórek w tabeli: jedna dla dostawcy, a druga dla kategorii.

Ustaw właściwość dostawcy DropDownList ID na Suppliers i powiąż ją z nowym obiektem ObjectDataSource o nazwie SuppliersDataSource. Skonfiguruj nowy obiekt ObjectDataSource, aby pobrać dane z klasy SuppliersBLL i jej metody GetSuppliers oraz ustawić listę rozwijaną karty UPDATE na (Brak). Kliknij przycisk Zakończ, aby ukończyć pracę kreatora.

Konfigurowanie obiektu ObjectDataSource do używania metody GetSuppliers klasy SuppliersBLL

Rysunek 7: Skonfiguruj źródło danych ObjectDataSource do używania metody klasy SuppliersBLLGetSuppliers (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Wyświetl pole danych Suppliers w DropDownList CompanyName i użyj pola danych SupplierID jako wartości ListItem.

Wyświetl pole danych CompanyName i użyj pola SupplierID jako wartości

Rysunek 8. Wyświetlanie CompanyName pola danych i użycie SupplierID jako wartości (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Nazwij drugą listę DropDownList Categories i powiąż ją z nowym obiektem ObjectDataSource o nazwie CategoriesDataSource. Skonfiguruj obiekt CategoriesDataSource ObjectDataSource, aby używał metody CategoriesBLL klasy sGetCategories; ustaw listy rozwijane na zakładkach UPDATE i DELETE na wartość (Brak), a następnie kliknij przycisk Zakończ, aby ukończyć pracę kreatora. Na koniec pole danych CategoryName powinno zostać wyświetlone w rozwijanej liście, a CategoryID użyte jako wartość.

Po dodaniu tych dwóch list rozwijanych i powiązaniu ich z odpowiednio skonfigurowanymi obiektami ObjectDataSources, ekran powinien wyglądać podobnie do rysunku 9.

Wiersz nagłówka zawiera teraz listy rozwijane Dostawców i kategorii

Rysunek 9. Wiersz nagłówka zawiera teraz listy Suppliers rozwijane i Categories (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Teraz musimy utworzyć pola tekstowe, aby zebrać nazwę i cenę dla każdego nowego produktu. Przeciągnij kontrolkę TextBox z Przybornika do Projektanta dla każdej z pięciu nazw produktów oraz dla każdego wiersza z ceną. Ustaw właściwości kontrolek TextBoxes na ID, ProductName1, UnitPrice1, ProductName2, UnitPrice2, ProductName3, itd.

Dodaj moduł CompareValidator po każdej cenie jednostkowej TextBoxes, ustawiając ControlToValidate właściwość na odpowiednią IDwartość . Również ustaw właściwość Operator na wartość GreaterThanEqual, ValueToCompare na wartość 0 i Type na wartość Currency. Te ustawienia instruują moduł CompareValidator, aby upewnić się, że wprowadzona cena jest prawidłową wartością waluty większą lub równą zero. Text Ustaw właściwość na *, a ErrorMessage na Cena musi być większa lub równa zeru. Ponadto pomiń wszystkie symbole waluty.

Uwaga / Notatka

Interfejs wstawiania nie zawiera żadnych kontrolek RequiredFieldValidator, mimo że pole ProductName w tabeli bazy danych Products nie zezwala na wartości NULL. Dzieje się tak, ponieważ chcemy zezwolić użytkownikowi na wprowadzanie maksymalnie pięciu produktów. Jeśli na przykład użytkownik podał nazwę produktu i cenę jednostkową dla pierwszych trzech wierszy, pozostawiając ostatnie dwa wiersze puste, dodalibyśmy tylko trzy nowe produkty do systemu. Ponieważ ProductName jest to jednak wymagane, musimy programowo sprawdzić, czy w przypadku wprowadzenia ceny jednostkowej zostanie podana odpowiednia wartość nazwy produktu. Poradzimy sobie z tym sprawdzianem w kroku 4.

Podczas sprawdzania poprawności danych wejściowych użytkownika funkcja CompareValidator zgłasza nieprawidłowe dane, jeśli wartość zawiera symbol waluty. Dodaj znak $ przed każdą ceną jednostkową w polach tekstowych, aby służył jako wizualny wskaźnik, który nakazuje użytkownikowi pominięcie symbolu waluty podczas wprowadzania ceny.

Na koniec dodaj kontrolkę ValidationSummary w panelu InsertingInterface , ustaw jej ShowMessageBox właściwość na true i jej ShowSummary właściwość na false. W przypadku tych ustawień, jeśli użytkownik wprowadzi nieprawidłową wartość ceny jednostkowej, gwiazdka pojawi się obok kontrolek TextBox, a właściwość ValidationSummary wyświetli pole komunikatu po stronie klienta, które pokazuje wcześniej określony komunikat o błędzie.

Na tym etapie ekran powinien wyglądać podobnie do rysunku 10.

Interfejs wstawiania zawiera teraz pola tekstowe dla nazw i cen produktów

Rysunek 10. Interfejs wstawiania zawiera teraz pola tekstowe nazw produktów i cen (kliknij, aby wyświetlić obraz pełnowymiarowy)

Następnie musimy dodać przyciski Dodaj produkty z wysyłki i Anulowania do wiersza stopki. Przeciągnij dwie kontrolki Przycisk z Przybornika do stopki interfejsu wstawiania, ustawiając właściwości pierwszego Przycisku na ID, a właściwości drugiego Przycisku na AddProducts i CancelButton oznaczone jako "Dodaj Produkty z Wysyłki" oraz "Anuluj". Ponadto ustaw właściwość CancelButton kontrolki CausesValidation na wartość false.

Na koniec musimy dodać webową kontrolkę etykiety, która będzie wyświetlać komunikaty o stanie dla dwóch interfejsów. Na przykład gdy użytkownik pomyślnie doda nową przesyłkę produktów, chcemy wrócić do interfejsu wyświetlania i wyświetlić komunikat potwierdzający. Jeśli jednak użytkownik podaje cenę nowego produktu, ale pomija nazwę produktu, musimy wyświetlić komunikat ostrzegawczy, ponieważ ProductName pole jest wymagane. Ponieważ ten komunikat jest potrzebny do wyświetlenia dla obu interfejsów, umieść go w górnej części strony poza panelami.

Przeciągnij kontrolkę etykiety Web z przybornika na górę strony w Projektancie. Ustaw właściwość ID na StatusLabel, wyczyść właściwość Text i ustaw właściwości Visible i EnableViewState na false. Jak widzieliśmy w poprzednich samouczkach, ustawienie właściwości EnableViewState na false pozwala nam programowo zmienić wartości właściwości elementu Label i automatycznie przywrócić je do wartości domyślnych przy kolejnym wykonaniu operacji zwrotnej. Upraszcza to kod wyświetlania komunikatu o stanie w odpowiedzi na akcję użytkownika, która znika po kolejnym poście zwrotnym. Na koniec ustaw właściwość StatusLabel kontrolki CssClass na „Ostrzeżenie”, która jest nazwą klasy CSS, zdefiniowaną w Styles.css, wyświetlającą tekst dużą, kursywną, pogrubioną, czerwoną czcionką.

Rysunek 11 przedstawia projektanta programu Visual Studio po dodaniu i skonfigurowaniu etykiety.

Umieść kontrolkę StatusLabel nad kontrolkami dwóch paneli

Rysunek 11. Umieść kontrolkę StatusLabel nad dwoma kontrolkami Panelu (kliknij, aby wyświetlić obraz pełnowymiarowy)

Krok 3. Przełączanie między interfejsami wyświetlania i wstawiania

Na tym etapie ukończyliśmy strukturę dla naszego wyświetlacza i wstawiania interfejsów, ale nadal pozostały nam dwa zadania.

  • Przełączanie między interfejsami wyświetlania i wstawiania
  • Dodawanie produktów w przesyłce do bazy danych

Obecnie interfejs wyświetlania jest widoczny, ale interfejs wstawiania jest ukryty. Jest to spowodowane tym, że DisplayInterface właściwość Panel jest Visible ustawiona na true (wartość domyślna), podczas gdy InsertingInterface właściwość Panel Visible jest ustawiona na false. Aby przełączać się między dwoma interfejsami, wystarczy przełączyć każdą wartość właściwości kontrolki Visible .

Chcemy przejść z interfejsu wyświetlania do interfejsu dodawania, gdy klikniemy przycisk Przetwarzaj przesyłkę produktu. W związku z tym utwórz procedurę obsługi zdarzeń dla tego zdarzenia Przycisku Click zawierającego następujący kod:

protected void ProcessShipment_Click(object sender, EventArgs e)
{
    DisplayInterface.Visible = false;
    InsertingInterface.Visible = true;
}

Ten kod po prostu ukrywa DisplayInterface panel i wyświetla InsertingInterface panel.

Następnie utwórz procedury zdarzeń dla kontrolek 'Dodaj produkty z przesyłki' i 'Anuluj' w interfejsie dodawania. Po kliknięciu jednego z tych przycisków musimy przywrócić interfejs wyświetlania. Utwórz Click procedury obsługi zdarzeń dla obu kontrolek Przycisk, aby wywoływać metodę ReturnToDisplayInterface, którą dodamy wkrótce. Oprócz ukrycia InsertingInterface panelu i wyświetlenia DisplayInterface panelu, metoda ReturnToDisplayInterface musi zwrócić kontrolki webowe do stanu wstępnej edycji. Obejmuje to ustawienie właściwości DropDownLists SelectedIndex na 0 i wyczyszczenie Text właściwości kontrolek TextBox.

Uwaga / Notatka

Rozważ, co może się zdarzyć, jeśli nie zwróciliśmy kontrolek do stanu przed edycją przed powrotem do interfejsu wyświetlania. Użytkownik może kliknąć przycisk Przetwarzaj przesyłkę produktu, wprowadzić produkty z przesyłki, a następnie kliknąć pozycję Dodaj produkty z wysyłki. Spowoduje to dodanie produktów i zwrócenie użytkownika do interfejsu wyświetlania. W tym momencie użytkownik może chcieć dodać kolejną przesyłkę. Po kliknięciu przycisku 'Proces wysyłki produktu' zostaną przekierowani do interfejsu wstawiania, ale wybory z listy rozwijanej (DropDownList) i wartości w polach tekstowych (TextBox) będą nadal wypełniane ich wcześniejszymi wartościami.

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

Oba Click programy obsługi zdarzeń po prostu wywołują metodę ReturnToDisplayInterface, chociaż wrócimy do obsługi zdarzenia "Dodaj produkty z przesyłki" w kroku 4 i dodamy kod, aby zapisać produkty. ReturnToDisplayInterface rozpoczyna się od przywrócenia list rozwijanych Suppliers i Categories do ich pierwszych pozycji. Dwie stałe firstControlID i lastControlID oznaczają początkowe i końcowe wartości indeksu kontrolnego używane do nazewnictwa TextBoxów nazwy produktu i ceny jednostkowej w interfejsie wstawiania, i są używane w ramach for pętli, która ustawia Text właściwości kontrolek TextBox z powrotem na pusty ciąg. Na koniec właściwości Paneli Visible są resetowane, aby interfejs wstawiania był ukryty, a interfejs wyświetlania był widoczny.

Pośmiń chwilę na przetestowanie tej strony w przeglądarce. Podczas pierwszej wizyty na stronie powinien zostać wyświetlony interfejs wyświetlania, jak pokazano na rysunku 5. Kliknij przycisk Przetwarzaj przesyłkę produktu. Strona zostanie przeładowana i powinien zostać wyświetlony interfejs wstawiania, jak pokazano na rysunku 12. Kliknięcie przycisku Dodaj produkty z wysyłki lub Anuluj powoduje powrót do interfejsu wyświetlania.

Uwaga / Notatka

Podczas przeglądania interfejsu wstawiania poświęć chwilę, aby przetestować walidatory CompareValidators w polach tekstowych dotyczących ceny jednostkowej. Po kliknięciu przycisku "Dodaj produkty z wysyłki" powinien zostać wyświetlony komunikat ostrzegawczy po stronie klienta dotyczący nieprawidłowych wartości waluty lub cen, których wartości są mniejsze niż zero.

Interfejs wstawiania jest wyświetlany po kliknięciu przycisku Proces wysyłki produktu

Rysunek 12: Interfejs dodawania jest wyświetlany po kliknięciu przycisku Proces wysyłki produktu (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 4. Dodawanie produktów

Wszystko, co pozostaje w tym samouczku, to zapisanie produktów w bazie danych w procedurze obsługi zdarzenia "Dodaj produkty z przycisku wysyłki" Click. Można to osiągnąć poprzez utworzenie ProductsDataTable oraz dodanie instancji ProductsRow dla każdej z dostarczonych nazw produktów. Po dodaniu tych ProductsRow elementów wywołamy metodę ProductsBLL klasy s UpdateWithTransaction przekazującą ProductsDataTableelement . Pamiętaj, że metoda utworzona w samouczku , dotyczącym obudowywania modyfikacji bazy danych w ramach transakcji, przekazuje do metody i . W tym miejscu uruchomiana jest transakcja ADO.NET, a TableAdapter wydaje instrukcję INSERT do bazy danych dla każdej dodanej wartości ProductsRow w tabeli DataTable. Zakładając, że wszystkie produkty są dodawane bez błędu, transakcja zostanie zatwierdzona, w przeciwnym razie zostanie wycofana.

Kod obsługi zdarzeń przycisku Dodaj produkty z wysyłki Click również musi przeprowadzać pewne sprawdzanie błędów. Ponieważ w interfejsie wstawiania brak jest walidatorów wymaganych pól (RequiredFieldValidators), użytkownik może wprowadzić cenę produktu, pomijając jego nazwę. Ponieważ nazwa produktu jest wymagana, jeśli taki warunek się pojawi, musimy powiadomić użytkownika i nie kontynuować działań. Kompletny Click kod procedury obsługi zdarzeń jest następujący:

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

Procedura obsługi zdarzeń rozpoczyna się od upewnienia się, że Page.IsValid właściwość zwraca wartość true. Jeśli zwraca false wartość, oznacza to, że jeden lub więcej elementów CompareValidator zgłasza nieprawidłowe dane. W takim przypadku nie chcemy próbować wstawiać wprowadzonych produktów, bo skończymy z wyjątkiem podczas próby przypisania wartości ceny jednostkowej wprowadzonej przez użytkownika do właściwości ProductsRowUnitPrice.

Następnie utworzone zostanie nowe ProductsDataTable wystąpienie (products). Pętla for służy do iterowania przez pola tekstowe z nazwą produktu i ceną jednostkową, a właściwości Text są odczytywane do zmiennych lokalnych productName i unitPrice. Jeśli użytkownik wprowadził wartość ceny jednostkowej, ale nie dla odpowiedniej nazwy produktu, StatusLabel wyświetla komunikat: "Jeśli podasz cenę jednostkową, musisz także zawrzeć nazwę produktu", a obsługa zdarzenia zostaje zakończona.

Jeśli podano nazwę produktu, zostanie utworzone nowe ProductsRow wystąpienie przy użyciu ProductsDataTable metody s NewProductsRow . Ta nowa właściwość wystąpienia ProductsRow jest ustawiona na bieżącą nazwę produktu w polu tekstowym, podczas gdy właściwości ProductName i SupplierID są przypisane do właściwości listy rozwijanej w nagłówku interfejsu wstawiania. Jeśli użytkownik wprowadził wartość ceny produktu, zostanie przypisana do właściwości instancji ProductsRow. W przeciwnym razie właściwość pozostanie nieprzypisana, co spowoduje wartość UnitPrice dla NULL w bazie danych. Na koniec właściwości Discontinued i UnitsOnOrder są przypisane do wartości zakodowanych na sztywno false i 0, odpowiednio.

Po przypisaniu właściwości do wystąpienia ProductsRow, zostaje ono dodane do ProductsDataTable.

Po zakończeniu for pętli sprawdzamy, czy zostały dodane jakiekolwiek produkty. Użytkownik może po tym wszystkim kliknąć pozycję Dodaj produkty z wysyłki przed wprowadzeniem nazw produktów lub cen. Jeśli w ProductsDataTable znajduje się co najmniej jeden produkt, wywoływana jest metoda klasy ProductsBLLUpdateWithTransaction. Następnie dane są przywracane do ProductsGrid widoku GridView, aby nowo dodane produkty pojawiły się w interfejsie wyświetlania. Element StatusLabel jest aktualizowany, aby wyświetlić komunikat potwierdzający, a ReturnToDisplayInterface jest uruchamiany, co powoduje ukrycie interfejsu wstawiania i pokazanie interfejsu wyświetlania.

Jeśli nie wprowadzono żadnych produktów, interfejs wstawiania pozostaje wyświetlany, ale komunikat Nie dodano produktów. Wprowadź nazwy produktów i ceny jednostkowe w polach tekstowych.

Na rysunku 13, 14 i 15 przedstawiono interfejsy wstawiania i wyświetlania w akcji. Na rysunku 13 użytkownik wprowadził wartość ceny jednostkowej bez odpowiedniej nazwy produktu. Rysunek 14 przedstawia interfejs wyświetlania po pomyślnym dodaniu trzech nowych produktów, natomiast rysunek 15 przedstawia dwa nowo dodane produkty w elementy GridView (trzeci z nich znajduje się na poprzedniej stronie).

Nazwa produktu jest wymagana podczas wprowadzania ceny jednostkowej

Rysunek 13. Nazwa produktu jest wymagana podczas wprowadzania ceny jednostkowej (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Dodano trzy nowe warzywa dla dostawcy Mayumi

Rysunek 14. Dodano trzy nowe warzywa dla dostawcy Mayumi (kliknij, aby wyświetlić obraz pełnowymiarowy)

Nowe produkty można znaleźć na ostatniej stronie kontrolki GridView

Rysunek 15. Nowe produkty można znaleźć na ostatniej stronie kontrolki GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)

Uwaga / Notatka

Logika wstawiania wsadowego używana w tym samouczku opakowuje wstawki w zakresie transakcji. Aby to sprawdzić, celowo wprowadź błąd na poziomie bazy danych. Na przykład zamiast przypisywać nową ProductsRow właściwość wystąpienia CategoryID do wybranej wartości w Categories liście DropDownList, przypisz ją do wartości takiej jak i * 5. Oto i indeksator pętli i zawiera wartości z zakresu od 1 do 5. W związku z tym podczas dodawania co najmniej dwóch produktów w partii wstaw pierwszy produkt będzie miał prawidłową CategoryID wartość (5), ale kolejne produkty będą miały CategoryID wartości, które nie są zgodne z CategoryID wartościami w Categories tabeli. Efekt netto polega na tym, że podczas gdy pierwszy INSERT powiedzie się, kolejne zakończą się niepowodzeniem z naruszeniem ograniczenia klucza obcego. Ponieważ wstawianie wsadowe jest atomowe, pierwszy INSERT zostanie cofnięty, przywracając bazę danych do stanu sprzed rozpoczęcia procesu wstawiania wsadowego.

Podsumowanie

W tym i dwóch poprzednich samouczkach utworzyliśmy interfejsy, które umożliwiają aktualizowanie, usuwanie i wstawianie pakietów danych, wszystkie z nich korzystają z obsługi transakcji, które dodaliśmy do warstwy dostępu do danych w samouczku Opakowywanie modyfikacji bazy danych w ramach transakcji. W przypadku niektórych scenariuszy takie interfejsy użytkownika przetwarzania wsadowego znacznie zwiększają wydajność użytkownika końcowego, zmniejszając liczbę kliknięć, odwrotnego przesyłania danych oraz przełączania kontekstu z klawiatury na mysz, zachowując jednocześnie integralność danych źródłowych.

Niniejszy samouczek zamyka naszą analizę pracy z danymi wsadowymi. W następnym zestawie samouczków przedstawiono różne zaawansowane scenariusze warstwy dostępu do danych, w tym użycie procedur składowanych w metodach TableAdapter, konfigurację ustawień na poziomie połączenia i polecenia w DAL, szyfrowanie łańcuchów połączenia i nie tylko!

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 tego samouczka to Hilton Giesenow i S ren Jacob Lauritsen. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, napisz do mnie na adres mitchell@4GuysFromRolla.com.