Dela via


Batchinföring (VB)

av Scott Mitchell

Ladda ned PDF

Lär dig hur du infogar flera databasposter i en enda åtgärd. I användargränssnittsskiktet utökar vi GridView så att användaren kan ange flera nya poster. I dataåtkomstlagret omsluter vi flera Insert-åtgärder i en transaktion för att säkerställa att alla infogningar lyckas eller att alla infogningar återställs.

Inledning

I självstudien om batchuppdatering granskade vi hur vi anpassade GridView-kontrollen för att presentera ett gränssnitt där flera poster kunde redigeras. Användaren som besöker sidan kan göra en serie ändringar och sedan, med ett enda knappklick, utföra en batchuppdatering. I situationer där användare ofta uppdaterar många poster på en och samma plats kan ett sådant gränssnitt spara otaliga klick och kontextväxlar från tangentbord till mus jämfört med standardfunktionerna för redigering per rad som först utforskades i självstudiekursen En översikt över infogning, uppdatering och borttagning av data .

Detta koncept kan även tillämpas vid tillägg av poster. Tänk dig att vi här på Northwind Traders ofta får leveranser från leverantörer som innehåller ett antal produkter för en viss kategori. Vi kan till exempel få en leverans med sex olika te- och kaffeprodukter från Tokyo Traders. Om en användare anger de sex produkterna en i taget via en DetailsView-kontroll måste de välja många av samma värden om och om igen: de måste välja samma kategori (Drycker), samma leverantör (Tokyo Traders), samma utgångna värde (False) och samma enheter på ordervärdet (0). Den här repetitiva datainmatningen är inte bara tidskrävande, utan är också utsatt för fel.

Med lite arbete kan vi skapa ett batchinfogningsgränssnitt som gör att användaren kan välja leverantör och kategori en gång, ange en serie produktnamn och enhetspriser och sedan klicka på en knapp för att lägga till de nya produkterna i databasen (se bild 1). När varje produkt läggs till tilldelas dess ProductName och UnitPrice datafält de värden som anges i textrutorna, medan dess CategoryID värden och SupplierID värden tilldelas värdena från listrutorna högst upp i formuläret. Värdena Discontinued och UnitsOnOrder är satta till de hårdkodade värdena False respektive 0.

Batch-infogningsgränssnittet

Bild 1: Batch-infogningsgränssnittet (klicka om du vill visa en bild i full storlek)

I den här handledningen skapar vi en sida som implementerar batchinfogningsgränssnittet som visas i Bild 1. Precis som med de föregående två guiderna kommer vi att omsluta infogningarna inom omfånget för en transaktion för att säkerställa atomicitet. Låt oss komma igång!

Steg 1: Skapa visningsgränssnittet

Den här genomgången består av en sida som är uppdelad i två områden: ett visningsområde och ett infogningsområde. Visningsgränssnittet, som vi skapar i det här steget, visar produkterna i en GridView och innehåller en knapp med titeln Process Product Shipment (Bearbeta produktleverans). När den här knappen klickas ersätts visningsgränssnittet med infogningsgränssnittet, som visas i bild 1. Visningsgränssnittet returneras när knapparna Lägg till produkter från leverans eller Avbryt har klickats på. Vi skapar infogningsgränssnittet i steg 2.

När du skapar en sida som har två gränssnitt, varav endast ett är synligt i taget, placeras varje gränssnitt vanligtvis i en panelwebbkontroll, som fungerar som en container för andra kontroller. Därför har vår sida två panelkontroller en för varje gränssnitt.

Börja med att BatchInsert.aspx öppna sidan i BatchData mappen och dra en panel från verktygslådan till designern (se bild 2). Sätt egenskapen för Panel ID till DisplayInterface. När du lägger till panelen i designern anges dess Height egenskaper och Width egenskaper till 50 respektive 125 px. Ta bort dessa egenskapsvärden från fönstret Egenskaper.

Dra en panel från verktygslådan till designern

Bild 2: Dra en panel från verktygslådan till designern (klicka om du vill visa en bild i full storlek)

