Condividi tramite


Inserimento batch (VB)

di Scott Mitchell

Scaricare il PDF

Informazioni su come inserire più record di database in una singola operazione. Nel livello interfaccia utente si estende GridView per consentire all'utente di immettere più nuovi record. Nel livello di accesso ai dati avvolgiamo più operazioni di inserimento all'interno di una transazione per garantire che tutti gli inserimenti abbiano esito positivo o vengano annullati.

Introduzione

Nell'esercitazione sull'aggiornamento batch è stata esaminata la personalizzazione del controllo GridView per presentare un'interfaccia in cui sono stati modificabili più record. L'utente che visita la pagina potrebbe apportare una serie di modifiche e quindi, con un singolo clic su un pulsante, eseguire un aggiornamento batch. Per situazioni in cui gli utenti comunemente aggiornano molti record in un'unica operazione, un'interfaccia di questo tipo può salvare innumerevoli clic e passaggi da tastiera a mouse rispetto alle funzionalità di modifica per singola riga predefinite esaminate nell'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione di dati.

Questo concetto può essere applicato anche quando si aggiungono record. Si supponga che qui a Northwind Traders in genere riceviamo spedizioni da fornitori che contengono un certo numero di prodotti per una determinata categoria. Ad esempio, potremmo ricevere una spedizione di sei diversi prodotti per tè e caffè da Tokyo Traders. Se un utente immette i sei prodotti uno alla volta tramite un controllo DetailsView, dovrà scegliere molti degli stessi valori più e più volte: dovranno scegliere la stessa categoria (Bevande), lo stesso fornitore (Tokyo Traders), lo stesso valore sospeso (False) e le stesse unità di ordine (0). L'inserimento ripetitivo dei dati richiede molto tempo e è soggetto a errori.

Con un piccolo lavoro è possibile creare un'interfaccia di inserimento batch che consente all'utente di scegliere il fornitore e la categoria una volta, immettere una serie di nomi di prodotto e prezzi unitari, quindi fare clic su un pulsante per aggiungere i nuovi prodotti al database (vedere la figura 1). Man mano che viene aggiunto ogni prodotto, ai campi dati ProductName e UnitPrice vengono assegnati i valori immessi nelle caselle di testo, mentre ai campi dati CategoryID e SupplierID vengono assegnati i valori degli Elenchi a Discesa nella parte superiore del modulo. I valori Discontinued e UnitsOnOrder vengono impostati sui valori prestabiliti di False e 0, rispettivamente.

Interfaccia di inserimento batch

