次の方法で共有


一括挿入 (VB)

スコット・ミッチェルによる

PDF をダウンロードする

1 回の操作で複数のデータベース レコードを挿入する方法について説明します。 ユーザー インターフェイス レイヤーでは、GridView を拡張して、ユーザーが複数の新しいレコードを入力できるようにします。 データ アクセス層では、トランザクション内で複数の挿入操作をラップして、すべての挿入が成功するか、すべての挿入がロールバックされるようにします。

イントロダクション

バッチ更新のチュートリアルでは、GridView コントロールをカスタマイズして、複数のレコードが編集可能なインターフェイスを表示する方法について説明しました。 ページにアクセスするユーザーは一連の変更を行い、1 回のボタン クリックでバッチ更新を実行できます。 ユーザーが一度に多くのレコードを更新するのが一般的な場合、このようなインターフェイスは、 データの挿入、更新、削除の概要 チュートリアルで最初に確認した既定の行ごとの編集機能と比較して、数え切れないほどのクリックとキーボードからマウスへのコンテキスト スイッチを保存できます。

この概念は、レコードを追加するときにも適用できます。 ここで、Northwind Traders では、特定のカテゴリの製品を多数含むサプライヤーから出荷を受け取ることがよくあります。 たとえば、東京トレーダーズから6種類の紅茶とコーヒー製品が出荷される場合があります。 ユーザーが DetailsView コントロールを使用して 6 つの製品を一度に 1 つずつ入力した場合、同じ値の多くを何度も選択する必要があります。同じカテゴリ (飲料)、同じサプライヤー (東京トレーダー)、同じ廃止値 (False)、注文値 (0) の同じ単位を選択する必要があります。 この繰り返しのデータ入力には時間がかかるだけでなく、エラーが発生しやすくなります。

少しの作業で、ユーザーがサプライヤーとカテゴリを一度選択し、一連の製品名と単価を入力し、ボタンをクリックして新しい製品をデータベースに追加できるようにするバッチ挿入インターフェイスを作成できます (図 1 を参照)。 各製品が追加されると、その ProductNameUnitPrice のデータ フィールドにはテキスト ボックスに入力された値が割り当てられます。一方、 CategoryID 値と SupplierID 値には、フォームの上部にある DropDownLists の値が割り当てられます。 Discontinued値とUnitsOnOrder値はそれぞれ、Falseと 0 のハードコーディングされた値に設定されます。

バッチ挿入インターフェイス

図 1: バッチ挿入インターフェイス (フルサイズの画像を表示する] をクリックします)

このチュートリアルでは、図 1 に示すバッチ挿入インターフェイスを実装するページを作成します。 前の2つのチュートリアルと同様に、アトミック性を確保するために、トランザクションのスコープ内で挿入操作を包み込みます。 では、始めましょう。

手順 1: 表示インターフェイスを作成する

このチュートリアルは、表示領域と挿入領域の 2 つの領域に分割された 1 つのページで構成されます。 この手順で作成する表示インターフェイスには、GridView に製品が表示され、[Process Product Shipment]\(製品出荷の処理\) というタイトルのボタンが含まれています。 このボタンをクリックすると、図 1 に示すように、表示インターフェイスが挿入インターフェイスに置き換えられます。 表示インターフェイスは、[出荷から製品を追加] または [キャンセル] ボタンがクリックされた後に戻ります。 手順 2 で挿入インターフェイスを作成します。

一度に 1 つだけ表示される 2 つのインターフェイスを持つページを作成する場合、各インターフェイスは通常、他のコントロールのコンテナーとして機能する Panel Web コントロール内に配置されます。 したがって、このページには、インターフェイスごとに 2 つの Panel コントロールが 1 つあります。

まず、BatchInsert.aspx フォルダーのBatchData ページを開き、ツールボックスからデザイナーにパネルをドラッグします (図 2 を参照)。 パネルの ID プロパティを DisplayInterfaceに設定します。 パネルをデザイナーに追加すると、その Height プロパティと Width プロパティはそれぞれ 50px と 125px に設定されます。 [プロパティ] ウィンドウからこれらのプロパティ値をクリアします。

ツールボックスからデザイナーにパネルをドラッグする

図 2: ツールボックスからデザイナーにパネルをドラッグする (フルサイズの画像を表示する をクリックします)

