Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
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.
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.
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.
Bild 3: Visa data som returneras från ProductsBLL klassens GetProducts metod (Klicka om du vill visa en bild i full storlek)
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.
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.
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.
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.
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.
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 Type på Currency. 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.
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.
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 void ProcessShipment_Click(object sender, EventArgs e)
{
DisplayInterface.Visible = false;
InsertingInterface.Visible = true;
}
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 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;
}
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.
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, som skapades i handledningen Omslutningsdatabasändringar i en transaktion, överför ProductsDataTable till metoden ProductsTableAdapter för UpdateWithTransaction. 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 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;
}
}
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).
Bild 13: Ett produktnamn krävs när du anger ett enhetspris (klicka om du vill visa en bild i full storlek)
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)
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.