Dra sedan en knapp- och GridView-kontroll till panelen. Ange knappens ID-egenskap till ProcessShipment och dess Text-egenskap till Bearbeta produktleverans. Ställ in GridViews ID-egenskap till ProductsGrid och använd dess snabbval för att binda den till en ny ObjectDataSource med namnet ProductsDataSource. Konfigurera ObjectDataSource för att hämta dess data från ProductsBLL klassens GetProducts -metod. Eftersom den här GridView endast används för att visa data anger du listrutorna på flikarna UPDATE, INSERT och DELETE till (Ingen). Klicka på Slutför för att slutföra guiden Konfigurera datakälla.

Visa data som returneras från Metoden GetProducts för ProductsBLL-klassen

Bild 3: Visa data som returneras från ProductsBLL klassens GetProducts metod (Klicka om du vill visa en bild i full storlek)

Ange Drop-Down-listorna i flikarna UPDATE, INSERT och DELETE till (Ingen)

Bild 4: Ange Drop-Down-listorna i flikarna UPDATE, INSERT och DELETE till (Ingen) (Klicka om du vill visa en bild i full storlek)

När du har slutfört guiden ObjectDataSource lägger Visual Studio till BoundFields och ett CheckBoxField för produktdatafälten. Ta bort alla utom fälten ProductName, CategoryName, SupplierName, UnitPriceoch Discontinued . Känn dig fri att göra några estetiska anpassningar. Jag bestämde mig för att formatera UnitPrice fältet som ett valutavärde, ordna om fälten och bytte namn på flera av fältvärdena HeaderText . Konfigurera även GridView så att det inkluderar stöd för växling och sortering genom att markera kryssrutorna Aktivera växling och Aktivera sortering i GridViews smarta tagg.

När du har lagt till kontrollerna Panel, Knapp, GridView och ObjectDataSource och anpassat GridView-fälten bör sidans deklarativa markering se ut ungefär så här:

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

Observera att markeringen för knappen och GridView visas inom de öppnande och avslutande <asp:Panel>-taggarna. Eftersom dessa kontroller finns i panelen DisplayInterface kan vi dölja dem genom att helt enkelt ange egenskapen Panel till VisibleFalse. Steg 3 handlar om att programmatiskt ändra egenskapen Visible hos en Panel som svar på ett knappklick för att visa ett gränssnitt samtidigt som det andra döljs.

Ta en stund att se våra framsteg via en webbläsare. Som bild 5 visar bör du se knappen Bearbeta produktleverans ovanför en GridView som visar produkterna tio i taget.

GridView listar produkterna och erbjuder funktioner för sortering och pagination

Bild 5: GridView listar produkterna och erbjuder sorterings- och pagineringsmöjligheter (Klicka för att visa bilden i full storlek)

Steg 2: Skapa infogningsgränssnittet

När visningsgränssnittet är klart är vi redo att skapa infogningsgränssnittet. I den här självstudien ska vi skapa ett infogande gränssnitt som frågar efter en enskild leverantör och kategorivärde och sedan tillåter användaren att ange upp till fem produktnamn och enhetsprisvärden. Med det här gränssnittet kan användaren lägga till en till fem nya produkter som alla delar samma kategori och leverantör, men som har unika produktnamn och priser.

Börja med att dra en panel från verktygslådan till designern och placera den under den befintliga DisplayInterface panelen. Ställ in ID-egenskapen för den nyligen tillagda panelen till InsertingInterface och ställ in dess Visible-egenskap till False. Vi lägger till kod som sätter egenskapen InsertingInterface för Panel Visible till True i steg 3. Rensa även panelens och Height, Width egenskapsvärden.

Sedan måste vi skapa infogningsgränssnittet som visades i bild 1. Det här gränssnittet kan skapas med en mängd olika HTML-tekniker, men vi använder en ganska enkel: en tabell med fyra kolumner och sju rader.

Anmärkning

När jag anger markering för HTML-element <table> föredrar jag att använda källvyn. Visual Studio har verktyg för att lägga till <table>-element genom designern, dock verkar designern alltför villig att mata in ovälkomna style inställningar i markupen. När jag har skapat pålägget <table> återgår jag vanligtvis till designern för att lägga till webbkontrollerna och ange deras egenskaper. När jag skapar tabeller med fördefinierade kolumner och rader föredrar jag att använda statisk HTML i stället för tabellwebbkontrollen eftersom alla webbkontroller som placeras i en tabellwebbkontroll bara kan nås med hjälp av FindControl("controlID") mönstret. Jag använder dock Table Web-kontroller för dynamiskt storleksanpassade tabeller (de vars rader eller kolumner baseras på vissa databas- eller användardefinierade kriterier), eftersom Table Web-kontrollen kan konstrueras programmatiskt.