次に、Button コントロールと GridView コントロールをパネルにドラッグします。 ボタンの ID プロパティを ProcessShipment に設定し、その Text プロパティを [製品出荷の処理] に設定します。 GridView の ID プロパティを ProductsGrid に設定し、スマート タグから ProductsDataSource という名前の新しい ObjectDataSource にバインドします。 ProductsBLL クラスの GetProducts メソッドからデータをプルするように ObjectDataSource を構成します。 この GridView はデータの表示にのみ使用されるため、[UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを [なし] に設定します。 [完了] をクリックして、データ ソースの構成ウィザードを完了します。

ProductsBLL クラスの GetProducts メソッドから返されたデータを表示する

図 3: ProductsBLL クラスの GetProducts メソッドから返されたデータを表示します (フルサイズの画像を表示する をクリックします)。

UPDATE タブ、INSERT タブ、DELETE タブの Drop-Down リストを (なし) に設定する

図 4: [UPDATE]、[INSERT]、[DELETE] タブの [Drop-Down リスト] を [なし] に設定する (フルサイズの画像を表示する 場合はクリックします)

ObjectDataSource ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が追加されます。 ProductNameCategoryNameSupplierNameUnitPriceDiscontinuedの各フィールドを除くすべてのフィールドを削除します。 美的なカスタマイズを自由に行うことができます。 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> 内に表示されることに注意してください。 これらのコントロールは DisplayInterface パネル内に存在するため、パネルの Visible プロパティを False に設定するだけで非表示にできます。 手順 3 では、ボタン クリックに応じて Panel の Visible プロパティをプログラムで変更し、もう一方のインターフェイスを非表示にして表示します。

少し時間を取って、ブラウザーで進行状況を確認してみましょう。 図 5 に示すように、一度に 10 個の製品を一覧表示する GridView の上に [製品出荷の処理] ボタンが表示されます。

GridView は製品を一覧表示し、並べ替えとページング機能を提供します

図 5: GridView は、製品の一覧と並べ替えとページング機能を提供します (フルサイズの画像を表示する をクリックします)。

手順 2: 挿入インターフェイスの作成

表示インターフェイスが完成したら、挿入インターフェイスを作成する準備ができました。 このチュートリアルでは、1 つのサプライヤーとカテゴリの値を求める挿入インターフェイスを作成し、ユーザーが最大 5 つの製品名と単価値を入力できるようにします。 このインターフェイスを使用すると、ユーザーは、すべて同じカテゴリとサプライヤーを共有するが、固有の製品名と価格を持つ 1 から 5 つの新製品を追加できます。

まず、ツールボックスからデザイナーにパネルをドラッグし、既存の DisplayInterface パネルの下に配置します。 この新しく追加された Panel の ID プロパティを InsertingInterface に設定し、その Visible プロパティを Falseに設定します。 手順 3 で InsertingInterface Panel の Visible プロパティを True に設定するコードを追加します。 また、パネルの HeightWidth プロパティの値をクリアします。

次に、図 1 に示した挿入インターフェイスを作成する必要があります。 このインターフェイスは、さまざまな HTML 手法を使用して作成できますが、4 列 7 行のテーブルという非常に単純なインターフェイスを使用します。

HTML <table> 要素のマークアップを入力するときは、ソース ビューを使用します。 Visual Studioにはデザイナーを使って<table>要素を追加するためのツールが含まれていますが、デザイナーはマークアップに意図しないstyle設定をあまりにも容易に挿入する傾向があるようです。 <table>マークアップを作成したら、通常はデザイナーに戻って Web コントロールを追加し、そのプロパティを設定します。 事前に決定された列と行を含むテーブルを作成する場合、 Table Web コントロール 内に配置された Web コントロールには FindControl("controlID") パターンのみを使用してアクセスできるため、Table Web コントロールではなく静的 HTML を使用することをお勧めします。 しかし、Table Web コントロールはプログラムで構築できるため、動的にサイズ変更されたテーブル (行または列がデータベースまたはユーザー指定の条件に基づくもの) には Table Web コントロールを使用します。

<asp:Panel> パネルの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>

この <table> マークアップには、Web コントロールがまだ含まれていません。これらのコントロールを一時的に追加します。 各 <tr> 要素には、特定の CSS クラス設定が含まれています。仕入先とカテゴリの DropDownList が移動するヘッダー行の BatchInsertHeaderRow 、出荷ボタンとキャンセル ボタンから製品を追加するフッター行の BatchInsertFooterRow 、製品と単価の TextBox コントロールを含む行の BatchInsertRowBatchInsertAlternatingRow の値を交互に設定します。 私は、これらのチュートリアルで使用した GridView コントロールと DetailsView コントロールのような外観を挿入インターフェイスに与えるために、 Styles.css ファイルに対応する CSS クラスを作成しました。 これらの 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;
}

