批次插入 (VB)

作者 :Scott Mitchell

下載 PDF

瞭解如何在單一作業中插入多個資料庫記錄。 在 [使用者介面層] 中,我們會擴充 GridView,讓用戶能夠輸入多個新記錄。 在數據存取層中,我們會在交易內包裝多個插入作業,以確保所有插入成功或所有插入都會回復。

簡介

Batch 更新 教學課程中,我們探討了自定義 GridView 控件,以呈現可編輯多個記錄的介面。 瀏覽頁面的使用者可能會進行一系列變更,然後按下單一按鈕,執行批次更新。 對於使用者通常會一次更新許多記錄的情況,相較於第一次探索到 [ 插入、更新及刪除數據 ] 教學課程中的預設個別數據列編輯功能,這類介面可以儲存不計計數的點選和鍵盤對滑鼠內容切換。

新增記錄時,也可以套用這個概念。 假設在 Northwind Traders,我們通常會從包含特定類別之許多產品的供應商收到出貨。 例如,我們可能會從東京 Traders 收到六種不同的咖啡和咖啡產品出貨。 如果使用者透過DetailsView控件一次輸入六個產品,則必須再次選擇許多相同的值:他們必須選擇相同的類別 () 、相同的供應商 (東京 Traders) 、相同的已停止值 (False) ,以及訂單值 (0) 的相同單位。 這個重複的數據輸入不僅耗時,也很容易發生錯誤。

透過一些工作,我們可以建立批次插入介面,讓使用者選擇供應商和類別一次、輸入一系列的產品名稱和單位價格,然後按下按鈕將新產品新增至資料庫 (請參閱圖 1) 。 新增每個產品時,其 ProductName 和數據 UnitPrice 欄位會指派 TextBoxes 中輸入的值,而其 CategoryIDSupplierID 值則會從窗體頂端的DropDownLists 指派值。 DiscontinuedUnitsOnOrder 值分別設定為和 0 的硬式編碼值False

批次插入介面

圖 1:批次插入介面 (按下即可檢視完整大小的影像)

在本教學課程中,我們將建立一個頁面,以實作如圖 1 所示的批次插入介面。 如同前兩個教學課程,我們會將插入包裝在交易的範圍內,以確保不可部分完成性。 讓我們開始吧!

步驟 1:建立顯示介面

本教學課程將包含分成兩個區域的單一頁面:顯示區域和插入區域。 我們將在此步驟中建立的顯示介面會顯示 GridView 中的產品,並包含標題為 [處理產品出貨] 的按鈕。 按下此按鈕時,顯示介面會取代為插入介面,如圖 1 所示。 顯示介面會在按兩下 [從出貨新增產品] 或 [取消] 按鈕之後傳回。 我們將在步驟 2 中建立插入介面。

建立具有兩個介面的頁面時,一次只能看見其中一個介面,每個介面通常會放在 Panel Web 控制件中,做為其他控制件的容器。 因此,我們的頁面會針對每個介面各有兩個 Panel 控件。

從開啟BatchInsert.aspx資料夾中的頁面BatchData開始,並將面板從 [工具箱] 拖曳到 Designer (請參閱圖 2) 。 將 Panel 的 ID 屬性設定為 DisplayInterface。 將 Panel 新增至 Designer 時,其 HeightWidth 屬性會分別設定為 50px 和 125px。 從 屬性視窗清除這些屬性值。

將面板從 [工具箱] 拖曳到 Designer

圖 2:將面板從 [工具箱] 拖曳到 Designer ([按兩下] 以檢視大小完整的影像)

接下來,將 Button 和 GridView 控件拖曳至 [面板]。 將 Button s ID 屬性設定為 ProcessShipment ,並將其 Text 屬性設定為 [處理產品出貨]。 將 GridView 的 ID 屬性設定為 ProductsGrid ,並從其智慧標記將它系結至名為 ProductsDataSource的新 ObjectDataSource。 將 ObjectDataSource 設定為從 ProductsBLL 類別 s GetProducts 方法提取其數據。 由於此 GridView 僅用於顯示資料,因此請將 UPDATE、INSERT 和 DELETE 索引標籤標的下拉式清單設定為 [無) (。 按兩下 [完成] 以完成 [設定數據源精靈]。

顯示從 ProductsBLL 類別 s GetProducts 方法傳回的數據

圖 3:顯示從 ProductsBLL 類別 s GetProducts 方法傳回的數據, (按鍵即可檢視完整大小的影像)

將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 [無] ()

圖 4:將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無]) (按兩下即可檢視完整大小的影像)