Ange följande markering i taggarna <asp:Panel> i panelen 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>

Den här <table> markeringen innehåller inga webbkontroller ännu. Vi lägger till dem tillfälligt. Observera att varje <tr>-element innehåller en viss CSS-klassinställning: BatchInsertHeaderRow för rubrikraden där leverantörens och kategorins listrutor kommer att placeras; BatchInsertFooterRow för sidfotsraden där knapparna Lägg till produkter från leverans och Avbryt kommer att placeras, och växlande BatchInsertRow och BatchInsertAlternatingRow-värden för raderna som ska innehålla textboxkontrollerna för produkt- och enhetspriset. Jag har skapat motsvarande CSS-klasser i Styles.css filen för att ge infogningsgränssnittet ett utseende som liknar kontrollerna GridView och DetailsView som vi har använt under de här självstudierna. Dessa CSS-klasser visas nedan.

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

Med den här markeringen angiven återgår du till designvyn. Detta <table> bör visas som en tabell med fyra kolumner, sju rader i designern, som bild 6 illustrerar.

Infogningsgränssnittet består av en tabell med fyra kolumner Seven-Row

Bild 6: Infogningsgränssnittet består av en fyrkolumn, Seven-Row tabell (klicka om du vill visa en bild i full storlek)

Nu är vi redo att lägga till webbkontrollerna i infogningsgränssnittet. Dra två rullgardinsmenyer från verktygslådan till lämpliga celler i tabellen, en för leverantören och en för kategorin.

Ange egenskapen för leverantören DropDownList ID till Suppliers och binda den till en ny ObjectDataSource med namnet SuppliersDataSource. Konfigurera den nya ObjectDataSource så att den hämtar sina data från klassens SuppliersBLLGetSuppliers-metod och ange listrutan för UPDATE-fliken till (Inget). Klicka på Slutför för att slutföra guiden.

Konfigurera ObjectDataSource för att använda GetSuppliers-metoden från SuppliersBLL-klassen

Bild 7: Konfigurera ObjectDataSource att använda SuppliersBLL klassens GetSuppliers metod (Klicka om du vill visa en bild i full storlek)

Låt Suppliers listrutan visa CompanyName datafältet och använd SupplierID datafältet som dess värden ListItem.

Visa fältet CompanyName-data och använd SupplierID som värde

Bild 8: Visa CompanyName datafältet och Använd SupplierID som värde (Klicka om du vill visa en bild i full storlek)

Namnge den andra listrutan Categories och binda den till en ny ObjectDataSource som heter CategoriesDataSource. CategoriesDataSource Konfigurera ObjectDataSource att använda CategoriesBLL klassens GetCategories metod. Ange listrutorna på flikarna UPPDATERA och TA BORT till (Ingen) och klicka på Slutför för att slutföra guiden. Slutligen ska listrutan visa datafältet CategoryName och använda CategoryID som värde.

När dessa två rullgardinsmenyer har lagts till och bundits till korrekt konfigurerade ObjectDataSources bör skärmen se ut ungefär som i bild 9.

Rubrikraden innehåller nu listrutorna Leverantörer och kategorier

Bild 9: Rubrikraden innehåller nu listrutorna Suppliers och Categories (klicka om du vill visa en bild i full storlek)

Nu måste vi skapa textrutorna för att samla in namn och pris för varje ny produkt. Dra en TextBox-kontroll från verktygslådan till designern för var och en av de fem produktnamnen och prisraderna. ID Ange egenskaperna för textrutorna till ProductName1, UnitPrice1, ProductName2, UnitPrice2, ProductName3, UnitPrice3och så vidare.

Lägg till en CompareValidator efter var och en av enhetspristextrutorna och ange ControlToValidate egenskapen till lämplig ID. Ställ också in Operator-egenskapen på GreaterThanEqual, ValueToCompare på 0 och TypeCurrency. De här inställningarna instruerar CompareValidator att se till att priset, om det anges, är ett giltigt valutavärde som är större än eller lika med noll. Text Ange egenskapen till *, och ErrorMessage till Priset måste vara större än eller lika med noll. Utelämna även valutasymboler.