このマークアップを入力して、デザイン ビューに戻ります。 図 6 に示すように、この <table> はデザイナーで 4 列 7 行のテーブルとして表示されます。

挿入インターフェイスは、4 列の Seven-Row テーブルで構成されます

図 6: 挿入インターフェイスは、4 列の Seven-Row テーブルで構成されています (フルサイズの画像を表示する をクリックします)。

これで、挿入インターフェイスに Web コントロールを追加する準備ができました。 ツールボックスから 2 つの DropDownList を、仕入先用とカテゴリ用のテーブルの適切なセルにドラッグします。

仕入先 DropDownList の ID プロパティを Suppliers に設定し、 SuppliersDataSourceという名前の新しい ObjectDataSource にバインドします。 SuppliersBLL クラスの GetSuppliers メソッドからデータを取得するように新しい ObjectDataSource を構成し、UPDATE タブのドロップダウン リストを (None) に設定します。 [完了] をクリックして、ウィザードを完了します。

SuppliersBLL クラスの GetSuppliers メソッドを使用するように ObjectDataSource を構成する

図 7: SuppliersBLL クラスの GetSuppliers メソッドを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)。

Suppliers DropDownList に CompanyName データ フィールドを表示し、SupplierID データ フィールドをListItemの値として使用します。

CompanyName データ フィールドを表示し、値として SupplierID を使用する

図 8: CompanyName データ フィールドを表示し、値として SupplierID を使用する (フルサイズの画像を表示する] をクリックします)。

2 番目の DropDownList Categories に名前を付け、 CategoriesDataSourceという名前の新しい ObjectDataSource にバインドします。 CategoriesDataSource クラスの CategoriesBLL メソッドを使用するように GetCategories ObjectDataSource を構成します。[UPDATE] タブと [DELETE] タブのドロップダウン リストを [なし] に設定し、[完了] をクリックしてウィザードを完了します。 最後に、DropDownList に CategoryName データ フィールドを表示し、値として CategoryID を使用します。

これら 2 つの DropDownList が追加され、適切に構成された ObjectDataSource にバインドされると、画面は図 9 のようになります。

ヘッダー行に仕入先とカテゴリの DropDownList が含まれるようになりました

図 9: ヘッダー行に SuppliersCategories DropDownLists が含まれるようになりました (フルサイズの画像を表示する をクリックします)。

ここでは、新しい各製品の名前と価格を収集する TextBoxes を作成する必要があります。 ツールボックスから 5 つの製品名と価格行のそれぞれについて、TextBox コントロールをデザイナーにドラッグします。 TextBoxes の ID プロパティを、 ProductName1UnitPrice1ProductName2UnitPrice2ProductName3UnitPrice3などを設定します。

各単価テキスト ボックスの後に CompareValidator を追加し、 ControlToValidate プロパティを適切な IDに設定します。 また、 Operator プロパティを GreaterThanEqualValueToCompare 0、 TypeCurrency に設定します。 これらの設定は、CompareValidator に対して、入力された価格が 0 以上の有効な通貨値であることを確認するように指示します。 Textプロパティを * に設定し、ErrorMessage に「価格はゼロ以上である必要があります。」と設定します。 また、通貨記号は省略してください。

挿入インターフェイスには RequiredFieldValidator コントロールは含まれません。ただし、ProductName データベース テーブルのProducts フィールドではNULL値が許可されません。 これは、ユーザーが最大 5 つの製品を入力できるようにするためです。 たとえば、ユーザーが最初の 3 行の製品名と単価を指定し、最後の 2 行を空白のままにした場合、3 つの新しい製品をシステムに追加するだけです。 ただし、 ProductName が必要であるため、単価が入力されているかどうかをプログラムで確認し、対応する製品名の値が指定されていることを確認する必要があります。 手順 4 でこのチェックに取り組みます。