Figura 1: Interfaccia di inserimento batch (fare clic per visualizzare l'immagine a dimensione intera)

In questa esercitazione verrà creata una pagina che implementa l'interfaccia di inserimento batch illustrata nella figura 1. Come per le due esercitazioni precedenti, incapsuleremo gli inserimenti nell'ambito di una transazione per garantire l'atomicità. Iniziamo!

Passaggio 1: Creazione dell'interfaccia di visualizzazione

Questa esercitazione sarà costituita da una singola pagina divisa in due aree: un'area di visualizzazione e un'area di inserimento. L'interfaccia di visualizzazione, che verrà creata in questo passaggio, mostra i prodotti in gridView e include un pulsante denominato Process Product Shipment.The display interface, we'll create in this step, shows the products in a GridView and includes a button titled Process Product Shipment. Quando si fa clic su questo pulsante, l'interfaccia di visualizzazione viene sostituita con l'interfaccia di inserimento, illustrata nella figura 1. L'interfaccia di visualizzazione viene restituita dopo aver selezionato i pulsanti Aggiungi prodotti dalla spedizione o Annulla. Verrà creata l'interfaccia di inserimento nel passaggio 2.

Quando si crea una pagina con due interfacce, una delle quali è visibile alla volta, ogni interfaccia viene in genere posizionata all'interno di un controllo Web Pannello, che funge da contenitore per altri controlli. Pertanto, la pagina avrà due controlli Pannello uno per ogni interfaccia.

Per iniziare, aprire la BatchInsert.aspx pagina nella BatchData cartella e trascinare un pannello dalla casella degli strumenti nella finestra di progettazione (vedere la figura 2). Impostare la proprietà Panel s ID su DisplayInterface. Quando si aggiunge il pannello alla finestra di progettazione, le proprietà Height e Width vengono impostate rispettivamente su 50px e 125px. Cancellare questi valori di proprietà dalla finestra Proprietà.

Trascinare un pannello dalla casella degli strumenti nella finestra di progettazione

Figura 2: Trascinare un pannello dalla casella degli strumenti nella finestra di progettazione (fare clic per visualizzare l'immagine a dimensione intera)

Trascinare quindi un controllo Button e GridView nel pannello. Impostare la proprietà del pulsante ID su ProcessShipment e la proprietà Text su Elabora spedizione prodotto. Impostare la proprietà gridView su ID e, dallo smart tag, associarla a un nuovo ObjectDataSource denominato ProductsGrid.ProductsDataSource Configurare ObjectDataSource per recuperare i dati dal metodo della classe ProductsBLLGetProducts. Poiché gridView viene usato solo per visualizzare i dati, impostare gli elenchi a discesa nelle schede UPDATE, INSERT e DELETE su (Nessuno). Fare clic su Fine per completare la procedura guidata di configurazione dell'origine dati.

Visualizzare i dati restituiti dal metodo GetProducts della classe ProductsBLL

Figura 3: Visualizzare i dati restituiti dal ProductsBLL metodo della GetProducts classe (fare clic per visualizzare l'immagine a dimensione intera)

Impostare gli elenchi di Drop-Down nelle schede UPDATE, INSERT e DELETE su (Nessuno)

Figura 4: Impostare gli elenchi di Drop-Down nelle schede UPDATE, INSERT e DELETE su (Nessuno) (Fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver completato la procedura guidata ObjectDataSource, Visual Studio aggiungerà BoundFields e checkBoxField per i campi dati del prodotto. Rimuovere tutti tranne i campi ProductName, CategoryName, SupplierName, UnitPrice e Discontinued. È possibile effettuare qualsiasi personalizzazione estetica. Ho deciso di formattare il UnitPrice campo come valore di valuta, riordinare i campi e rinominare diversi valori dei campi HeaderText . Configurare anche GridView per includere il supporto per il paging e l'ordinamento selezionando le caselle di controllo Abilita paging e Abilita ordinamento nello smart tag gridView.

Dopo aver aggiunto i controlli Panel, Button, GridView e ObjectDataSource e personalizzando i campi di GridView, il markup dichiarativo della pagina dovrebbe essere simile al seguente:

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

Si noti che il markup per Button e GridView vengono visualizzati all'interno dei tag di apertura e chiusura <asp:Panel> . Poiché questi controlli si trovano all'interno del DisplayInterface pannello, è possibile nasconderli semplicemente impostando la proprietà Panel s Visible su False. Il passaggio 3 esamina a livello di codice la modifica della proprietà Panel s Visible in risposta a un clic di un pulsante per visualizzare un'interfaccia mentre si nasconde l'altra.

Prenditi un momento per visualizzare i nostri progressi tramite un browser. Come mostra la figura 5, dovresti vedere un pulsante "Elabora Spedizione Prodotto" sopra un controllo GridView che elenca i prodotti dieci alla volta.

GridView elenca i prodotti e le funzionalità di ordinamento e paging offerte

Figura 5: GridView elenca i prodotti e le funzionalità di ordinamento e paging (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 2: Creazione dell'interfaccia di inserimento

Al termine dell'interfaccia di visualizzazione, è possibile creare l'interfaccia di inserimento. Per questa esercitazione, è possibile creare un'interfaccia di inserimento che richiede un singolo fornitore e valore di categoria e quindi consente all'utente di immettere fino a cinque nomi di prodotto e valori di prezzo unitario. Con questa interfaccia, l'utente può aggiungere uno a cinque nuovi prodotti che condividono la stessa categoria e fornitore, ma hanno nomi e prezzi univoci di prodotto.

Per iniziare, trascinare un pannello dalla casella degli strumenti nella finestra di progettazione, posizionandolo sotto il pannello esistente DisplayInterface . Impostare la ID proprietà di questo pannello appena aggiunto su InsertingInterface e impostarne la Visible proprietà su False. Aggiungeremo il codice che imposta la InsertingInterface proprietà Panel s Visible su True nel passaggio 3. Anche cancella i valori delle proprietà Height e Width del pannello.

Successivamente, è necessario creare l'interfaccia di inserimento visualizzata nella figura 1. Questa interfaccia può essere creata tramite una varietà di tecniche HTML, ma si userà una tabella di quattro colonne, sette righe.

Annotazioni

Quando si immette markup per gli elementi HTML <table> , preferisco usare la visualizzazione Origine. Anche se Visual Studio dispone di strumenti per aggiungere <table> elementi tramite il Designer, il Designer sembra troppo incline a inserire impostazioni non richieste style nel markup. Dopo aver creato il <table> markup, in genere torno alla finestra di progettazione per aggiungere i controlli Web e impostarne le proprietà. Quando si creano tabelle con colonne e righe determinate in modo predeterminato, è preferibile usare codice HTML statico anziché il controllo Web Table perché è possibile accedere a tutti i controlli Web posizionati all'interno di un controllo Web tabella solo usando il FindControl("controlID") criterio. Tuttavia, si usano controlli Web tabella per tabelle di dimensioni dinamiche (quelle le cui righe o colonne sono basate su alcuni criteri specificati dall'utente o database), poiché il controllo Web tabella può essere costruito a livello di codice.

Immettere il markup seguente all'interno dei <asp:Panel> tag del InsertingInterface pannello:

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

Questo <table> markup non include ancora controlli Web, che verranno aggiunti momentaneamente. Si noti che ogni <tr> elemento contiene un'impostazione specifica della classe CSS: BatchInsertHeaderRow per la riga di intestazione in cui verranno visualizzati gli elenchi a discesa fornitore e categoria; BatchInsertFooterRow per la riga del piè di pagina in cui verranno visualizzati i pulsanti Aggiungi prodotti da spedizione e Annulla, e alternando BatchInsertRow e BatchInsertAlternatingRow valori per le righe che conterranno i controlli TextBox prodotto e prezzo unitario. Ho creato le classi CSS corrispondenti nel Styles.css file per dare all'interfaccia di inserimento un aspetto simile ai controlli GridView e DetailsView usati in queste esercitazioni. Queste classi CSS sono illustrate di seguito.

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

Con questo markup immesso, tornare alla visualizzazione Struttura. Dovrebbe <table> apparire come una tabella a quattro colonne e sette righe nel Designer, come illustrato nella Figura 6.

L'interfaccia di inserimento è costituita da una tabella a quattro colonne Seven-Row

Figura 6: L'interfaccia di inserimento è costituita da una tabella a quattro colonne Seven-Row (fare clic per visualizzare l'immagine a dimensione intera)

È ora possibile aggiungere i controlli Web all'interfaccia di inserimento. Trascina due menu a tendina dalla casella degli strumenti nelle celle appropriate della tabella: uno per il fornitore e uno per la categoria.

Impostare la proprietà del fornitore DropDownList su IDSuppliers e associarla a un nuovo ObjectDataSource denominato SuppliersDataSource. Configurare il nuovo ObjectDataSource per recuperare i dati dalla classe SuppliersBLL metodo GetSuppliers e impostare l'elenco a discesa della scheda UPDATE su (None). Fare clic su Fine per completare la procedura guidata.

Configurare ObjectDataSource per l'utilizzo del metodo GetSuppliers della classe SuppliersBLL

Figura 7: Configurare ObjectDataSource per utilizzare il metodo della SuppliersBLL classe GetSuppliers (fare clic per visualizzare l'immagine a schermo intero)

Fai visualizzare a Suppliers DropDownList il CompanyName campo dati e usa il SupplierID campo dati come valori di ListItem.

Visualizzare il campo dati CompanyName e usare SupplierID come valore

Figura 8: Visualizzare il CompanyName campo dati e Usa SupplierID come valore (fare clic per visualizzare l'immagine a dimensione intera)

Denominare il secondo DropDownList Categories e associarlo a un nuovo ObjectDataSource denominato CategoriesDataSource. Configurare l'oggetto CategoriesDataSource ObjectDataSource per utilizzare il metodo della classe CategoriesBLLGetCategories. Impostare gli elenchi a discesa nelle schede UPDATE e DELETE su (Nessuno) e cliccare su Fine per completare la procedura guidata. Infine, fare in modo che DropDownList visualizzi il CategoryName campo dati e usi CategoryID come valore.

Dopo che questi due DropDownList sono stati aggiunti e collegati a ObjectDataSource configurati in modo appropriato, la schermata dovrebbe essere simile alla figura 9.

La riga di intestazione contiene ora gli elenchi a discesa Fornitori e Categorie

Figura 9: La riga di intestazione ora contiene gli elenchi a discesa Suppliers e Categories (fare clic per visualizzare l'immagine a dimensione intera)

È ora necessario creare i textbox per raccogliere il nome e il prezzo per ogni nuovo prodotto. Trascinare un controllo TextBox dalla casella degli strumenti sul Designer per ciascuna delle cinque righe relative al nome e al prezzo del prodotto. Impostare le ID proprietà di TextBoxes su ProductName1, UnitPrice1, ProductName2, UnitPrice2, ProductName3, UnitPrice3e così via.

Aggiungere un CompareValidator dopo ciascun TextBox del prezzo unitario, impostando la ControlToValidate proprietà sull'appropriato ID. Impostare anche la Operator proprietà su GreaterThanEqual, ValueToCompare su 0 e Type su Currency. Queste impostazioni indicano a CompareValidator di assicurarsi che il prezzo, se immesso, sia un valore di valuta valido maggiore o uguale a zero. Impostare la Text proprietà su *e ErrorMessage su Il prezzo deve essere maggiore o uguale a zero. Inoltre, omettere eventuali simboli di valuta.

Annotazioni

L'interfaccia di inserimento non include alcun controllo RequiredFieldValidator, nonostante il ProductName campo della tabella del Products database non consenta NULL valori. Ciò è dovuto al fatto che si vuole consentire all'utente di immettere fino a cinque prodotti. Ad esempio, se l'utente deve fornire il nome del prodotto e il prezzo unitario per le prime tre righe, lasciando vuote le ultime due righe, aggiungeremo solo tre nuovi prodotti al sistema. Poiché ProductName è obbligatorio, tuttavia, sarà necessario verificare a livello di codice che se viene immesso un prezzo unitario che venga fornito un valore di nome prodotto corrispondente. Questo controllo verrà affrontato nel passaggio 4.

Quando si convalida l'input dell'utente, CompareValidator segnala dati non validi se il valore contiene un simbolo di valuta. Aggiungere un $ davanti a ogni casella di testo del prezzo unitario da usare come segnale visivo che indica all'utente di omettere il simbolo di valuta quando si immette il prezzo.

Infine, aggiungere un controllo ValidationSummary all'interno del InsertingInterface pannello, impostare la relativa ShowMessageBox proprietà su True e la relativa ShowSummary proprietà su False. Con queste impostazioni, se l'utente immette un valore di prezzo unitario non valido, accanto ai controlli TextBox incriminati verrà visualizzato un asterisco e il ValidationSummary mostrerà un messaggio di errore client-side tramite una finestra di dialogo che mostra il messaggio di errore che abbiamo specificato in precedenza.

A questo punto, la schermata dovrebbe essere simile alla figura 10.

L'interfaccia di inserimento include ora caselle di testo per i nomi e i prezzi dei prodotti

Figura 10: L'interfaccia di inserimento include ora caselle di testo per i nomi e i prezzi dei prodotti (fare clic per visualizzare l'immagine a dimensione intera)

Successivamente, è necessario aggiungere i pulsanti Aggiungi prodotti da Spedizione e Annulla alla riga del piè di pagina. Trascinare due controlli pulsante dalla casella degli strumenti nel piè di pagina dell'interfaccia di inserimento, impostando le proprietà del primo pulsante su ID e AddProducts, e quelle del secondo pulsante su Aggiungi prodotti dalla spedizione e Annulla, rispettivamente. Impostare inoltre la CancelButton proprietà del CausesValidation controllo su false.

Infine, è necessario aggiungere un controllo etichetta Web che visualizzi i messaggi di stato per le due interfacce. Ad esempio, quando un utente aggiunge correttamente una nuova spedizione di prodotti, si vuole tornare all'interfaccia di visualizzazione e visualizzare un messaggio di conferma. Se, tuttavia, l'utente fornisce un prezzo per un nuovo prodotto ma lascia il nome del prodotto, è necessario visualizzare un messaggio di avviso perché il ProductName campo è obbligatorio. Poiché è necessario che questo messaggio venga visualizzato per entrambe le interfacce, posizionarlo nella parte superiore della pagina all'esterno dei pannelli.

Trascina un controllo Web Etichetta dalla casella degli strumenti all'inizio della pagina nel Designer. Impostare la ID proprietà su StatusLabel, cancellare la Text proprietà e impostare le Visible proprietà e EnableViewState su False. Come si è visto nelle esercitazioni precedenti, l'impostazione della EnableViewState proprietà su False consente di modificare programmaticamente i valori della proprietà Label e di ripristinarli automaticamente alle impostazioni predefinite nel postback successivo. Questo semplifica il codice per visualizzare un messaggio di stato in risposta a un'azione dell'utente che scompare nel postback successivo. Impostare infine la proprietà del StatusLabel controllo CssClass su Warning, che è il nome di una classe CSS definita in Styles.css che visualizza il testo in un font grande, corsivo, in grassetto e rosso.

La figura 11 mostra La finestra di progettazione di Visual Studio dopo l'aggiunta e la configurazione dell'etichetta.

Posizionare il controllo StatusLabel sopra i due controlli pannello

Figura 11: Posizionare il StatusLabel controllo sopra i due controlli pannello (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 3: Passaggio tra le interfacce di visualizzazione e inserimento

A questo punto abbiamo completato il markup per la visualizzazione e l'inserimento delle interfacce, ma siamo ancora rimasti con due attività:

  • Passaggio tra le interfacce di visualizzazione e inserimento
  • Aggiunta dei prodotti nella spedizione al database

Attualmente, l'interfaccia di visualizzazione è visibile, ma l'interfaccia di inserimento è nascosta. Ciò è dovuto al fatto che la DisplayInterface proprietà Panel s è impostata su Visible (valore predefinito), mentre la True proprietà Panel s InsertingInterfaceVisible è impostata su False. Per passare da un'interfaccia all'altra, è sufficiente attivare o disattivare il valore della proprietà di Visible ogni controllo.

Si vuole passare dall'interfaccia di visualizzazione all'interfaccia di inserimento quando si fa clic sul pulsante Spedisci prodotto. Pertanto, creare un gestore eventi per questo evento Button Click che contiene il codice seguente:

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

Questo codice nasconde semplicemente il DisplayInterface pannello e mostra il InsertingInterface pannello.

Successivamente, creare gestori eventi per i controlli Add Products from Shipment e Cancel Button nell'interfaccia di inserimento. Quando si fa clic su uno di questi pulsanti, è necessario ripristinare l'interfaccia di visualizzazione. Creare Click gestori eventi per entrambi i controlli dei pulsanti in modo che chiamino ReturnToDisplayInterface, un metodo che sarà aggiunto a breve. Oltre a nascondere il InsertingInterface pannello e visualizzare il DisplayInterface pannello, il ReturnToDisplayInterface metodo deve restituire i controlli Web allo stato di pre-modifica. Ciò comporta l'impostazione delle proprietà DropDownLists SelectedIndex su 0 e la cancellazione delle Text proprietà dei controlli TextBox.

Annotazioni

Considerare cosa può accadere se i controlli non sono stati restituiti allo stato di pre-modifica prima di tornare all'interfaccia di visualizzazione. Un utente potrebbe fare clic sul pulsante Elabora spedizione prodotto, immettere i prodotti dalla spedizione e quindi fare clic su Aggiungi prodotti dalla spedizione. Verranno aggiunti i prodotti e l'utente verrà riportato all'interfaccia di visualizzazione. A questo punto l'utente potrebbe voler aggiungere un'altra spedizione. Facendo clic sul pulsante "Process Product Shipment" (Elabora spedizione prodotto), si ritorna all'interfaccia di inserimento, ma le selezioni nella DropDownList e i valori nel TextBox continuano a essere popolati con i valori precedenti.

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

Entrambi i gestori dell'evento Click chiamano semplicemente il metodo ReturnToDisplayInterface, anche se torneremo al gestore dell'evento "Add Products from Shipment" Click nel passaggio 4 per aggiungere il codice per salvare i prodotti. ReturnToDisplayInterface inizia reimpostando le Suppliers e Categories DropDownLists alle loro prime opzioni. Le due costanti firstControlID e lastControlID contrassegnano i valori di indice di controllo iniziale e finale utilizzati per denominare il nome del prodotto e gli unit price TextBox nell'interfaccia di inserimento e vengono utilizzati nei limiti del For ciclo che imposta nuovamente le Text proprietà dei controlli TextBox su una stringa vuota. Infine, le proprietà Pannelli Visible vengono reimpostate in modo che l'interfaccia di inserimento sia nascosta e l'interfaccia di visualizzazione visualizzata.

Prenditi un momento per provare questa pagina in un browser. Quando si visita per la prima volta la pagina dovrebbe essere visualizzata l'interfaccia di visualizzazione, come illustrato nella figura 5. Cliccare sul pulsante Elaborazione spedizione prodotto. La pagina eseguirà un postback e ora dovrebbe visualizzarsi l'interfaccia di inserimento, come mostrato nella Figura 12. Facendo clic sui pulsanti Aggiungi prodotti da Spedizione o Annulla si torna all'interfaccia di visualizzazione.

Annotazioni

Durante la visualizzazione dell'interfaccia di inserimento, prendi un momento per testare i CompareValidator sulle caselle di testo del prezzo unitario. Verrà visualizzata una finestra di messaggio sul lato client quando si fa clic sul pulsante Aggiungi prodotti dalla spedizione con valori di valuta o prezzi non validi con un valore minore di zero.You should see a client-side messagebox warning when click the Add Products from Shipment button with invalid currency values or prices with a value less than zero.

L'interfaccia di inserimento viene visualizzata dopo aver fatto clic sul pulsante Elaborazione spedizione prodotto

Figura 12: L'interfaccia di inserimento viene visualizzata dopo aver fatto clic sul pulsante Elaborazione spedizione prodotto (fare clic per visualizzare l'immagine a dimensione intera)

Passaggio 4: Aggiunta dei prodotti

Tutto ciò che resta da fare per questa esercitazione è salvare i prodotti nel database nell'evento gestore del pulsante Aggiungi prodotti dalla spedizione Click. A tale scopo, è possibile creare un oggetto ProductsDataTable e aggiungere un'istanza ProductsRow per ognuno dei nomi di prodotto specificati. Dopo aver aggiunto questi ProductsRow oggetti, faremo una chiamata al metodo della classe ProductsBLLUpdateWithTransaction passando il ProductsDataTable. Tenere presente che il metodo UpdateWithTransaction, che è stato creato nella lezione 'Wrapping delle modifiche al database all'interno di una transazione', passa al metodo ProductsDataTable di ProductsTableAdapter. Da qui viene avviata una transazione ADO.NET e TableAdapter rilascia un'istruzione INSERT al database per ogni elemento aggiunto ProductsRow in DataTable. Supponendo che tutti i prodotti vengano aggiunti senza errori, viene eseguito il commit della transazione, in caso contrario viene eseguito il rollback.

Il codice per il gestore eventi Add Products from Shipment Button Click deve anche eseguire un po' di controllo degli errori. Poiché nell'interfaccia di inserimento non sono utilizzati requiredFieldValidator, un utente potrebbe immettere un prezzo per un prodotto omettendone il nome. Poiché il nome del prodotto è obbligatorio, se tale condizione si svolge è necessario avvisare l'utente e non procedere con gli inserimenti. Il codice completo Click del gestore eventi segue:

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

Il gestore eventi inizia assicurandosi che la Page.IsValid proprietà restituisca un valore di True. Se restituisce False, significa che uno o più compareValidator segnalano dati non validi. In questo caso non si vuole tentare di inserire i prodotti immessi o si otterrà un'eccezione quando si tenta di assegnare il valore di prezzo unitario immesso dall'utente alla ProductsRow proprietà s UnitPrice .

Viene quindi creata una nuova ProductsDataTable istanza (products). Viene utilizzato un For ciclo per scorrere attraverso i TextBox del nome del prodotto e del prezzo unitario, e le proprietà Text vengono lette nelle variabili locali productName e unitPrice. Se l'utente ha immesso un valore per il prezzo unitario ma non per il nome del prodotto corrispondente, StatusLabel viene visualizzato il messaggio Se si specifica un prezzo unitario, è necessario includere anche il nome del prodotto e il gestore eventi viene chiuso.

Se è stato specificato un nome di prodotto, viene creata una nuova ProductsRow istanza usando il ProductsDataTable metodo s NewProductsRow . Questa nuova proprietà s della ProductsRow istanza viene impostata sul nome del prodotto corrente TextBox mentre le proprietà ProductName, SupplierID e CategoryID vengono assegnate alle proprietà SelectedValue degli elenchi a discesa nell'intestazione dell'interfaccia di inserimento. Se l'utente ha immesso un valore per il prezzo del prodotto, viene assegnato alla proprietà dell'istanza ProductsRowUnitPrice . In caso contrario, la proprietà viene lasciata non assegnata, con conseguente NULL valore per UnitPrice nel database. Infine, le proprietà Discontinued e UnitsOnOrder vengono assegnate rispettivamente ai valori False prefissati e a 0.

Dopo che le proprietà sono state assegnate all'istanza ProductsRow, essa viene aggiunta a ProductsDataTable.

Al termine del For ciclo, controlliamo se sono stati aggiunti prodotti. L'utente può, dopo tutto, aver fatto clic su Aggiungi prodotti dalla spedizione prima di immettere i nomi o i prezzi dei prodotti. Se è presente almeno un prodotto in ProductsDataTable, viene chiamato il metodo della classe ProductsBLLUpdateWithTransaction. Successivamente, i dati vengono associati nuovamente al ProductsGrid GridView in modo che i nuovi prodotti aggiunti vengano visualizzati nell'interfaccia di visualizzazione. L'elemento StatusLabel viene aggiornato per visualizzare un messaggio di conferma e ReturnToDisplayInterface viene richiamato, nascondendo l'interfaccia di inserimento e mostrando quella di visualizzazione.

Se non sono stati immessi prodotti, l'interfaccia di inserimento rimane visualizzata ma il messaggio Nessun prodotto è stato aggiunto. Inserisci i nomi dei prodotti e i prezzi unitari nelle caselle di testo.

La figura s 13, 14 e 15 mostra le interfacce di inserimento e visualizzazione in azione. Nella figura 13 l'utente ha immesso un valore di prezzo unitario senza un nome di prodotto corrispondente. La figura 14 mostra l'interfaccia di visualizzazione dopo l'aggiunta di tre nuovi prodotti, mentre la figura 15 mostra due dei nuovi prodotti aggiunti in GridView (la terza è nella pagina precedente).

Il nome di un prodotto è obbligatorio quando si immette un prezzo unitario

Figura 13: Il nome di un prodotto è obbligatorio quando si immette un prezzo unitario (fare clic per visualizzare l'immagine a dimensione intera)

Sono state aggiunte tre nuove verdure per il fornitore Mayumi

Figura 14: Sono state aggiunte tre nuove verdure per il fornitore Mayumi (fare clic per visualizzare l'immagine a grandezza naturale)

I nuovi prodotti sono disponibili nell'ultima pagina di GridView

Figura 15: I nuovi prodotti sono disponibili nell'ultima pagina di GridView (fare clic per visualizzare l'immagine a dimensione intera)

Annotazioni

La logica di inserimento batch usata in questa esercitazione gestisce gli inserimenti all'interno di una transazione. Per verificarlo, introdurre intenzionalmente un errore a livello di database. Ad esempio, invece di assegnare la nuova proprietà ProductsRow dell'istanza CategoryID al valore selezionato nella Categories DropDownList, assegnarla a un valore come i * 5. Ecco i l'indicizzatore del ciclo e ha valori compresi tra 1 e 5. Pertanto, quando si aggiungono due o più prodotti in batch, il primo prodotto avrà un valore valido CategoryID (5), ma i prodotti successivi avranno valori CategoryID che non corrispondono ai valori CategoryID nella tabella Categories. L'effetto netto è che, mentre il primo INSERT avrà esito positivo, quelli successivi avranno esito negativo con una violazione del vincolo di chiave esterna. Poiché l'inserimento batch è atomico, verrà eseguito il rollback del primo INSERT , restituendo il database al relativo stato prima dell'avvio del processo di inserimento batch.

Riassunto

Nel corso di questo e dei due tutorial precedenti sono state create delle interfacce che permettono l'aggiornamento, l'eliminazione e l'inserimento di blocchi di dati, tutte utilizzavano il supporto per le transazioni che abbiamo aggiunto al livello di accesso ai dati nel tutorial Racchiudere le modifiche al database all'interno di una transazione. Per determinati scenari, tali interfacce utente di elaborazione batch migliorano notevolmente l'efficienza dell'utente finale riducendo il numero di clic, postback e commutatori di contesto da tastiera a mouse, mantenendo allo stesso tempo l'integrità dei dati sottostanti.

Questa esercitazione completa l'analisi dell'uso di dati in batch. Il set successivo di esercitazioni illustra diversi scenari avanzati del livello di accesso ai dati, tra cui l'uso di stored procedure nei metodi di TableAdapter, la configurazione delle impostazioni a livello di connessione e di comando in DAL, la crittografia delle stringhe di connessione e altro ancora.

Buon programmatori!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto a mitchell@4GuysFromRolla.com.

Grazie speciale a

Questa serie di esercitazioni è stata esaminata da molti revisori competenti. I principali revisori per questa esercitazione erano Hilton Giesenow e Søren Jacob Lauritsen. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, mandami un messaggio a mitchell@4GuysFromRolla.com.