Anmärkning

Infogningsgränssnittet innehåller inga RequiredFieldValidator-kontroller, även om ProductName fältet i databastabellen Products inte tillåter NULL värden. Det beror på att vi vill låta användaren ange upp till fem produkter. Om användaren till exempel skulle ange produktnamnet och enhetspriset för de tre första raderna och lämna de två sista raderna tomma skulle vi bara lägga till tre nya produkter i systemet. Eftersom ProductName krävs måste vi dock programmatiskt kontrollera att om ett enhetspris anges måste ett motsvarande produktnamnsvärde anges. Vi tar itu med denna kontroll i steg 4.

När användarens indata verifieras rapporterar CompareValidator ogiltiga data om värdet innehåller en valutasymbol. Lägg till ett $-tecken framför vart och ett av enhetsprisets textfält som en visuell signal som instruerar användaren att inte inkludera valutasymbolen när användaren anger priset.

Slutligen lägger du till en ValidationSummary-kontroll i panelen InsertingInterface , anger dess ShowMessageBox egenskap till True och dess ShowSummary egenskap till False. Om användaren med de här inställningarna anger ett ogiltigt enhetsprisvärde visas en asterisk bredvid de felaktiga TextBox-kontrollerna och Valideringssumman visar en meddelanderuta på klientsidan som visar det felmeddelande som vi angav tidigare.

I det här läget bör skärmen se ut ungefär som bild 10.

Infogningsgränssnittet innehåller nu textrutor för produktnamn och priser

Bild 10: Infogningsgränssnittet innehåller nu textrutor för produktnamn och priser (Klicka om du vill visa en bild i full storlek)

Sedan måste vi lägga till knapparna Lägg till produkter från leverans och Avbryt i sidfotsraden. Dra två knappkontroller från verktygslådan till sidfoten för infogningsgränssnittet och ställ in egenskaperna för knapparna för ID och AddProducts samt CancelButton och Text till "Lägg till produkter från leverans" och "Avbryt" respektive. Ange dessutom CancelButton kontrollens CausesValidation egenskap till false.

Slutligen måste vi lägga till en etikettwebbkontroll som visar statusmeddelanden för de två gränssnitten. När en användare till exempel lägger till en ny leverans av produkter vill vi återgå till visningsgränssnittet och visa ett bekräftelsemeddelande. Om användaren däremot tillhandahåller ett pris för en ny produkt men lämnar produktnamnet måste vi visa ett varningsmeddelande eftersom fältet ProductName krävs. Eftersom vi behöver det här meddelandet för att visa för båda gränssnitten placerar du det överst på sidan utanför panelerna.

Dra en etikettwebbkontroll från verktygslådan längst upp på sidan i designern. Sätt ID egenskapen till StatusLabel, rensa Text egenskapen och sätt Visible och EnableViewState egenskaperna till False. Som vi har sett i tidigare handledningar kan vi genom att ange EnableViewState-egenskapen False ändra egenskapsvärdena för Etiketter programmatiskt och låta dem automatiskt återgå till sina standardvärden vid nästa postback. Detta förenklar koden för att visa ett statusmeddelande som svar på en användaråtgärd som försvinner vid efterföljande efteråterställning. Ställ slutligen in StatusLabel kontrollens CssClass egenskap på Varning, som är namnet på en CSS-klass som definierats i Styles.css och som visar text i ett stort, kursivt, fetstilt, rött teckensnitt.

Bild 11 visar Visual Studio Designer när etiketten har lagts till och konfigurerats.

Placera Kontrollen StatusLabel ovanför de två panelkontrollerna

Bild 11: Placera StatusLabel kontrollen ovanför de två panelkontrollerna (klicka om du vill visa en bild i full storlek)

Steg 3: Växla mellan visnings- och infogningsgränssnitt

Nu har vi slutfört markeringen för våra visnings- och infogningsgränssnitt, men vi har fortfarande två uppgifter kvar:

  • Växla mellan visnings- och infogningsgränssnitt
  • Lägga till produkterna i leveransen till databasen

För närvarande är visningsgränssnittet synligt men infogningsgränssnittet är dolt. Det beror på att DisplayInterface egenskapen Panel är Visible inställd på True (standardvärdet), medan InsertingInterface egenskapen Panel är Visible inställd på False. För att växla mellan de två gränssnitten behöver vi helt enkelt växla mellan varje kontrolls egenskapsvärde Visible .

