バッチ更新を実行する (VB)
すべての項目が編集モードであり、ページで [すべて更新] ボタンをクリックすることでその値を保存することができる、完全な編集が可能な DataList を作成する方法について説明します。
はじめに
前のチュートリアル では、項目レベルの DataList を作成する方法について説明しました。 標準的な編集が可能な GridView と同様に、DataList の各項目には [編集] ボタンが含まれており、クリックすると項目が編集可能になります。 この項目レベルの編集は、たまにしか更新されないデータに適していますが、特定のユース ケースのシナリオでは、ユーザーが多くのレコードを編集する必要があります。 ユーザーが何十ものレコードを編集する必要があり、強制的に [編集] をクリックして変更を加え、そのたびに [更新] をクリックする必要がある場合、クリック数によって生産性が低下する可能性があります。 このような状況では、すべての項目が編集モードであり、ページの [すべて更新] ボタンをクリックして値を編集できる、完全な編集が可能な DataList を提供することをお勧めします。 (図 1 参照)。
図 1: 完全な編集が可能な DataList 内の各項目を変更可能 (クリックするとフルサイズの画像が表示されます)
このチュートリアルでは、ユーザーが完全な編集が可能な DataList を使用してサプライヤーの住所情報を更新できるようにする方法について説明します。
手順 1: DataList の ItemTemplate で編集可能なユーザー インターフェイスを作成する
前のチュートリアルでは、項目レベルの編集可能な標準の DataList を作成するために、次の 2 つのテンプレートを使用しました。
ItemTemplate
には、読み取り専用のユーザー インターフェイス (各製品の名前と価格を表示するための Label Web コントロール) が含まれていました。EditItemTemplate
には、編集モードのユーザー インターフェイス (2 つの TextBox Web コントロール) が含まれていました。
DataList の EditItemIndex
プロパティは、EditItemTemplate
を使用して (存在する場合)、レンダリングされる DataListItem
を指定します。 特に、ItemIndex
の値が DataList の EditItemIndex
プロパティと一致する DataListItem
は、EditItemTemplate
を使用してレンダリングされます。 このモデルは、一度に 1 つの項目しか編集できない場合にはうまく機能しますが、完全な編集が可能な DataList を作成する場合にはうまくいきません。
完全に編集可能な DataList の場合は、編集可能なインターフェイスを使用してレンダリングするために、すべての DataListItem
が必要です。 これを実現する最も簡単な方法は、ItemTemplate
で編集可能なインターフェイスを定義することです。 サプライヤーの住所情報を変更する場合、編集可能なインターフェイスには、テキストとしてサプライヤー名が含まれ、住所、市区町村、国/地域の値のためのテキストボックスが含まれます。
まず BatchUpdate.aspx
ページを開き、DataList コントロールを追加し、その ID
プロパティを Suppliers
に設定します。 DataListのスマートタグから、SuppliersDataSource
という名前の新しい ObjectDataSource コントロールを追加します。
図 2: SuppliersDataSource
という名前の 新しい ObjectDataSource の作成 (クリックするとフルサイズの画像が表示されます)
SuppliersBLL
クラスの GetSuppliers()
メソッドを使用してデータを取得するように ObjectDataSource を構成します (図 3 参照)。 前のチュートリアルと同様に、ObjectDataSource を使用してサプライヤー情報を更新するのではなく、ビジネス ロジック層を直接操作します。 そのため、[更新] タブでドロップダウン リストを (なし) に設定します (図 4 参照)。
図 3: GetSuppliers()
メソッドを使用したサプライヤー情報の取得 (クリックするとフルサイズの画像が表示されます)
図 4: [更新] タブでドロップダウン リストを (なし) に設定 (クリックするとフルサイズの画像が表示されます)
ウィザードが完了すると、Visual Studio によって DataList の ItemTemplate
が自動的に生成され、データ ソースによって返された各データ フィールドが Label Web コントロールに表示されます。 代わりに編集インターフェイスが提供されるように、このテンプレートを変更する必要があります。 ItemTemplate
は、DataList のスマート タグから [テンプレートの編集] オプションを使用するか、宣言構文を使用して直接 Designer でカスタマイズできます。
サプライヤーの名前をテキストとして表示し、サプライヤーの住所、市区町村、国/地域の値の TextBox を含む編集インターフェイスを作成します。 これらの変更を行うと、ページの宣言構文は次のようになります。
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
DataSourceID="SuppliersDataSource">
<ItemTemplate>
<h4><asp:Label ID="CompanyNameLabel" runat="server"
Text='<%# Eval("CompanyName") %>' /></h4>
<table border="0">
<tr>
<td class="SupplierPropertyLabel">Address:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Address" runat="server"
Text='<%# Eval("Address") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">City:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="City" runat="server"
Text='<%# Eval("City") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">Country:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Country" runat="server"
Text='<%# Eval("Country") %>' />
</td>
</tr>
</table>
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
Note
前のチュートリアルと同様に、このチュートリアルの DataList ではビュー状態を有効にする必要があります。
ItemTemplate
では、SupplierPropertyLabel
と SupplierPropertyValue
という 2 つの新しい CSS クラスを使用しています。これらは Styles.css
クラスに追加され、ProductPropertyLabel
および ProductPropertyValue
CSS クラスと同じスタイル設定を使用するように構成されています。
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
これらの変更を行った後、ブラウザー経由でこのページにアクセスします。 図 5 に示すように、各 DataList 項目は、サプライヤー名をテキストとして表示し、TextBox を使用して住所、市区町村、国/地域を表示します。
図 5: 編集可能な DataList の各サプライヤー (クリックするとフルサイズの画像が表示されます)
手順 2: [すべて更新] ボタンの追加
図 5 の各サプライヤーには、住所、市区町村、国/地域の各フィールドがテキスト ボックスに表示されていますが、現時点では [更新] ボタンは使用できません。 完全な編集が可能な DataList では、項目ごとに更新ボタンがあるのではなく、ページ上に [すべて更新] ボタンが 1 つあり、それをクリックすると DataList 内のすべてのレコードが更新されます。 このチュートリアルでは、ページの上部と下部に、2 つの [すべて更新] ボタンを追加してみましょう (ただし、どちらのボタンをクリックしても同じ効果があります)。
まず、Button Web コントロールを DataList の上に追加し、その ID
プロパティを UpdateAll1
に設定します。 次に、2 番目の Button Web コントロールを DataList の下に追加し、その ID
を UpdateAll2
に設定します。 2 つのボタンの Text
プロパティを [すべて更新] に設定します。 最後に、両方の Button Click
イベントのイベント ハンドラーを作成します。 各イベント ハンドラーで更新ロジックを重複させるのではなく、そのロジックを 3 番目のメソッド UpdateAllSupplierAddresses
にリファクタリングして、イベント ハンドラーが単にこの 3 番目のメソッドを呼び出すようにしましょう。
Protected Sub UpdateAll1_Click(sender As Object, e As EventArgs) _
Handles UpdateAll1.Click
UpdateAllSupplierAddresses()
End Sub
Protected Sub UpdateAll2_Click(sender As Object, e As EventArgs) _
Handles UpdateAll2.Click
UpdateAllSupplierAddresses()
End Sub
Private Sub UpdateAllSupplierAddresses()
' TODO: Write code to update _all_ of the supplier addresses in the DataList
End Sub
図 6 は、[すべて更新] ボタンが追加された後のページを示しています。
図 6: ページに追加された 2 つの [すべての更新] ボタン (クリックするとフルサイズの画像が表示されます)
手順 3: すべてのサプライヤーの住所情報の更新
DataList のすべての項目で編集インターフェイスを表示し、[すべて更新] ボタンが追加されたので、あとは一括更新を実行するコードを記述するだけです。 具体的には、DataList の項目をループして SuppliersBLL
クラスの UpdateSupplierAddress
メソッドを呼び出す必要があります。
DataList を構成する DataListItem
インスタンスのコレクションは、DataList のItems
プロパティを介してアクセスできます。 DataListItem
への参照がある場合は、次のコードに示すように、DataKeys
コレクションから対応する SupplierID
を取得し、次のコードの示すように、ItemTemplate
内の TextBox Web コントロールをプログラムで参照できます。
Private Sub UpdateAllSupplierAddresses()
' Create an instance of the SuppliersBLL class
Dim suppliersAPI As New SuppliersBLL()
' Iterate through the DataList's items
For Each item As DataListItem In Suppliers.Items
' Get the supplierID from the DataKeys collection
Dim supplierID As Integer = Convert.ToInt32(Suppliers.DataKeys(item.ItemIndex))
' Read in the user-entered values
Dim address As TextBox = CType(item.FindControl("Address"), TextBox)
Dim city As TextBox = CType(item.FindControl("City"), TextBox)
Dim country As TextBox = CType(item.FindControl("Country"), TextBox)
Dim addressValue As String = Nothing, _
cityValue As String = Nothing, _
countryValue As String = Nothing
If address.Text.Trim().Length > 0 Then
addressValue = address.Text.Trim()
End If
If city.Text.Trim().Length > 0 Then
cityValue = city.Text.Trim()
End If
If country.Text.Trim().Length > 0 Then
countryValue = country.Text.Trim()
End If
' Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress _
(supplierID, addressValue, cityValue, countryValue)
Next
End Sub
ユーザーが [すべて更新] ボタンのいずれかをクリックすると、UpdateAllSupplierAddresses
メソッドは Suppliers
DataList 内の各 DataListItem
ボタンを反復処理し、SuppliersBLL
クラスの UpdateSupplierAddress
メソッドを呼び出して、対応する値を渡します。 住所、市区町村、または国/地域に渡す値が入力されていない値は、(空白文字列ではなく) Nothing
への UpdateSupplierAddress
の値であり、基になるレコードのフィールドのデータベース NULL
になります。
Note
機能強化として、バッチ更新の実行後に何らかの確認メッセージが表示される状態 Label Web コントロールをページに追加する必要がある場合があります。
変更されたアドレスのみの更新
このチュートリアルで使用するバッチ更新アルゴリズムは、住所情報が変更されたかどうかに関係なく、DataList 内のすべてのサプライヤーに対して UpdateSupplierAddress
メソッドを呼び出します。 通常、このようなブラインド更新はパフォーマンスの問題にはなりませんが、データベース テーブルの変更を監査している場合、余計なレコードが増えてしまう可能性があります。 たとえば、トリガーを使用してすべての UPDATE
の Suppliers
テーブルを監査テーブルに記録する場合、ユーザーが [すべて更新] ボタンをクリックするたびに、ユーザーが変更を行ったかどうかに関係なく、システム内の各サプライヤーに対して新しい監査レコードが作成されます。
ADO.NET DataTable クラスと DataAdapter クラスは、変更、削除、新しいレコードのみがデータベース通信を行うバッチ更新をサポートするように設計されています。 DataTable の各行にはRowState
プロパティがあり、その行が DataTable に追加されたのか、DataTable から削除されたのか、変更されたのか、あるいは変更されていないのかを示します。 DataTable が最初に入力される場合、すべての行は変更なしとマークされます。 行のいずれかの列の値を変更すると、その行が変更されたとマークされます。
SuppliersBLL
クラスでは、まず SuppliersDataTable
に 1 つのサプライヤー レコードを読み込んで指定されたサプライヤーの住所情報を更新し、次のコードを使用する Address
、City
、Country
列の値を設定します。
Public Function UpdateSupplierAddress _
(supplierID As Integer, address As String, city As String, country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
' no matching record found, return false
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
' Update the supplier Address-related information
Dim rowsAffected As Integer = Adapter.Update(supplier)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End If
End Function
このコードは、渡された住所、市区町村、国/地域の値を、値が変更されたかどうかに関係なく、SuppliersDataTable
の SuppliersRow
に単純に割り当てます。 これらの変更により、SuppliersRow
の RowState
プロパティは変更済みとしてマークされます。 データ アクセス層の Update
メソッドが呼び出されると、SupplierRow
が変更されたことが確認され、UPDATE
コマンドがデータベースに送信されます。
ただし、渡された住所、市区町村、国/地域の値が SuppliersRow
の既存の値と異なる場合にのみ割り当てるコードをこのメソッドに追加したとします。 住所、市区町村、国/地域が既存のデータと同じである場合、変更は行われず、SupplierRow
の RowState
は変更なしとしてマークされたままになります。 結果として、DAL の Update
メソッドが呼び出されると、SuppliersRow
は変更されていないため、データベース呼び出しは行われません。
この変更を適用するには、渡された住所、市区町村、国/地域の値を無条件で割り当てるステートメントを次のコードに置き換えます。
' Only assign the values to the SupplierRow's column values if they differ
If address Is Nothing AndAlso Not supplier.IsAddressNull() Then
supplier.SetAddressNull()
ElseIf (address IsNot Nothing AndAlso supplier.IsAddressNull) _
OrElse (Not supplier.IsAddressNull() AndAlso _
String.Compare(supplier.Address, address) <> 0) Then
supplier.Address = address
End If
If city Is Nothing AndAlso Not supplier.IsCityNull() Then
supplier.SetCityNull()
ElseIf (city IsNot Nothing AndAlso supplier.IsCityNull) _
OrElse (Not supplier.IsCityNull() AndAlso _
String.Compare(supplier.City, city) <> 0) Then
supplier.City = city
End If
If country Is Nothing AndAlso Not supplier.IsCountryNull() Then
supplier.SetCountryNull()
ElseIf (country IsNot Nothing AndAlso supplier.IsCountryNull) _
OrElse (Not supplier.IsCountryNull() AndAlso _
String.Compare(supplier.Country, country) <> 0) Then
supplier.Country = country
End If
このコードを追加すると、DAL の Update
メソッドは、住所関連の値が変更されたレコードに対してのみ、UPDATE
ステートメントをデータベースに送信します。
または、渡されたアドレス フィールドとデータベース データの間に違いがあるかどうかを追跡し、違いがない場合は、単に DAL の Update
メソッドの呼び出しをバイパスすることもできます。 DB ダイレクト メソッドは、データベース呼び出しが実際に必要かどうかを判断するために RowState
を確認できる SuppliersRow
インスタンスが渡されないため、この方法は、DB ダイレクト メソッドを使用している場合に適しています。
Note
UpdateSupplierAddress
メソッドが呼び出されるたびに、更新されたレコードに関する情報を取得するためにデータベースへの呼び出しが行われます。 その後、データに変更がある場合は、テーブル行を更新するためにデータベースへの別の呼び出しが行われます。 このワークフローは、BatchUpdate.aspx
ページからのすべての変更を含む EmployeesDataTable
インスタンスを受け入れる UpdateSupplierAddress
メソッド オーバーロードを作成することによって最適化できます。 その後、データベースへの呼び出しを 1 回行い、Suppliers
テーブルからすべてのレコードを取得できます。 その後、2 つの結果セットを列挙し、変更が発生したレコードのみを更新できます。
まとめ
このチュートリアルでは、完全な編集が可能な DataList を作成し、ユーザーが複数のサプライヤーの住所情報をすばやく変更できるようにする方法について説明しました。 まず、DataList の ItemTemplate
にあるサプライヤーの住所、市区町村、国/地域の値の TextBox Web コントロールの編集インターフェイスで定義しました。 次に、DataList の上下に [すべて更新] ボタンを追加しました。 ユーザーが変更を行い、[すべて更新] ボタンのいずれかをクリックすると、DataListItem
が列挙され、SuppliersBLL
クラスの UpdateSupplierAddress
メソッドの呼び出しが行われます。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.comの創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の著書は Sams Teach Yourself ASP.NET 2.0 in 24 Hoursです。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Zack Jones と Ken Pespisa でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 mitchell@4GuysFromRolla.comに行をドロップしてください。