完成 ObjectDataSource 精靈之後,Visual Studio 會為產品數據欄位新增 BoundFields 和 CheckBoxField。 拿掉、CategoryNameSupplierNameUnitPriceDiscontinued 欄位的所有ProductName專案。 您可以隨意進行任何美觀的自定義。 我決定將 UnitPrice 欄位格式化為貨幣值、重新排序欄位,並重新命名數個域 HeaderText 值。 此外,也可以藉由核取 GridView 智慧標記中的 [啟用分頁] 和 [啟用排序] 複選框,將 GridView 設定為包含分頁和排序支援。

新增 Panel、Button、GridView 和 ObjectDataSource 控件並自定義 GridView 字段之後,您的頁面宣告式標記看起來應該如下所示:

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

請注意,Button 和 GridView 的標記會出現在開頭和結尾 <asp:Panel> 標記內。 由於這些控件位於 Panel 內 DisplayInterface ,我們只要將 Panel 的 Visible 屬性設定為 False即可隱藏這些控件。 步驟 3 會以程式設計方式變更 Panel Visible 屬性,以回應按鈕按兩下以顯示一個介面,同時隱藏另一個介面。

請花點時間透過瀏覽器檢視我們的進度。 如圖 5 所示,您應該會看到 GridView 上方的 [處理產品出貨] 按鈕,一次列出 10 個產品。

GridView 清單 產品與供應專案排序和分頁功能

圖 5:GridView 清單 產品與供應專案排序和分頁功能 (按兩下即可檢視完整大小的影像)

步驟 2:建立插入介面

當顯示介面完成時,我們就可以開始建立插入介面。 在本教學課程中,讓我們建立插入介面,提示輸入單一供應商和類別值,然後允許使用者輸入最多五個產品名稱和單價值。 透過此介面,使用者可以新增一到五個新產品,這些新產品全都共用相同的類別和供應商,但具有唯一的產品名稱和價格。

首先,將面板從 [工具箱] 拖曳到 Designer,然後將它放在現有的 DisplayInterface [面板] 下方。 ID將這個新加入 Panel 的 屬性設定為 InsertingInterface ,並將其 Visible 屬性設定為 False。 我們會在步驟 3 中新增程式代碼,將 InsertingInterface Panel Visible 屬性設定為 True 。 同時清除 Panel 和 HeightWidth 屬性值。

接下來,我們需要建立如圖 1 所示的插入介面。 此介面可以透過各種 HTML 技術來建立,但我們會使用相當直接的介面:四欄、七列數據表。

注意

輸入 HTML <table> 元素的標記時,我偏好使用 [來源] 檢視。 雖然 Visual Studio 確實有可透過 Designer 新增<table>元素的工具,但 Designer 似乎太願意將未指派的style設定插入標記中。 建立<table>標記之後,我通常會返回 Designer 以新增 Web 控件並設定其屬性。 使用預先決定的數據行和數據列建立數據表時,我偏好使用靜態 HTML 而非 數據表 Web 控件 ,因為放置於表格 Web 控制件中的任何 Web 控制項只能使用 模式來存取 FindControl("controlID") 。 不過,我確實使用數據表 Web 控件來動態調整數據表大小, (數據列或數據行是以某些資料庫或使用者指定的準則為基礎的數據表) ,因為數據表 Web 控件可以透過程式設計方式建構。

Panel 的InsertingInterface標籤<asp:Panel>輸入下列標記:

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