Vi vill flytta från visningsgränssnittet till infogningsgränssnittet när knappen Bearbeta produktleverans klickas. Skapa därför en händelsehanterare för den här knappens Click händelse som innehåller följande kod:

Protected Sub ProcessShipment_Click(sender As Object, e As EventArgs) _
    Handles ProcessShipment.Click
    DisplayInterface.Visible = False
    InsertingInterface.Visible = True
End Sub

Den här koden döljer bara panelen DisplayInterface och visar panelen InsertingInterface .

Skapa sedan händelsehanterare för kontrollerna Lägg till produkter från leverans och Avbryt knapp i infogningsgränssnittet. När någon av dessa knappar klickas måste vi återgå till visningsgränssnittet. Skapa Click händelsehanterare för båda knappkontrollerna så att de anropar ReturnToDisplayInterface, en metod som vi lägger till tillfälligt. Förutom att dölja panelen InsertingInterface och visa panelen DisplayInterfaceReturnToDisplayInterface måste metoden returnera webbkontrollerna till sitt förredigeringstillstånd. Det innebär att du ställer in Listrutornas egenskaper SelectedIndex till 0 och rensar egenskaperna för Text TextBox-kontrollerna.

Anmärkning

Tänk på vad som kan hända om vi inte returnerade kontrollerna till deras förredigeringstillstånd innan vi återvänder till visningsgränssnittet. En användare kan klicka på knappen Bearbeta produktleverans, ange produkterna från leveransen och sedan klicka på Lägg till produkter från leverans. Detta skulle lägga till produkterna och returnera användaren till visningsgränssnittet. I det här läget kanske användaren vill lägga till en annan leverans. När du klickar på knappen Bearbeta produktleverans återgår de till infogningsgränssnittet, men listlistvalen och Textbox-värdena fylls fortfarande i med sina tidigare värden.

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' TODO: Save the products
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Protected Sub CancelButton_Click(sender As Object, e As EventArgs) _
    Handles CancelButton.Click
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Const firstControlID As Integer = 1
Const lastControlID As Integer = 5
Private Sub ReturnToDisplayInterface()
    ' Reset the control values in the inserting interface
    Suppliers.SelectedIndex = 0
    Categories.SelectedIndex = 0
    For i As Integer = firstControlID To lastControlID
        CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text = String.Empty
        CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text = String.Empty
    Next
    DisplayInterface.Visible = True
    InsertingInterface.Visible = False
End Sub

Båda Click händelsehanterarna anropar ReturnToDisplayInterface helt enkelt metoden, även om vi återgår till händelsehanteraren Lägg till produkter från leverans Click i steg 4 och lägger till kod för att spara produkterna. ReturnToDisplayInterface börjar med att återställa listrutorna Suppliers och Categories till sina första alternativ. De två konstanterna firstControlID och lastControlID markerar de start- och slutkontrollindexvärden som används för att namnge produktnamnet och enhetspriset Textboxar i infogningsgränssnittet och används i gränserna för loopen For som anger Text egenskaperna för TextBox-kontrollerna tillbaka till en tom sträng. Slutligen återställs panelegenskaperna Visible så att infogningsgränssnittet är dolt och visningsgränssnittet visas.

Ta en stund att testa den här sidan i en webbläsare. När du först besöker sidan bör du se visningsgränssnittet som visades i bild 5. Klicka på knappen Bearbeta produktleverans. Sidan kommer att skickas tillbaka och du bör nu se infogningsgränssnittet enligt bild 12. Om du klickar på knapparna Lägg till produkter från leverans eller Avbryt återgår du till visningsgränssnittet.

Anmärkning

När du visar infogningsgränssnittet, ta dig tid att testa CompareValidators på TextBoxar för enhetspriser. Du bör se en meddelandelåda på klientsidan när du klickar på knappen Lägg till produkter från leverans med ogiltiga valutavärden eller priser med ett värde som är mindre än noll.

Infogningsgränssnittet visas när du har klickat på knappen Bearbeta produktleverans

Bild 12: Infogningsgränssnittet visas när du har klickat på knappen Bearbeta produktleverans (Klicka om du vill visa en bild i full storlek)

Steg 4: Lägga till produkterna