ユーザーの入力を検証するときに、値に通貨記号が含まれている場合、CompareValidator は無効なデータを報告します。 価格を入力するときに通貨記号を省略するようにユーザーに指示する視覚的な手掛かりとして機能する各単価の TextBoxes の前に $ を追加します。

最後に、 InsertingInterface パネル内に ValidationSummary コントロールを追加し、 ShowMessageBox プロパティを True に設定し、 ShowSummary プロパティを False に設定します。 これらの設定では、ユーザーが無効な単価値を入力すると、問題のある TextBox コントロールの横にアスタリスクが表示され、ValidationSummary には、前に指定したエラー メッセージを示すクライアント側のメッセージ ボックスが表示されます。

この時点で、画面は図 10 のようになります。

挿入インターフェイスに、製品名と価格のテキスト ボックスが含まれるようになりました

図 10: 挿入インターフェイスには、製品名と価格のテキスト ボックスが含まれるようになりました (フルサイズの画像を表示する をクリックします)。

次に、[出荷から製品を追加] ボタンと [キャンセル] ボタンをフッター行に追加する必要があります。 ツールボックスから挿入インターフェイスのフッターに 2 つの Button コントロールをドラッグし、Buttons ID プロパティを AddProducts プロパティと CancelButton プロパティに設定し、 Text プロパティを [出荷から製品を追加] プロパティと [キャンセル] プロパティにそれぞれ設定します。 さらに、 CancelButton コントロールの CausesValidation プロパティを false に設定します。

最後に、2 つのインターフェイスのステータス メッセージを表示する Label Web コントロールを追加する必要があります。 たとえば、ユーザーが製品の新しい出荷を正常に追加したら、表示インターフェイスに戻り、確認メッセージを表示します。 ただし、ユーザーが新しい製品の価格を指定しても製品名を省略した場合は、 ProductName フィールドが必要であるため、警告メッセージを表示する必要があります。 このメッセージは両方のインターフェイスに対して表示する必要があるため、パネルの外側のページの上部に配置します。

ラベル Web コントロールをツールボックスからデザイナーのページの上部にドラッグします。 ID プロパティをStatusLabelに設定し、Text プロパティをクリアして、VisibleプロパティとEnableViewStateプロパティをFalseに設定します。 前のチュートリアルで説明したように、 EnableViewState プロパティを False に設定すると、プログラムによって Label のプロパティ値を変更し、後続のポストバック時に自動的に既定値に戻すことができます。 これにより、後続のポストバックで消えるユーザー アクションに応答してステータス メッセージを表示するコードが簡略化されます。 最後に、 StatusLabel コントロールの CssClass プロパティを Warning に設定します。これは、大きな斜体、太字、赤のフォントでテキストを表示する Styles.css で定義された CSS クラスの名前です。

図 11 は、ラベルが追加および構成された後の Visual Studio デザイナーを示しています。

2 つのパネル コントロールの上に StatusLabel コントロールを配置する

図 11: 2 つのパネル コントロールの上に StatusLabel コントロールを配置します (フルサイズの画像を表示する をクリックします)。

手順 3: ディスプレイインターフェイスと挿入インターフェイスの切り替え

この時点で、インターフェイスの表示と挿入のマークアップを完了しましたが、次の 2 つのタスクが残っています。

  • ディスプレイと挿入インターフェイスの切り替え
  • 出荷の製品をデータベースに追加する

現在、表示インターフェイスは表示されますが、挿入インターフェイスは非表示になっています。 これは、 DisplayInterface Panel の Visible プロパティが True (既定値) に設定されているのに対し、 InsertingInterface Panel の Visible プロパティは False に設定されているためです。 2 つのインターフェイスを切り替えるには、各コントロールの Visible プロパティ値を切り替える必要があります。

[製品出荷の処理]ボタンがクリックされたときに、表示インタフェースから挿入インタフェースに移動します。 そのため、次のコードを含むこの Button の Click イベントのイベント ハンドラーを作成します。

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

このコードでは、 DisplayInterface パネルが非表示になり、 InsertingInterface パネルが表示されます。