<table> 標記尚未包含任何 Web 控件,我們會立即新增這些控制件。 請注意,每個 <tr> 元素都包含特定的 CSS 類別設定: BatchInsertHeaderRow 針對供應商和類別 DropDownLists 將移至的頁首數據列; BatchInsertFooterRow 針對頁尾數據列,其中 [從出貨新增產品] 和 [取消按鈕] 會前往;以及包含產品和單價 TextBox 控件的數據列,交替 BatchInsertRowBatchInsertAlternatingRow 值。 我已在 檔案中 Styles.css 建立對應的 CSS 類別,讓插入介面的外觀類似於我們在這些教學課程中使用的 GridView 和 DetailsView 控件。 這些 CSS 類別如下所示。

/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
    font-weight: bold;
    text-align: right;
}
.BatchInsertHeaderRow td
{
    color: White;
    background-color: #900;
    padding: 11px;
}
.BatchInsertFooterRow td
{
    text-align: center;
    padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
    background-color: #fcc;
}

輸入此標記后,返回 [設計] 檢視。 這<table>應該會顯示為 Designer 中的四欄、七列數據表,如圖 6 所示。

插入介面是由四個數據行所組成,Seven-Row 數據表

圖 6:插入介面是由四欄所組成,Seven-Row 數據表 (按兩下即可檢視完整大小的影像)

我們現在已準備好將 Web 控件新增至插入介面。 將兩個DropDownList從 [工具箱] 拖曳到第一個供貨商數據表中適當的單元格,另一個用於類別。