Allt som återstår för den här guiden är att spara produkterna i databasen i händelsehanteraren för knappen "Lägg till produkter från leveransen" Click. Detta kan åstadkommas genom att skapa en ProductsDataTable och lägga till en instans för vart och ett ProductsRow av de angivna produktnamnen. När dessa ProductsRow har lagts till gör vi ett anrop till ProductsBLL klassens UpdateWithTransaction -metod som skickar in ProductsDataTable. Kom ihåg att metoden UpdateWithTransaction, skapad under självstudiekursen Omslutningsdatabasändringar i en transaktion, skickar ProductsDataTable till ProductsTableAdapters UpdateWithTransaction-metod. Därifrån startas en ADO.NET transaktion och TableAdapter utfärdar en INSERT -instruktion till databasen för varje som läggs till ProductsRow i DataTable. Förutsatt att alla produkter läggs till felfritt, slutförs transaktionen, annars återställs den.

Koden för händelsehanteraren Lägg till produkter från leveransknappen Click måste också utföra lite felkontroll. Eftersom det inte finns några RequiredFieldValidators som används i infogningsgränssnittet kan en användare ange ett pris för en produkt samtidigt som dess namn utelämnas. Eftersom produktens namn krävs måste vi, om en sådan situation uppstår, varna användaren och inte fortsätta med infogningarna. Den fullständiga Click händelsehanterarkoden följer:

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' Make sure that the UnitPrice CompareValidators report valid data...
    If Not Page.IsValid Then Exit Sub
    ' Add new ProductsRows to a ProductsDataTable...
    Dim products As New Northwind.ProductsDataTable()
    For i As Integer = firstControlID To lastControlID
        ' Read in the values for the product name and unit price
        Dim productName As String = CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text.Trim()
        Dim unitPrice As String = CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text.Trim()
        ' Ensure that if unitPrice has a value, so does productName
        If unitPrice.Length > 0 AndAlso productName.Length = 0 Then
            ' 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
            Exit Sub
        End If
        ' Only add the product if a product name value is provided
        If productName.Length > 0 Then
            ' Add a new ProductsRow to the ProductsDataTable
            Dim newProduct As Northwind.ProductsRow = 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 Then
                newProduct.UnitPrice = Convert.ToDecimal(unitPrice)
            End If
            ' Add any "default" values
            newProduct.Discontinued = False
            newProduct.UnitsOnOrder = 0
            products.AddProductsRow(newProduct)
        End If
    Next
    ' If we reach here, see if there were any products added
    If products.Count > 0 Then
        ' Add the new products to the database using a transaction
        Dim productsAPI As 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
    End If
End Sub

Händelsehanteraren startar genom att se till att Page.IsValid egenskapen returnerar värdet True. Om den returnerar Falseinnebär det att en eller flera av CompareValidators rapporterar ogiltiga data. I så fall vill vi inte försöka infoga de angivna produkterna eller så får vi ett undantag när vi försöker tilldela det användaringivna enhetsprisvärdet till ProductsRow egenskapen s UnitPrice .

Därefter skapas en ny ProductsDataTable instans (products). En For loop används för att iterera genom produktnamnet och enhetspriset i textfält, vars Text egenskaper läses in i de lokala variablerna productName och unitPrice. Om användaren har angett ett värde för enhetspriset men inte för motsvarande produktnamn StatusLabel visas meddelandet Om du anger ett enhetspris måste du även inkludera namnet på produkten och händelsehanteraren avslutas.

Om ett produktnamn har angetts skapas en ny ProductsRow instans med hjälp av ProductsDataTable metoden s NewProductsRow . Den här nya ProductsRow-instansens ProductName-egenskap är inställd på den aktuella produkten i TextBox, medan SupplierID- och CategoryID-egenskaperna tilldelas SelectedValue-egenskaperna i listrutorna i infogningsgränssnittets sidhuvud. Om användaren angav ett värde för produktens pris tilldelas det till instansens ProductsRowUnitPrice egenskap. Annars lämnas egenskapen otilldelade, vilket resulterar i ett NULL värde för UnitPrice i databasen. Slutligen tilldelas egenskaperna Discontinued och UnitsOnOrder de hårdkodade värdena False respektive 0.

När egenskaperna har tilldelats till instansen ProductsRow läggs den till i ProductsDataTable.