次に、挿入インターフェイスの [出荷からの製品の追加] コントロールと [キャンセル] ボタン コントロールのイベント ハンドラーを作成します。 これらのボタンのいずれかがクリックされたら、表示インターフェイスに戻す必要があります。 両方の Button コントロール Click イベント ハンドラーを作成して、 ReturnToDisplayInterfaceを呼び出すことができるようにします。これは、一時的に追加するメソッドです。 InsertingInterface パネルを非表示にし、DisplayInterface パネルを表示するだけでなく、ReturnToDisplayInterface メソッドは、Web コントロールを編集前の状態に戻す必要があります。 これには、DropDownLists SelectedIndex プロパティを 0 に設定し、TextBox コントロールの Text プロパティをクリアする必要があります。

表示インターフェイスに戻る前に、コントロールを編集前の状態に戻さなかった場合に何が起こるかを検討してください。 ユーザーは、[製品出荷の処理] ボタンをクリックし、出荷の製品を入力して、[出荷から製品を追加] をクリックします。 これにより、製品が追加され、ユーザーが表示インターフェイスに戻ります。 この時点で、ユーザーは別の出荷を追加できます。 [製品出荷の処理] ボタンをクリックすると、挿入インターフェイスに戻りますが、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 イベント ハンドラーに戻り、製品を保存するコードを追加します。 ReturnToDisplayInterfaceSuppliers および Categories のドロップダウンリストを最初のオプションに戻します。 2 つの定数firstControlIDおよびlastControlIDは、挿入インターフェイスで製品名と単価の TextBoxes の名前付けに使用される開始および終了コントロールのインデックス値をマークし、TextBox コントロールのForプロパティを空の文字列に戻すText ループの境界で使用されます。 最後に、挿入インターフェイスが非表示になり、表示インターフェイスが表示されるように、パネル Visible プロパティがリセットされます。

ブラウザーでこのページをテストします。 最初にページにアクセスすると、図 5 に示すように表示インターフェイスが表示されます。 [製品出荷の処理] ボタンをクリックします。 ページがポストバックされ、図 12 に示すように挿入インターフェイスが表示されます。 [出荷から製品を追加] または [キャンセル] ボタンをクリックすると、表示インターフェイスに戻ります。

挿入インターフェイスを表示しているときに、単価テキストボックスで CompareValidator を試してみてください。 無効な通貨値または値が 0 未満の価格の [出荷から製品を追加] ボタンをクリックすると、クライアント側のメッセージ ボックスの警告が表示されます。

プロセス製品出荷ボタンをクリックすると、挿入インタフェースが表示されます。

図 12: プロセス製品出荷ボタンをクリックした後、挿入インターフェイスが表示されます (フルサイズの画像を表示する をクリックします)。

手順 4: 製品を追加する

このチュートリアルで残っているのは、出荷ボタンの [製品の追加] イベント ハンドラーでデータベースに製品 Click 保存することです。 これを実現するには、 ProductsDataTable を作成し、指定された製品名ごとに ProductsRow インスタンスを追加します。 これらのProductsRowが追加されたら、ProductsBLLを渡すUpdateWithTransactionクラスのProductsDataTableメソッドを呼び出します。 UpdateWithTransactionチュートリアルで作成されたメソッドがProductsDataTableProductsTableAdapterメソッドにUpdateWithTransactionを渡すことを思い出してください。 そこから、ADO.NET トランザクションが開始され、TableAdapter は DataTable に追加された各INSERTに対してProductsRow ステートメントをデータベースに発行します。 すべての製品がエラーなしで追加されると仮定すると、トランザクションはコミットされ、それ以外の場合はロールバックされます。

出荷ボタンの 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が返された場合は、1 つ以上の CompareValidator が無効なデータを報告していることを意味します。このような場合は、入力した製品を挿入しようとしません。ユーザーが入力した単価値をProductsRowUnitPrice プロパティに割り当てようとすると、例外が発生します。

次に、新しい ProductsDataTable インスタンスが作成されます (products)。 For ループは、製品名と単価の TextBox を反復処理するために使用され、Textプロパティはローカル変数productNameおよびunitPriceに読み込まれます。 ユーザーが単価の値を入力したが、対応する製品名の値を入力していない場合は、 StatusLabel にメッセージが表示されます。単価を指定する場合は、製品の名前も含める必要があり、イベント ハンドラーが終了します。

