トランザクション内のデータベース変更をラップする (VB)
このチュートリアルは、データのバッチの更新、削除、挿入を行う 4 つのチュートリアルの最初のチュートリアルです。 このチュートリアルでは、データベース トランザクションでバッチ変更をアトミック操作として実行する方法について説明します。これにより、すべてのステップが成功するか、すべてのステップが失敗するかを保証します。
はじめに
「 データの挿入、更新、および削除の概要 」チュートリアルから見たように、GridView では行レベルの編集と削除が組み込まれています。 マウスを数回クリックするだけで、行ごとに編集と削除を行うコンテンツであれば、コード行を記述せずに豊富なデータ変更インターフェイスを作成できます。 ただし、特定のシナリオでは、これは不十分であり、レコードのバッチを編集または削除する機能をユーザーに提供する必要があります。
たとえば、ほとんどの Web ベースのメール クライアントでは、グリッドを使用して各メッセージを一覧表示します。各行には、電子メールの情報 (件名、送信者など) と共にチェックボックスが含まれています。 このインターフェイスを使用すると、ユーザーは複数のメッセージを確認し、[選択したメッセージの削除] ボタンをクリックして複数のメッセージを削除できます。 バッチ編集インターフェイスは、ユーザーが多くの異なるレコードを一般的に編集する場合に最適です。 ユーザーが [編集] をクリックして変更を加え、変更する必要があるレコードごとに [更新] をクリックするのではなく、バッチ編集インターフェイスによって、各行が編集インターフェイスでレンダリングされます。 ユーザーは、変更する必要がある行のセットをすばやく変更し、[すべて更新] ボタンをクリックしてこれらの変更を保存できます。 この一連のチュートリアルでは、データのバッチを挿入、編集、削除するためのインターフェイスを作成する方法について説明します。
バッチ操作を実行するときは、バッチ内の一部の操作が成功し、他の操作が失敗する可能性があるかどうかを判断することが重要です。 インターフェイスを削除するバッチを検討する - 最初に選択したレコードが正常に削除されても、外部キー制約違反のために 2 つ目のレコードが失敗した場合はどうなりますか? 最初のレコードの削除をロールバックするか、最初のレコードを削除したままにしてもかまいませんか?
バッチ操作を アトミック操作 (すべてのステップが成功するか、すべてのステップが失敗する) として扱う場合は、 データベース トランザクションのサポートを含むようにデータ アクセス層を拡張する必要があります。 データベース トランザクションは、トランザクションの傘下で実行される 、UPDATE
、および DELETE
ステートメントのINSERT
セットの原子性を保証し、最新のデータベース システムのほとんどでサポートされている機能です。
このチュートリアルでは、DAL を拡張してデータベース トランザクションを使用する方法について説明します。 以降のチュートリアルでは、インターフェイスのバッチ挿入、更新、および削除のための Web ページの実装について説明します。 始めましょう。
注意
バッチ トランザクション内のデータを変更する場合、アトミック性は常に必要であるとは限りません。 一部のシナリオでは、Web ベースの電子メール クライアントから一連のメールを削除する場合など、一部のデータ変更が成功し、同じバッチ内の他の変更が失敗する場合があります。 削除プロセスの途中でデータベース エラーが発生した場合、エラーなしで処理されたレコードが削除されたままになる可能性があります。 このような場合、データベース トランザクションをサポートするために DAL を変更する必要はありません。 ただし、原子性が不可欠なバッチ操作シナリオは他にもあります。 顧客が 1 つの銀行口座から別の銀行口座に資金を移動する場合は、2 つの操作を実行する必要があります。1 つ目の口座から資金を差し引いてから 2 番目の口座に追加する必要があります。 銀行は最初のステップが成功しても 2 番目のステップが失敗しても気にしないかもしれませんが、顧客は明らかに混乱します。 このチュートリアルに取り組み、次の 3 つのチュートリアルで構築するインターフェイスのバッチ挿入、更新、および削除でデータベース トランザクションを使用する予定がない場合でも、DAL の機能強化を実装してデータベース トランザクションをサポートすることをお勧めします。
トランザクションの概要
ほとんどのデータベースには トランザクションのサポートが含まれており、複数のデータベース コマンドを 1 つの論理作業単位にグループ化できます。 トランザクションを構成するデータベース コマンドはアトミックであることが保証されます。つまり、すべてのコマンドが失敗するか、すべて成功します。
一般に、トランザクションは次のパターンを使用して SQL ステートメントを使用して実装されます。
- トランザクションの開始を示します。
- トランザクションを構成する SQL ステートメントを実行します。
- 手順 2 のステートメントのいずれかでエラーが発生した場合は、トランザクションをロールバックします。
- 手順 2 のすべてのステートメントがエラーなしで完了した場合は、トランザクションをコミットします。
トランザクションの作成、コミット、ロールバックに使用される SQL ステートメントは、SQL スクリプトの記述またはストアド プロシージャの作成時に手動で入力することも、ADO.NET または名前空間内のクラスを使用するプログラムによる方法をSystem.Transactions
使用して入力することもできます。 このチュートリアルでは、ADO.NET を使用したトランザクションの管理のみを確認します。 今後のチュートリアルでは、データ アクセス層でストアド プロシージャを使用する方法について説明します。この時点で、トランザクションを作成、ロールバック、コミットするための SQL ステートメントについて説明します。 それまでの間は、ストアド プロシージャのトランザクションの管理SQL Server参照してください。
注意
名前空間の System.Transactions
クラスを使用すると、開発者はTransactionScope
トランザクションのスコープ内で一連のステートメントをプログラムでラップでき、2 つの異なるデータベースや、Microsoft SQL Server データベース、Oracle データベース、Web サービスなどの異種データ ストアなど、複数のソースを含む複雑なトランザクションのサポートが含まれます。 ADO.NET はデータベーストランザクションに対してより具体的であり、多くの場合、リソースの負荷がはるかに少ないため、このチュートリアルではクラスの代わりに TransactionScope
ADO.NET トランザクションを使用することにしました。 さらに、特定のシナリオでは、クラスは TransactionScope
Microsoft 分散トランザクション コーディネーター (MSDTC) を使用します。 MSDTC を取り巻く構成、実装、およびパフォーマンスの問題は、これらのチュートリアルの範囲を超えて、かなり特殊で高度なトピックになります。
ADO.NET で SqlClient プロバイダーを操作する場合、トランザクションは、 オブジェクトを返す SqlTransaction
クラスの BeginTransaction
メソッドのSqlConnection
呼び出しによって開始されます。 トランザクションを構成するデータ変更ステートメントは、ブロック内に try...catch
配置されます。 ブロック内の ステートメントでtry
エラーが発生した場合、実行は、オブジェクトの Rollback
メソッドを使用してトランザクションをロールバックできる ブロックにSqlTransaction
転送catch
されます。 すべてのステートメントが正常に完了すると、ブロックの末尾try
にある オブジェクトの Commit
メソッドを呼び出SqlTransaction
すと、トランザクションがコミットされます。 次のコード スニペットは、このパターンを示しています。
' Create the SqlTransaction object
Dim myTransaction As SqlTransaction = SqlConnectionObject.BeginTransaction();
Try
'
' ... Perform the database transaction�s data modification statements...
'
' If we reach here, no errors, so commit the transaction
myTransaction.Commit()
Catch
' If we reach here, there was an error, so rollback the transaction
myTransaction.Rollback()
Throw
End Try
既定では、型指定された DataSet の TableAdapters はトランザクションを使用しません。 トランザクションのサポートを提供するには、TableAdapter クラスを拡張して、上記のパターンを使用してトランザクションのスコープ内で一連のデータ変更ステートメントを実行する追加のメソッドを含める必要があります。 手順 2 では、部分クラスを使用してこれらのメソッドを追加する方法について説明します。
手順 1: バッチ データ Web ページの操作の作成
データベース トランザクションをサポートするように DAL を拡張する方法を調べ始める前に、まず、このチュートリアルに必要な ASP.NET Web ページと、次の 3 つのページを作成してみましょう。 まず、 という名前 BatchData
の新しいフォルダーを追加し、次の ASP.NET ページを追加して、各ページをマスター ページに Site.master
関連付けます。
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
図 1: SqlDataSource-Related チュートリアルの ASP.NET ページを追加する
他のフォルダーと同様に、 Default.aspx
ユーザー コントロールを SectionLevelTutorialListing.ascx
使用して、セクション内のチュートリアルを一覧表示します。 したがって、このユーザー コントロールを にDefault.aspx
追加するには、ソリューション エクスプローラーからページのデザイン ビューにドラッグします。
図 2: ユーザー コントロールを SectionLevelTutorialListing.ascx
に追加する Default.aspx
(クリックするとフルサイズの画像が表示されます)
最後に、これら 4 つのページをエントリとしてファイルに Web.sitemap
追加します。 具体的には、サイト マップ <siteMapNode>
のカスタマイズの後に次のマークアップを追加します。
<siteMapNode title="Working with Batched Data"
url="~/BatchData/Default.aspx"
description="Learn how to perform batch operations as opposed to
per-row operations.">
<siteMapNode title="Adding Support for Transactions"
url="~/BatchData/Transactions.aspx"
description="See how to extend the Data Access Layer to support
database transactions." />
<siteMapNode title="Batch Updating"
url="~/BatchData/BatchUpdate.aspx"
description="Build a batch updating interface, where each row in a
GridView is editable." />
<siteMapNode title="Batch Deleting"
url="~/BatchData/BatchDelete.aspx"
description="Explore how to create an interface for batch deleting
by adding a CheckBox to each GridView row." />
<siteMapNode title="Batch Inserting"
url="~/BatchData/BatchInsert.aspx"
description="Examine the steps needed to create a batch inserting
interface, where multiple records can be created at the
click of a button." />
</siteMapNode>
を更新した Web.sitemap
後、ブラウザーを使用してチュートリアル Web サイトを表示します。 左側のメニューに、バッチ データの操作に関するチュートリアルの項目が含まれるようになりました。
図 3: サイト マップにバッチ データの操作に関するチュートリアルのエントリが含まれるようになりました
手順 2: データベース トランザクションをサポートするようにデータ アクセス層を更新する
最初のチュートリアル「 データ アクセス層の作成」で説明したように、DAL の型指定された DataSet は DataTables と TableAdapters で構成されています。 DataTables はデータを保持しますが、TableAdapters はデータベースから DataTables にデータを読み取り、DataTables に加えられた変更でデータベースを更新する機能を提供します。 TableAdapters には、データを更新するための 2 つのパターン (Batch Update と DB-Direct と呼ばれる) が用意されていることを思い出してください。 Batch Update パターンでは、TableAdapter に DataRows の DataSet、DataTable、またはコレクションが渡されます。 このデータは列挙され、挿入、変更、または削除された行ごとに、 InsertCommand
、 UpdateCommand
、または DeleteCommand
が実行されます。 DB-Direct パターンでは、TableAdapter には、1 つのレコードの挿入、更新、または削除に必要な列の値が渡されます。 その後、DB Direct パターン メソッドは、渡された値を使用して、適切な InsertCommand
、 UpdateCommand
、または DeleteCommand
ステートメントを実行します。
使用される更新パターンに関係なく、TableAdapters 自動生成メソッドはトランザクションを使用しません。 既定では、TableAdapter によって実行される各挿入、更新、または削除は、1 つの個別の操作として扱われます。 たとえば、BLL の一部のコードがデータベースに 10 個のレコードを挿入するために、DB-Direct パターンが使用されるとします。 このコードでは、TableAdapter のメソッドを Insert
10 回呼び出します。 最初の 5 回の挿入が成功したが、6 番目の挿入で例外が発生した場合、挿入された最初の 5 つのレコードはデータベースに残ります。 同様に、Batch Update パターンを使用して DataTable 内の挿入、変更、削除された行に対する挿入、更新、および削除を実行する場合、最初のいくつかの変更が成功したが、後でエラーが発生した場合、完了した以前の変更はデータベースに残ります。
特定のシナリオでは、一連の変更にわたって原子性を確保したいと考えています。 これを実現するには、トランザクションの傘の下に 、UpdateCommand
DeleteCommand
、 を実行する新しいメソッドをInsertCommand
追加して、TableAdapter を手動で拡張する必要があります。 「データ アクセス層の作成」では、部分クラスを使用して、型指定された DataSet 内の DataTable の機能を拡張する方法について説明しました。 この手法は、TableAdapters でも使用できます。
型指定された DataSet Northwind.xsd
は、フォルダーのDAL
サブフォルダーにありますApp_Code
。 という名前のフォルダーにサブフォルダーを DAL
作成し、 という TransactionSupport
名前 ProductsTableAdapter.TransactionSupport.vb
の新しいクラス ファイルを追加します (図 4 を参照)。 このファイルには、 の部分的な実装 ProductsTableAdapter
が保持されます。これには、トランザクションを使用してデータ変更を実行するためのメソッドが含まれます。
図 4: という名前のフォルダーとという名前 TransactionSupport
のクラス ファイルを追加する ProductsTableAdapter.TransactionSupport.vb
ファイルに次のコードを ProductsTableAdapter.TransactionSupport.vb
入力します。
Imports System.Data
Imports System.Data.SqlClient
Namespace NorthwindTableAdapters
Partial Public Class ProductsTableAdapter
Private _transaction As SqlTransaction
Private Property Transaction() As SqlTransaction
Get
Return Me._transaction
End Get
Set(ByVal Value As SqlTransaction)
Me._transaction = Value
End Set
End Property
Public Sub BeginTransaction()
' Open the connection, if needed
If Me.Connection.State <> ConnectionState.Open Then
Me.Connection.Open()
End If
' Create the transaction and assign it to the Transaction property
Me.Transaction = Me.Connection.BeginTransaction()
' Attach the transaction to the Adapters
For Each command As SqlCommand In Me.CommandCollection
command.Transaction = Me.Transaction
Next
Me.Adapter.InsertCommand.Transaction = Me.Transaction
Me.Adapter.UpdateCommand.Transaction = Me.Transaction
Me.Adapter.DeleteCommand.Transaction = Me.Transaction
End Sub
Public Sub CommitTransaction()
' Commit the transaction
Me.Transaction.Commit()
' Close the connection
Me.Connection.Close()
End Sub
Public Sub RollbackTransaction()
' Rollback the transaction
Me.Transaction.Rollback()
' Close the connection
Me.Connection.Close()
End Sub
End Class
End Namespace
ここでのクラス宣言のキーワード (keyword)はPartial
、 内に追加されたメンバーが名前空間のクラスにProductsTableAdapter
追加されることをコンパイラにNorthwindTableAdapters
示します。 ファイルの Imports System.Data.SqlClient
先頭にある ステートメントに注意してください。 TableAdapter は SqlClient プロバイダーを使用するように構成されているため、内部的には オブジェクトを SqlDataAdapter
使用してそのコマンドをデータベースに発行します。 したがって、 クラスを SqlTransaction
使用してトランザクションを開始し、コミットまたはロールバックする必要があります。 Microsoft SQL Server以外のデータ ストアを使用している場合は、適切なプロバイダーを使用する必要があります。
これらのメソッドは、トランザクションの開始、ロールバック、コミットに必要な構成要素を提供します。 これらは、 内から、DAL 内の別のProductsTableAdapter
クラス、またはアーキテクチャ内の別のレイヤー (BLL など) から使用できるようにマークPublic
されています。 BeginTransaction
TableAdapter の内部 SqlConnection
を開き (必要な場合)、トランザクションを開始して プロパティに Transaction
割り当て、トランザクションを内部 SqlDataAdapter
オブジェクト SqlCommand
にアタッチします。 CommitTransaction
および RollbackTransaction
は、内部Connection
オブジェクトをTransaction
閉じる前に、それぞれ オブジェクト s Commit
と Rollback
メソッドを呼び出します。
手順 3: トランザクションの傘の下にデータを更新および削除するメソッドを追加する
これらのメソッドが完了したら、トランザクションの傘の下で一連のコマンドを ProductsDataTable
実行するメソッドを または BLL に追加する準備が整いました。 次のメソッドでは、Batch Update パターンを使用して、トランザクションを ProductsDataTable
使用してインスタンスを更新します。 メソッドを呼び出してトランザクションを BeginTransaction
開始し、 ブロックを Try...Catch
使用してデータ変更ステートメントを発行します。 オブジェクトの Update
メソッドのAdapter
呼び出しによって例外が発生した場合、トランザクションがロールバックされ、例外が再スローされるブロックに実行が転送catch
されます。 メソッドは、 Update
指定された ProductsDataTable
の行を列挙し、必要な InsertCommand
、 UpdateCommand
、および DeleteCommand
を実行することで、Batch Update パターンを実装することを思い出してください。 これらのコマンドのいずれかがエラーになった場合、トランザクションはロールバックされ、トランザクションの有効期間中に行われた以前の変更が元に戻されます。 ステートメントが Update
エラーなしで完了した場合、トランザクションは完全にコミットされます。
Public Function UpdateWithTransaction _
(ByVal dataTable As Northwind.ProductsDataTable) As Integer
Me.BeginTransaction()
Try
' Perform the update on the DataTable
Dim returnValue As Integer = Me.Adapter.Update(dataTable)
' If we reach here, no errors, so commit the transaction
Me.CommitTransaction()
Return returnValue
Catch
' If we reach here, there was an error, so rollback the transaction
Me.RollbackTransaction()
Throw
End Try
End Function
の UpdateWithTransaction
部分クラスを ProductsTableAdapter
使用して、 メソッドを クラスに ProductsTableAdapter.TransactionSupport.vb
追加します。 または、このメソッドをビジネス ロジック レイヤーの ProductsBLL
クラスに追加し、構文を若干変更することもできます。 つまり、 のMe.BeginTransaction()
Me.CommitTransaction()
Me.RollbackTransaction()
キーワード (keyword)Me
を に置Adapter
き換える必要があります (型のプロパティProductsTableAdapter
ProductsBLL
の名前をAdapter
呼び出してください)。
メソッドは UpdateWithTransaction
Batch Update パターンを使用しますが、次のメソッドに示すように、一連の DB-Direct 呼び出しをトランザクションのスコープ内で使用することもできます。 メソッドはDeleteProductsWithTransaction
、 型Integer
の 入力List(Of T)
として を受け入れます。これはProductID
、削除する s です。 メソッドは へのBeginTransaction
呼び出しを介してトランザクションを開始し、 ブロック内でTry
、各ProductID
値の DB-Direct パターン Delete
メソッドを呼び出して、指定されたリストを反復処理します。 への呼び出し Delete
のいずれかが失敗した場合、制御は、トランザクションがロールバックされ、例外が再スローされるブロックに転送 Catch
されます。 すべての呼び出しが Delete
成功すると、トランザクションがコミットされます。 このメソッドを クラスに ProductsBLL
追加します。
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
複数の TableAdapters にトランザクションを適用する
このチュートリアルで調べたトランザクション関連のコードを使用すると、 に対する ProductsTableAdapter
複数のステートメントをアトミック操作として扱えます。 しかし、異なるデータベース テーブルに対する複数の変更をアトミックに実行する必要がある場合はどうでしょうか。 たとえば、カテゴリを削除するときに、最初に現在の製品を他のカテゴリに再割り当てすることをお勧めします。 製品の再割り当てとカテゴリの削除の 2 つの手順は、アトミック操作として実行する必要があります。 ただし、 ProductsTableAdapter
にはテーブルを変更するための Products
メソッドのみが含まれており、 にはテーブルを CategoriesTableAdapter
変更 Categories
するためのメソッドのみが含まれています。 では、トランザクションで TableAdapters の両方を囲む方法を説明します。
1 つのオプションは、名前付きの DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
にメソッドをCategoriesTableAdapter
追加し、そのメソッドがストアド プロシージャを呼び出し、両方が製品を再割り当てし、ストアド プロシージャ内で定義されているトランザクションのスコープ内でカテゴリを削除することです。 今後のチュートリアルでは、ストアド プロシージャでトランザクションを開始、コミット、ロールバックする方法について説明します。
もう 1 つのオプションは、 メソッドを含むヘルパー クラスを DAL に DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
作成することです。 このメソッドは、 と ProductsTableAdapter
のCategoriesTableAdapter
インスタンスを作成し、これら 2 つの TableAdapters Connection
プロパティを同じSqlConnection
インスタンスに設定します。 その時点で、2 つの TableAdapters のいずれかが への BeginTransaction
呼び出しでトランザクションを開始します。 製品を再割り当てしてカテゴリを削除するための TableAdapters メソッドは、必要に応じてトランザクションがコミットまたはロールバックされたブロックで Try...Catch
呼び出されます。
手順 4: ビジネス ロジック レイヤーにメソッドを追加UpdateWithTransaction
する
手順 3 では、 UpdateWithTransaction
DAL の に ProductsTableAdapter
メソッドを追加しました。 対応するメソッドを BLL に追加する必要があります。 プレゼンテーション レイヤーは DAL を直接呼び出して メソッドを呼び出 UpdateWithTransaction
すことができますが、これらのチュートリアルでは、DAL をプレゼンテーション レイヤーから分離する階層構造のアーキテクチャを定義するように努めています。 したがって、このアプローチを継続する必要があります。
クラス ファイルを ProductsBLL
開き、対応する DAL メソッドを呼び出す という名前 UpdateWithTransaction
のメソッドを追加します。 には、先ほど追加した と、手順 3 でProductsBLL
UpdateWithTransaction
追加DeleteProductsWithTransaction
した の 2 つの新しいメソッドがあります。
Public Function UpdateWithTransaction _
(ByVal products As Northwind.ProductsDataTable) As Integer
Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
注意
これらのメソッドには、 クラス内ProductsBLL
のDataObjectMethodAttribute
他のほとんどのメソッドに割り当てられた属性は含まれません。これらのメソッドは、ASP.NET ページ分離コード クラスから直接呼び出されるためです。 ObjectDataSource DataObjectMethodAttribute
の [データ ソースの構成] ウィザードと、どのタブ (SELECT、UPDATE、INSERT、または DELETE) に表示されるメソッドにフラグを設定するために使用されることを思い出してください。 GridView にはバッチ編集または削除の組み込みサポートがないため、コード不要の宣言型アプローチを使用するのではなく、プログラムでこれらのメソッドを呼び出す必要があります。
手順 5: プレゼンテーション 層からデータベース データをアトミックに更新する
レコードのバッチを更新するときにトランザクションに及ぼす影響を示すために、GridView 内のすべての製品を一覧表示するユーザー インターフェイスを作成し、クリックすると製品 CategoryID
の値を再割り当てするボタン Web コントロールを含めます。 特に、カテゴリの再割り当ては進行するため、最初のいくつかの製品に有効な CategoryID
値が割り当てられ、他の製品には意図的に存在 CategoryID
しない値が割り当てられます。 既存のカテゴリCategoryID
に一致しない 製品CategoryID
でデータベースを更新しようとすると、外部キー制約違反が発生し、例外が発生します。 この例でわかるように、トランザクションを使用すると、外部キー制約違反から発生した例外によって、以前の有効な CategoryID
変更がロールバックされるということです。 ただし、トランザクションを使用しない場合、最初のカテゴリに対する変更は残ります。
まず、フォルダー内のページをTransactions.aspx
BatchData
開き、GridView をツールボックスからDesignerにドラッグします。 を ID
に Products
設定し、スマート タグから という名前 ProductsDataSource
の新しい ObjectDataSource にバインドします。 ObjectDataSource を構成して、クラスの GetProducts
メソッドからデータをProductsBLL
プルします。 これは読み取り専用の GridView になるため、[UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを [(なし)] に設定し、[完了] をクリックします。
図 5: クラスの GetProducts
メソッドを使用ProductsBLL
するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)
図 6: UPDATE、INSERT、および DELETE タブの Drop-Down Lists を (なし) に設定します (フルサイズの画像を表示する 場合は、ここをクリックします)
データ ソースの構成ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が作成されます。 、ProductName
および を除くこれらのフィールドをすべて削除し、 プロパティと CategoryName
BoundFields HeaderText
プロパティの名前をそれぞれ Product プロパティと Category プロパティに変更ProductName
します。ProductID
CategoryName
CategoryID
スマート タグから、[ページングを有効にする] オプションをチェックします。 これらの変更を行った後、GridView と ObjectDataSource の宣言型マークアップは次のようになります。
<asp:GridView ID="Products" runat="server" AllowPaging="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
SortExpression="CategoryName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
次に、GridView の上に 3 つのボタン Web コントロールを追加します。 最初のボタンの Text プロパティを [グリッドの更新] に設定し、2 番目のプロパティを [カテゴリの変更 (WITH TRANSACTION)] に設定し、3 番目のボタンを [カテゴリの変更 (トランザクションなし)] に設定します。
<p>
<asp:Button ID="RefreshGrid" runat="server" Text="Refresh Grid" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithTransaction" runat="server"
Text="Modify Categories (WITH TRANSACTION)" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithoutTransaction" runat="server"
Text="Modify Categories (WITHOUT TRANSACTION)" />
</p>
この時点で、Visual Studio のデザイン ビューは図 7 に示すスクリーン ショットのようになります。
図 7: ページには GridView と 3 つのボタン Web コントロールが含まれています (フルサイズの画像を表示する 場合はクリックします)
3 つの Button Click
の各イベントのイベント ハンドラーを作成し、次のコードを使用します。
Protected Sub RefreshGrid_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RefreshGrid.Click
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data using a transaction
productsAPI.UpdateWithTransaction(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithoutTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithoutTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data WITHOUT using a transaction
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Update(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
refresh Button のイベント ハンドラーは、GridView の Click
メソッドを呼び出すことによって、データを GridView DataBind
にProducts
再バインドするだけです。
2 番目のイベント ハンドラーは、製品 CategoryID
を再割り当てし、BLL の新しいトランザクション メソッドを使用して、トランザクションの傘の下でデータベースの更新を実行します。 各製品 s CategoryID
は、その と同じ値に任意に設定されることに注意してください ProductID
。 これらの製品には有効な CategoryID
にマップされる値があるため、これは最初のいくつかの製品ProductID
で正常に動作します。 しかし、sがProductID
大きくなり始めると、sとCategoryID
sのこの偶然のProductID
重複は適用されなくなりました。
3 番目Click
のイベント ハンドラーは、同じ方法で製品 CategoryID
を更新しますが、既定Update
のメソッドを使用してデータベースに更新プログラムをProductsTableAdapter
送信します。 このメソッドは Update
トランザクション内で一連のコマンドをラップしないため、最初に発生した外部キー制約違反エラーの前に変更が行われると、保持されます。
この動作を示すには、ブラウザーからこのページにアクセスしてください。 図 8 に示すように、最初にデータの最初のページが表示されます。 次に、[カテゴリの変更 (WITH TRANSACTION)] ボタンをクリックします。 これによりポストバックが発生し、すべての製品 CategoryID
値の更新が試みられますが、外部キー制約違反が発生します (図 9 を参照)。
図 8: 製品が Pageable GridView に表示される (フルサイズの画像を表示する をクリックします)
図 9: 外部キー制約違反のカテゴリの結果を再割り当てする (フルサイズの画像を表示する をクリックします)
次に、ブラウザーの [戻る] ボタンをクリックし、[グリッドの更新] ボタンをクリックします。 データを更新すると、図 8 に示すのとまったく同じ出力が表示されます。 つまり、一部の製品 CategoryID
が有効な値に変更され、データベースで更新されたにもかかわらず、外部キー制約違反が発生したときにロールバックされました。
次に、[カテゴリの変更 (トランザクションなし)] ボタンをクリックしてみてください。 これにより、同じ外部キー制約違反エラーが発生します (図 9 を参照)。 今回は、値が CategoryID
有効な値に変更された製品はロールバックされません。 ブラウザーの [戻る] ボタンを押し、[グリッドの更新] ボタンをクリックします。 図 10 に示すように、 CategoryID
最初の 8 つの製品の s が再割り当てされています。 たとえば、図 8 では、Chang は 1 の を CategoryID
持っていましたが、図 10 では 2 に再割り当てされています。
図 10: 一部の製品 CategoryID
の値は更新されましたが、その他は更新されませんでした (クリックするとフルサイズの画像が表示されます)
まとめ
既定では、TableAdapter の メソッドは、実行されたデータベース ステートメントをトランザクションのスコープ内でラップしませんが、少しの作業で、トランザクションを作成、コミット、ロールバックするメソッドを追加できます。 このチュートリアルでは、 クラスにProductsTableAdapter
、、CommitTransaction
、 の 3 つのメソッドを作成しましたRollbackTransaction
。 BeginTransaction
これらのメソッドをブロックと共に使用して、 Try...Catch
一連のデータ変更ステートメントをアトミックにする方法について説明しました。 特に、 で ProductsTableAdapter
メソッドをUpdateWithTransaction
作成しました。このメソッドでは、Batch Update パターンを使用して、指定された の行に必要な変更を実行しますProductsDataTable
。 また、 メソッドを DeleteProductsWithTransaction
BLL の ProductsBLL
クラスに追加しました。このメソッドは、値の ProductID
をList
入力として受け入れ、それぞれの ProductID
DB-Direct パターン メソッドDelete
を呼び出します。 どちらのメソッドも、まずトランザクションを作成し、ブロック内でデータ変更ステートメントを Try...Catch
実行します。 例外が発生した場合、トランザクションはロールバックされ、それ以外の場合はコミットされます。
手順 5 では、トランザクション バッチ更新と、トランザクションの使用を怠ったバッチ更新の影響を示しました。 次の 3 つのチュートリアルでは、このチュートリアルで説明する基盤に基づいて構築し、バッチ更新、削除、挿入を実行するためのユーザー インターフェイスを作成します。
プログラミングに満足!
もっと読む
このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。
- トランザクションが簡単になりました:
System.Transactions
- TransactionScope と DataAdapters
- .NET での Oracle データベース トランザクションの使用
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dave Gardner、Hilton Giesenow、Toria Murphy でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示