När loopen For är klar kontrollerar vi om några produkter har lagts till. Användaren kan trots allt ha klickat på Lägg till produkter från leverans innan du anger några produktnamn eller priser. Om det finns minst en produkt i ProductsDataTableProductsBLL anropas metoden class sUpdateWithTransaction. Därefter returneras data till ProductsGrid GridView så att de nyligen tillagda produkterna visas i visningsgränssnittet. StatusLabel Uppdateras för att visa ett bekräftelsemeddelande och ReturnToDisplayInterface anropas, döljer infogningsgränssnittet och visar visningsgränssnittet.

Om inga produkter angavs visas infogningsgränssnittet, men meddelandet Inga produkter har lagts till. Ange produktnamnen och enhetspriserna i textrutorna som visas.

Bild s 13, 14 och 15 visar infognings- och visningsgränssnitten i praktiken. I bild 13 har användaren angett ett enhetsprisvärde utan motsvarande produktnamn. Bild 14 visar visningsgränssnittet efter att tre nya produkter har lagts till, medan bild 15 visar två av de nyligen tillagda produkterna i GridView (den tredje är på föregående sida).

Ett produktnamn krävs när du anger ett enhetspris

Bild 13: Ett produktnamn krävs när du anger ett enhetspris (klicka om du vill visa en bild i full storlek)

Tre nya grönsaker har lagts till för leverantören Mayumi s

Bild 14: Tre nya grönsaker har lagts till för leverantören Mayumi s (Klicka om du vill visa en bild i full storlek)

De nya produkterna finns på sista sidan i GridView

Bild 15: De nya produkterna finns på den sista sidan i GridView (Klicka om du vill visa en bild i full storlek)

Anmärkning

Den batchinmatningslogik som används i den här självstudien omsluter infogningarna inom transaktionens omfång. För att verifiera detta introducerar du avsiktligt ett fel på databasnivå. I stället för att till exempel tilldela den nya ProductsRow instansens CategoryID egenskap till det valda värdet i Categories listrutan tilldelar du den till ett värde som i * 5. Här i är loopindexeraren och har värden mellan 1 och 5. Därför, när du lägger till två eller flera produkter i batchinfogningen, kommer den första produkten att ha ett giltigt CategoryID värde (5), men efterföljande produkter kommer att ha CategoryID värden som inte matchar CategoryID värdena i Categories tabellen. Nettoeffekten är att medan den första INSERT kommer att lyckas, misslyckas de efterföljande med överträdelser av utländsk nyckelbegränsning. Eftersom batchinfogningen är atomisk återställs den första INSERT och returnerar databasen till dess tillstånd innan batchinfogningsprocessen påbörjades.

Sammanfattning

I den här och de föregående två guiderna har vi skapat gränssnitt som gör det möjligt att uppdatera, ta bort och införa datapartier, som alla använde transaktionsstödet som vi lade till i dataåtkomstlagret i självstudiekursen Omslutning av databasändringar inom en transaktion. I vissa scenarier kan sådana batchbearbetningsgränssnitt avsevärt förbättra slutanvändarnas effektivitet genom att minska antalet klick, återkopplingar och kontextväxlar mellan tangentbord och mus, samtidigt som integriteten för underliggande data bibehålls.

Den här handledningen slutför vår genomgång av hur man arbetar med batchbaserad data. I nästa uppsättning självstudier går vi igenom en mängd olika avancerade scenarier för dataåtkomstlager, inklusive att använda lagrade procedurer i TableAdapter-metoderna, konfigurera inställningar för anslutning och kommandonivå i DAL, kryptera anslutningssträngar med mera!

Lycka till med programmerandet!

Om författaren

Scott Mitchell, författare till sju ASP/ASP.NET-böcker och grundare av 4GuysFromRolla.com, har arbetat med Microsofts webbtekniker sedan 1998. Scott arbetar som oberoende konsult, tränare och författare. Hans senaste bok är Sams Teach Yourself ASP.NET 2.0 på 24 timmar. Han kan nås på mitchell@4GuysFromRolla.com.

Särskilt tack till

Den här självstudieserien granskades av många användbara granskare. Huvudgranskare för den här självstudien var Hilton Giesenow och Søren Jacob Lauritsen. Vill du granska mina kommande MSDN-artiklar? Om så är fallet, hör av dig på mitchell@4GuysFromRolla.com.