製品名が指定されている場合は、ProductsRowProductsDataTable メソッドを使用して新しいNewProductsRow インスタンスが作成されます。 この新しい ProductsRow インスタンスの ProductName プロパティは現在の製品名 TextBox に設定され、 SupplierID プロパティと CategoryID プロパティは挿入インターフェイスのヘッダーの DropDownLists の SelectedValue プロパティに割り当てられます。 ユーザーが製品の価格の値を入力した場合、ProductsRow インスタンスの UnitPrice プロパティに割り当てられます。それ以外の場合、プロパティは未割り当てのままにされ、データベース内のNULLUnitPrice値になります。 最後に、 Discontinued プロパティと UnitsOnOrder プロパティがそれぞれ、ハードコーディングされた値 False と 0 に割り当てられます。

プロパティが ProductsRow インスタンスに割り当てられた後、 ProductsDataTableに追加されます。

For ループが完了すると、製品が追加されたかどうかを確認します。 ユーザーは、結局のところ、製品名や価格を入力する前に、出荷から製品を追加するをクリックすることができます。 ProductsDataTableに少なくとも 1 つの製品がある場合は、ProductsBLL クラスの UpdateWithTransaction メソッドが呼び出されます。 次に、新しく追加された製品が表示インターフェイスに表示されるように、データが ProductsGrid GridView にリバインドされます。 StatusLabelが更新されて確認メッセージが表示され、ReturnToDisplayInterfaceが呼び出され、挿入インターフェイスが非表示になり、表示インターフェイスが表示されます。

製品が入力されなかった場合、挿入インターフェースは表示されたままですが、「製品は追加されませんでした」というメッセージが表示されます。 テキストボックスに製品名と単価を入力してください。

図 13、14、および 15 は、実際のインターフェイスの挿入と表示を示しています。 図 13 では、ユーザーは対応する製品名なしで単価値を入力しています。 図 14 は、3 つの新しい製品が正常に追加された後の表示インターフェイスを示しています。図 15 は、GridView に新しく追加された 2 つの製品を示しています (3 番目の製品は前のページにあります)。

単価を入力するときに製品名が必要です

図 13: 単価を入力するときに製品名が必要です (フルサイズの画像を表示する をクリックします)。

3つの新しい野菜がサプライヤー真由美sのために追加されました

図 14: 3 つの新しい野菜が追加された仕入先真由美 s (フルサイズの画像を表示する をクリックします)。

新しい製品は GridView の最後のページにあります

図 15: 新しい製品は、GridView の最後のページで見つけることができます (フルサイズの画像を表示する をクリックします)。

このチュートリアルで使用するバッチ挿入論理は、トランザクションのスコープ内に挿入を組み込みます。 これを確認するには、意図的にデータベース レベルのエラーを引き起こします。 たとえば、新しい ProductsRow インスタンスの CategoryID プロパティを Categories DropDownList で選択した値に割り当てるのではなく、 i * 5などの値に割り当てます。 ここで i はループ インデクサーであり、1 から 5 の範囲の値を持ちます。 したがって、2 つ以上の製品をバッチ挿入で追加すると、最初の製品は有効なCategoryID値 (5) になりますが、後続の製品には、CategoryID テーブルの最大CategoryID値と一致しないCategories値が含まれます。 その結果、最初の INSERT は成功しますが、後続のは外部キー制約違反で失敗します。 バッチ挿入はアトミックであるため、最初の INSERT はロールバックされ、バッチ挿入プロセスが開始される前の状態にデータベースが返されます。

概要

このチュートリアルと前の 2 つのチュートリアルでは、データのバッチの更新、削除、挿入を可能にするインターフェイスを作成しました。そのすべてが、 トランザクション チュートリアル内のデータベース変更の折り返し でデータ アクセス層に追加したトランザクション サポートを使用しました。 特定のシナリオでは、このようなバッチ処理ユーザー インターフェイスは、基になるデータの整合性を維持しながら、クリック数、ポストバック、キーボードからマウスへのコンテキスト スイッチの数を減らすことで、エンド ユーザーの効率を大幅に向上させます。

このチュートリアルでは、バッチ 処理されたデータの操作について説明します。 次の一連のチュートリアルでは、TableAdapter メソッドでのストアド プロシージャの使用、DAL での接続レベルとコマンド レベルの設定の構成、接続文字列の暗号化など、さまざまな高度なデータ アクセス層シナリオについて説明します。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、ヒルトン ギセナウと S レン ジェイコブ ラウリセンでした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。