將供應商 DropDownList s ID 屬性設定為 Suppliers ,並將其系結至名為 SuppliersDataSource的新 ObjectDataSource。 將新的 ObjectDataSource 設定為從 SuppliersBLL 類別 s GetSuppliers 方法擷取其數據,並將 UPDATE 索引標籤下拉式清單設定為 [無]) (。 按一下 [完成] 以完成精靈。

將 ObjectDataSource 設定為使用 SuppliersBLL 類別的 GetSuppliers 方法

圖 7:將 ObjectDataSource 設定為使用 SuppliersBLL 類別 s GetSuppliers 方法, (按兩下即可檢視完整大小的影像)

Suppliers讓DropDownList顯示CompanyName資料欄位,並使用SupplierID資料欄位作為其ListItem值。

顯示 CompanyName 數據欄位並使用 SupplierID 作為值

圖 8:顯示 CompanyName [數據字段] 並使用 SupplierID 作為 [值] (按兩下即可檢視完整大小的影像)

將第二個DropDownList Categories 命名為,並將其系結至名為 CategoriesDataSource的新 ObjectDataSource。 將 CategoriesDataSource ObjectDataSource 設定為使用 CategoriesBLL 類別 s GetCategories 方法;將 UPDATE 和 DELETE 索引標籤中的下拉式清單設定為 ([無) ],然後按兩下 [完成] 以完成精靈。 最後,讓DropDownList顯示 CategoryName 數據欄位,並使用 CategoryID 作為值。

新增這兩個DropDownList並系結至適當設定的 ObjectDataSources 之後,您的畫面看起來應該類似圖 9。

標頭數據列現在包含供應商和類別 DropDownLists

圖 9:標頭數據列現在包含 SuppliersCategories DropDownLists (按兩下即可檢視完整大小的影像)

我們現在需要建立 TextBox,以收集每個新產品的名稱和價格。 將 TextBox 控制件從 [工具箱] 拖曳到五個產品名稱和價格數據列的每一個 Designer。 將 ID TextBoxes 的屬性設定為 ProductName1UnitPrice1ProductName2UnitPrice2ProductName3UnitPrice3等等。

在每個單價 TextBox 之後新增 CompareValidator,並將 ControlToValidate 屬性設定為適當的 ID。 同時將 Operator 屬性設定為 、ValueToCompare設定為 GreaterThanEqual0,並將 Type 設定為 Currency。 這些設定會指示 CompareValidator 確保輸入的價格是大於或等於零的有效貨幣值。 將 Text 屬性設定為 *,且 ErrorMessage 價格必須大於或等於零。 此外,請省略任何貨幣符號。

注意

即使資料庫數據表中的Products欄位不允許NULL值,ProductName插入介面也不會包含任何 RequiredFieldValidator 控件。 這是因為我們想要讓用戶輸入最多五個產品。 例如,如果使用者提供前三個數據列的產品名稱和單價,讓最後兩個數據列保持空白,我們只會將三個新產品新增至系統。 不過,由於 ProductName 是必要專案,因此我們必須以程式設計方式檢查,以確保是否已輸入單價來提供對應的產品名稱值。 我們將在步驟 4 中處理這項檢查。

驗證使用者的輸入時,CompareValidator 會在值包含貨幣符號時報告無效的數據。 在每個單價 TextBox 前面新增 $ 作為視覺提示,以指示使用者在輸入價格時省略貨幣符號。

最後,在 Panel 中InsertingInterface新增 ValidationSummary 控件,將其 屬性設定為 TrueShowMessageBox並將其 ShowSummary 屬性設定為 False。 使用這些設定時,如果使用者輸入無效的單價值,則會在違規的 TextBox 控件旁邊顯示星號,而 ValidationSummary 會顯示一個用戶端消息框,其中顯示我們稍早指定的錯誤訊息。

此時,您的畫面看起來應該類似圖 10。

插入介面現在包含產品名稱和價格的 TextBox

圖 10:插入介面現在包含 [產品名稱] 和 [價格] 的文字框, (按兩下即可檢視完整大小的影像)

接下來,我們需要將 [從出貨新增產品] 和 [取消] 按鈕新增至頁尾數據列。 將 [工具箱] 中的兩個 [按鈕] 控件拖曳到插入介面的頁尾,將 Buttons ID 屬性設定為 AddProducts ,並將CancelButtonText屬性分別設定為 [從出貨新增產品] 和 [取消]。 此外,將 CancelButton 控制件的 CausesValidation 屬性設定為 false

最後,我們需要新增標籤 Web 控制件,以顯示兩個介面的狀態消息。 例如,當使用者成功新增產品出貨時,我們想要返回顯示介面並顯示確認訊息。 不過,如果使用者提供新產品的價格,但離開產品名稱,我們必須顯示警告訊息,因為 ProductName 需要字段。 由於我們需要這兩個介面顯示此訊息,請將它放在面板外部頁面的頂端。

將標籤 Web 控制件從 [工具箱] 拖曳至頁面頂端 Designer。 將 ID 屬性設定為 StatusLabel、清除 Text 屬性,並將與 EnableViewState 屬性設定VisibleFalse。 如先前教學課程中所見,將 屬性設定 EnableViewStateFalse 可讓我們以程式設計方式變更 Label 的屬性值,並讓它們自動還原回後續回傳的預設值。 這可簡化程式代碼,以顯示狀態消息,以回應後續回傳時消失的某些用戶動作。 最後,將 StatusLabel 控件的 CssClass 屬性設定為Warning,這是中 Styles.css 定義的 CSS 類別名稱,該類別會在大型斜體、粗體、粗體、紅色字型中顯示文字。

圖 11 顯示新增和設定標籤之後的 Visual Studio Designer。

將 StatusLabel 控件放在兩個面板控件上方

圖 11:將控件放在兩個 StatusLabel 面板控件上方, (按兩下即可檢視完整大小的影像)

步驟 3:在顯示和插入介面之間切換

此時,我們已完成顯示和插入介面的標記,但仍會留下兩項工作:

  • 在顯示與插入介面之間切換
  • 將出貨中的產品新增至資料庫

目前顯示介面是可見的,但插入介面是隱藏的。 這是因為 Panel 的 Visible 屬性設定為 True (預設值) ,而 InsertingInterface Panel 的 Visible 屬性設定為 FalseDisplayInterface 若要在兩個介面之間切換,我們只需要切換每個控件的 Visible 屬性值。

當單擊 [處理產品出貨] 按鈕時,我們想要從顯示介面移至插入介面。 因此,請為此 Button s Click 事件建立事件處理程式,其中包含下列程式代碼:

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

此程式代碼只會隱藏 DisplayInterface 面板並顯示 InsertingInterface 面板。

接下來,在插入介面中建立 [從出貨新增產品] 和 [取消按鈕] 控件的事件處理程式。 按兩下其中一個按鈕時,我們需要還原回顯示介面。 建立 Click 這兩個 Button 控件的事件處理程式,以便呼叫 ReturnToDisplayInterface,我們將會立即新增的方法。 除了隱藏 InsertingInterface Panel 並顯示 DisplayInterface Panel 之外, ReturnToDisplayInterface 方法還需要將 Web 控件傳回其預先編輯狀態。 這牽涉到將DropDownLists SelectedIndex 屬性設定為0,並清除 Text TextBox控件的屬性。

注意

如果我們未在返回顯示介面之前,將控件傳回其預先編輯狀態,請考慮會發生什麼情況。 使用者可以按下 [處理產品出貨] 按鈕,從出貨輸入產品,然後按兩下 [從出貨新增產品]。 這會新增產品,並將用戶傳回顯示介面。 此時,使用者可能會想要新增另一個出貨。 按兩下 [處理產品出貨] 按鈕時,他們會返回插入介面,但DropDownList 選取專案和 TextBox 值仍會填入其先前的值。

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

這兩 Click 個事件處理程式只會呼叫 ReturnToDisplayInterface 方法,雖然我們會回到步驟 4 中的從出貨 Click 新增產品事件處理程式,並新增程式代碼以儲存產品。 ReturnToDisplayInterface 首先, Suppliers 將和 Categories DropDownLists傳回至其第一個選項。 兩個常數 firstControlID ,並 lastControlID 標記在插入介面中命名產品名稱和單價 TextBox 時所使用的開始和結束控件索引值,並用於迴圈的 For 界限,將 TextBox 控件的屬性設定 Text 回空字元串。 最後,Panels Visible 屬性會重設,以便隱藏插入介面並顯示顯示介面。

請花點時間在瀏覽器中測試此頁面。 第一次瀏覽頁面時,您應該會看到顯示介面,如圖 5 所示。 按兩下 [處理產品出貨] 按鈕。 頁面將會回傳,您現在應該會看到插入介面,如圖 12 所示。 按兩下 [從出貨新增產品] 或 [取消] 按鈕會將您傳回顯示介面。

注意

檢視插入介面時,請花點時間測試單價 TextBox 上的 CompareValidators。 按兩下 [從出貨新增產品] 按鈕時,您應該會看到用戶端消息框警告,其貨幣值無效或值為小於零的價格。

按兩下 [處理產品出貨] 按鈕之後,即會顯示插入介面

圖 12:按兩下 [處理產品出貨] 按鈕之後,就會顯示插入介面, (按兩下即可檢視全大小的影像)

步驟 4:新增產品

本教學課程保留的所有專案,都是將產品儲存至從出貨按鈕新增 Click 產品事件處理程式中的資料庫。 這可以藉由為提供的每個產品名稱建立 ProductsDataTable 和 新增 ProductsRow 實例來完成。 新增這些 ProductsRow 之後,我們將呼叫ProductsBLL傳入 的ProductsDataTable類別方法UpdateWithTransaction。 回想一 UpdateWithTransaction 下,在 交易教學課程中包裝資料庫修改 中建立的方法會傳遞 ProductsDataTableProductsTableAdapterUpdateWithTransaction 方法。 從該處開始 ADO.NET 交易,而 TableAdapter 會針對 DataTable 中每個新增ProductsRow的資料庫發出INSERT語句。 假設所有產品都會新增而不會發生錯誤,則會認可交易,否則會回復。

從出貨按鈕 Click 新增產品事件處理程式的程式代碼也需要執行一些錯誤檢查。 由於插入介面中未使用 RequiredFieldValidator,因此使用者可以在省略其名稱時輸入產品的價格。 由於需要產品名稱,因此,如果這類條件展開,我們需要警示使用者,而不繼續進行插入。 完整的 Click 事件處理程式程式代碼如下:

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

事件處理程式一開始會確保 Page.IsValid 屬性傳回的值 True。 如果傳回 False,則表示一或多個 CompareValidator 會報告無效的數據;在這種情況下,我們不想嘗試插入輸入的產品,否則在嘗試將使用者輸入的單價值指派給 ProductsRow s UnitPrice 屬性時,最後會有例外狀況。

接下來,會在) (products 建立新的ProductsDataTable實例。 For循環可用來逐一查看產品名稱和單價 TextBox,而且Text屬性會讀入局部變數productNameunitPrice。 如果使用者已輸入單價的值,但未輸入對應產品名稱的值,會顯示 StatusLabel 訊息 如果您提供單價,您也必須包含產品名稱,並結束事件處理程式。

如果已提供產品名稱,則會使用 ProductsDataTable s NewProductsRow 方法建立新的ProductsRow實例。 這個新的 ProductsRow 實例屬性 ProductName 會設定為目前的產品名稱 TextBox, SupplierID 而 和 CategoryID 屬性則會指派給 SelectedValue 插入介面 s 標頭中 DropDownLists 的屬性。 如果使用者輸入產品價格的值,則會將它指派給 ProductsRow 實例的 UnitPrice 屬性;否則,該屬性會保持未指派,這會導致 NULL 資料庫中的值 UnitPrice 。 最後, Discontinued 會將 和 UnitsOnOrder 屬性分別指派給硬式編碼值 False 和0。

將屬性指派給 ProductsRow 實體之後,它會新增至 ProductsDataTable

在迴圈完成時 For ,我們會檢查是否已新增任何產品。 使用者可能之後,已按兩下 [從出貨新增產品] ,再輸入任何產品名稱或價格。 如果 中 ProductsDataTable至少有一個產品,則會 ProductsBLL 呼叫 類別 s UpdateWithTransaction 方法。 接下來,數據會重新系結至 ProductsGrid GridView,以便新新增的產品會出現在顯示介面中。 StatusLabel會更新 以顯示確認訊息,並ReturnToDisplayInterface叫用 ,隱藏插入介面並顯示顯示介面。

如果未輸入任何產品,插入介面仍會顯示,但未新增任何產品訊息。 請在文字框中輸入產品名稱和單位價格。

圖 13、14 和 15 顯示動作中的插入和顯示介面。 在圖 13 中,使用者輸入了沒有對應產品名稱的單位價格值。 圖 14 顯示成功新增三個新產品之後的顯示介面,而圖 15 顯示 GridView 中新增的兩個產品, (第三個產品位於上一頁) 。

輸入單價時需要產品名稱

圖 13:輸入單價時需要產品名稱 (按兩下即可檢視大小完整的影像)

已為供應商 Mayumi s 新增三個新的 Veggggies

圖 14:已針對供應商 Mayumi s 新增三個新的 Veggies (按兩下以檢視大小完整的影像)

您可以在 GridView 的最後一頁找到新產品

圖 15:您可以在 GridView 的最後一頁找到新產品 (按兩下即可檢視大小完整的影像)

注意

本教學課程中使用的批次插入邏輯會將插入包裝在交易的範圍內。 若要確認這一點,請以目的方式引入資料庫層級錯誤。 例如,不要將新的 ProductsRow 實例屬性 CategoryID 指派給DropDownList中的 Categories 選取值,而是將它指派給類似 i * 5的值。 以下是 i 迴圈索引器,其值範圍從 1 到 5。 因此,在批次插入中新增兩個或多個產品時,第一個 CategoryID 產品的有效值會 (5) ,但後續產品會有 CategoryID 的值不符合 CategoryID 數據表中的 Categories 值。 淨效果是,當第一個 INSERT 成功時,後續的結果將會因為外鍵條件約束違規而失敗。 由於批次插入是不可部分完成的,因此會回復第一個 INSERT ,在批次插入程序開始之前,將資料庫傳回其狀態。

摘要

在這兩個教學課程中,我們已建立介面,以允許更新、刪除和插入批次數據,這些介面都使用我們在 交易內包裝資料庫修改 中新增至數據存取層的交易支援。 在某些情況下,這類批處理使用者介面可藉由減少點擊次數、回傳和鍵盤對滑鼠內容切換,同時維持基礎數據的完整性,大幅提升使用者效率。

本教學課程會完成使用批處理數據的概念。 下一組教學課程會探索各種進階數據存取層案例,包括使用 TableAdapter 方法中的預存程式、在 DAL 中設定連線和命令層級設定、加密連接字串等等!

快樂的程序設計!

關於作者

Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET

特別感謝

本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是「前者 Giesenow」和「S ren#Lauritsen」。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。