スコット・ミッチェルによる
1 回の操作で複数のデータベース レコードを挿入する方法について説明します。 ユーザー インターフェイス レイヤーでは、GridView を拡張して、ユーザーが複数の新しいレコードを入力できるようにします。 データ アクセス層では、トランザクション内で複数の挿入操作をラップして、すべての挿入が成功するか、すべての挿入がロールバックされるようにします。
イントロダクション
バッチ更新のチュートリアルでは、GridView コントロールをカスタマイズして、複数のレコードが編集可能なインターフェイスを表示する方法について説明しました。 ページにアクセスするユーザーは一連の変更を行い、1 回のボタン クリックでバッチ更新を実行できます。 ユーザーが一度に多くのレコードを更新するのが一般的な場合、このようなインターフェイスは、 データの挿入、更新、削除の概要 チュートリアルで最初に確認した既定の行ごとの編集機能と比較して、数え切れないほどのクリックとキーボードからマウスへのコンテキスト スイッチを保存できます。
この概念は、レコードを追加するときにも適用できます。 ここで、Northwind Traders では、特定のカテゴリの製品を多数含むサプライヤーから出荷を受け取ることがよくあります。 たとえば、東京トレーダーズから6種類の紅茶とコーヒー製品が出荷される場合があります。 ユーザーが DetailsView コントロールを使用して 6 つの製品を一度に 1 つずつ入力した場合、同じ値の多くを何度も選択する必要があります。同じカテゴリ (飲料)、同じサプライヤー (東京トレーダー)、同じ廃止値 (False)、注文値 (0) の同じ単位を選択する必要があります。 この繰り返しのデータ入力には時間がかかるだけでなく、エラーが発生しやすくなります。
少しの作業で、ユーザーがサプライヤーとカテゴリを一度選択し、一連の製品名と単価を入力し、ボタンをクリックして新しい製品をデータベースに追加できるようにするバッチ挿入インターフェイスを作成できます (図 1 を参照)。 各製品が追加されると、その ProductName
と UnitPrice
のデータ フィールドにはテキスト ボックスに入力された値が割り当てられます。一方、 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] タブのドロップダウン リストを [なし] に設定します。 [完了] をクリックして、データ ソースの構成ウィザードを完了します。
図 3: ProductsBLL
クラスの GetProducts
メソッドから返されたデータを表示します (フルサイズの画像を表示する をクリックします)。
図 4: [UPDATE]、[INSERT]、[DELETE] タブの [Drop-Down リスト] を [なし] に設定する (フルサイズの画像を表示する 場合はクリックします)
ObjectDataSource ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が追加されます。
ProductName
、CategoryName
、SupplierName
、UnitPrice
、Discontinued
の各フィールドを除くすべてのフィールドを削除します。 美的なカスタマイズを自由に行うことができます。
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 の上に [製品出荷の処理] ボタンが表示されます。
図 5: GridView は、製品の一覧と並べ替えとページング機能を提供します (フルサイズの画像を表示する をクリックします)。
手順 2: 挿入インターフェイスの作成
表示インターフェイスが完成したら、挿入インターフェイスを作成する準備ができました。 このチュートリアルでは、1 つのサプライヤーとカテゴリの値を求める挿入インターフェイスを作成し、ユーザーが最大 5 つの製品名と単価値を入力できるようにします。 このインターフェイスを使用すると、ユーザーは、すべて同じカテゴリとサプライヤーを共有するが、固有の製品名と価格を持つ 1 から 5 つの新製品を追加できます。
まず、ツールボックスからデザイナーにパネルをドラッグし、既存の DisplayInterface
パネルの下に配置します。 この新しく追加された Panel の ID
プロパティを InsertingInterface
に設定し、その Visible
プロパティを False
に設定します。 手順 3 で InsertingInterface
Panel の Visible
プロパティを True
に設定するコードを追加します。 また、パネルの Height
と Width
プロパティの値をクリアします。
次に、図 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 コントロールを含む行の BatchInsertRow
と BatchInsertAlternatingRow
の値を交互に設定します。 私は、これらのチュートリアルで使用した 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 行のテーブルとして表示されます。
図 6: 挿入インターフェイスは、4 列の Seven-Row テーブルで構成されています (フルサイズの画像を表示する をクリックします)。
これで、挿入インターフェイスに Web コントロールを追加する準備ができました。 ツールボックスから 2 つの DropDownList を、仕入先用とカテゴリ用のテーブルの適切なセルにドラッグします。
仕入先 DropDownList の ID
プロパティを Suppliers
に設定し、 SuppliersDataSource
という名前の新しい ObjectDataSource にバインドします。
SuppliersBLL
クラスの GetSuppliers
メソッドからデータを取得するように新しい ObjectDataSource を構成し、UPDATE タブのドロップダウン リストを (None) に設定します。 [完了] をクリックして、ウィザードを完了します。
図 7: SuppliersBLL
クラスの GetSuppliers
メソッドを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)。
Suppliers
DropDownList に CompanyName
データ フィールドを表示し、SupplierID
データ フィールドをListItem
の値として使用します。
図 8: CompanyName
データ フィールドを表示し、値として SupplierID
を使用する (フルサイズの画像を表示する] をクリックします)。
2 番目の DropDownList Categories
に名前を付け、 CategoriesDataSource
という名前の新しい ObjectDataSource にバインドします。
CategoriesDataSource
クラスの CategoriesBLL
メソッドを使用するように GetCategories
ObjectDataSource を構成します。[UPDATE] タブと [DELETE] タブのドロップダウン リストを [なし] に設定し、[完了] をクリックしてウィザードを完了します。 最後に、DropDownList に CategoryName
データ フィールドを表示し、値として CategoryID
を使用します。
これら 2 つの DropDownList が追加され、適切に構成された ObjectDataSource にバインドされると、画面は図 9 のようになります。
図 9: ヘッダー行に Suppliers
と Categories
DropDownLists が含まれるようになりました (フルサイズの画像を表示する をクリックします)。
ここでは、新しい各製品の名前と価格を収集する TextBoxes を作成する必要があります。 ツールボックスから 5 つの製品名と価格行のそれぞれについて、TextBox コントロールをデザイナーにドラッグします。 TextBoxes の ID
プロパティを、 ProductName1
、 UnitPrice1
、 ProductName2
、 UnitPrice2
、 ProductName3
、 UnitPrice3
などを設定します。
各単価テキスト ボックスの後に CompareValidator を追加し、 ControlToValidate
プロパティを適切な ID
に設定します。 また、 Operator
プロパティを GreaterThanEqual
、 ValueToCompare
0、 Type
を Currency
に設定します。 これらの設定は、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 デザイナーを示しています。
図 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
イベント ハンドラーに戻り、製品を保存するコードを追加します。
ReturnToDisplayInterface
は Suppliers
および Categories
のドロップダウンリストを最初のオプションに戻します。 2 つの定数firstControlID
およびlastControlID
は、挿入インターフェイスで製品名と単価の TextBoxes の名前付けに使用される開始および終了コントロールのインデックス値をマークし、TextBox コントロールのFor
プロパティを空の文字列に戻すText
ループの境界で使用されます。 最後に、挿入インターフェイスが非表示になり、表示インターフェイスが表示されるように、パネル Visible
プロパティがリセットされます。
ブラウザーでこのページをテストします。 最初にページにアクセスすると、図 5 に示すように表示インターフェイスが表示されます。 [製品出荷の処理] ボタンをクリックします。 ページがポストバックされ、図 12 に示すように挿入インターフェイスが表示されます。 [出荷から製品を追加] または [キャンセル] ボタンをクリックすると、表示インターフェイスに戻ります。
注
挿入インターフェイスを表示しているときに、単価テキストボックスで CompareValidator を試してみてください。 無効な通貨値または値が 0 未満の価格の [出荷から製品を追加] ボタンをクリックすると、クライアント側のメッセージ ボックスの警告が表示されます。
図 12: プロセス製品出荷ボタンをクリックした後、挿入インターフェイスが表示されます (フルサイズの画像を表示する をクリックします)。
手順 4: 製品を追加する
このチュートリアルで残っているのは、出荷ボタンの [製品の追加] イベント ハンドラーでデータベースに製品 Click
保存することです。 これを実現するには、 ProductsDataTable
を作成し、指定された製品名ごとに ProductsRow
インスタンスを追加します。 これらのProductsRow
が追加されたら、ProductsBLL
を渡すUpdateWithTransaction
クラスのProductsDataTable
メソッドを呼び出します。
UpdateWithTransaction
チュートリアルで作成されたメソッドがProductsDataTable
のProductsTableAdapter
メソッドに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 が無効なデータを報告していることを意味します。このような場合は、入力した製品を挿入しようとしません。ユーザーが入力した単価値をProductsRow
のUnitPrice
プロパティに割り当てようとすると、例外が発生します。
次に、新しい ProductsDataTable
インスタンスが作成されます (products
)。
For
ループは、製品名と単価の TextBox を反復処理するために使用され、Text
プロパティはローカル変数productName
およびunitPrice
に読み込まれます。 ユーザーが単価の値を入力したが、対応する製品名の値を入力していない場合は、 StatusLabel
にメッセージが表示されます。単価を指定する場合は、製品の名前も含める必要があり、イベント ハンドラーが終了します。
製品名が指定されている場合は、ProductsRow
のProductsDataTable
メソッドを使用して新しいNewProductsRow
インスタンスが作成されます。 この新しい ProductsRow
インスタンスの ProductName
プロパティは現在の製品名 TextBox に設定され、 SupplierID
プロパティと CategoryID
プロパティは挿入インターフェイスのヘッダーの DropDownLists の SelectedValue
プロパティに割り当てられます。 ユーザーが製品の価格の値を入力した場合、ProductsRow
インスタンスの UnitPrice
プロパティに割り当てられます。それ以外の場合、プロパティは未割り当てのままにされ、データベース内のNULL
のUnitPrice
値になります。 最後に、 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: 単価を入力するときに製品名が必要です (フルサイズの画像を表示する をクリックします)。
図 14: 3 つの新しい野菜が追加された仕入先真由美 s (フルサイズの画像を表示する をクリックします)。
図 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にメッセージを送ってください。