Share via


トランザクション内のデータベース変更をラップする (VB)

作成者: Scott Mitchell

PDF のダウンロード

このチュートリアルは、データのバッチの更新、削除、挿入を行う 4 つのチュートリアルの最初のチュートリアルです。 このチュートリアルでは、データベース トランザクションでバッチ変更をアトミック操作として実行する方法について説明します。これにより、すべてのステップが成功するか、すべてのステップが失敗するかを保証します。

はじめに

データの挿入、更新、および削除の概要 」チュートリアルから見たように、GridView では行レベルの編集と削除が組み込まれています。 マウスを数回クリックするだけで、行ごとに編集と削除を行うコンテンツであれば、コード行を記述せずに豊富なデータ変更インターフェイスを作成できます。 ただし、特定のシナリオでは、これは不十分であり、レコードのバッチを編集または削除する機能をユーザーに提供する必要があります。

たとえば、ほとんどの Web ベースのメール クライアントでは、グリッドを使用して各メッセージを一覧表示します。各行には、電子メールの情報 (件名、送信者など) と共にチェックボックスが含まれています。 このインターフェイスを使用すると、ユーザーは複数のメッセージを確認し、[選択したメッセージの削除] ボタンをクリックして複数のメッセージを削除できます。 バッチ編集インターフェイスは、ユーザーが多くの異なるレコードを一般的に編集する場合に最適です。 ユーザーが [編集] をクリックして変更を加え、変更する必要があるレコードごとに [更新] をクリックするのではなく、バッチ編集インターフェイスによって、各行が編集インターフェイスでレンダリングされます。 ユーザーは、変更する必要がある行のセットをすばやく変更し、[すべて更新] ボタンをクリックしてこれらの変更を保存できます。 この一連のチュートリアルでは、データのバッチを挿入、編集、削除するためのインターフェイスを作成する方法について説明します。

バッチ操作を実行するときは、バッチ内の一部の操作が成功し、他の操作が失敗する可能性があるかどうかを判断することが重要です。 インターフェイスを削除するバッチを検討する - 最初に選択したレコードが正常に削除されても、外部キー制約違反のために 2 つ目のレコードが失敗した場合はどうなりますか? 最初のレコードの削除をロールバックするか、最初のレコードを削除したままにしてもかまいませんか?

バッチ操作を アトミック操作 (すべてのステップが成功するか、すべてのステップが失敗する) として扱う場合は、 データベース トランザクションのサポートを含むようにデータ アクセス層を拡張する必要があります。 データベース トランザクションは、トランザクションの傘下で実行される 、UPDATE、および DELETE ステートメントのINSERTセットの原子性を保証し、最新のデータベース システムのほとんどでサポートされている機能です。

このチュートリアルでは、DAL を拡張してデータベース トランザクションを使用する方法について説明します。 以降のチュートリアルでは、インターフェイスのバッチ挿入、更新、および削除のための Web ページの実装について説明します。 始めましょう。

注意

バッチ トランザクション内のデータを変更する場合、アトミック性は常に必要であるとは限りません。 一部のシナリオでは、Web ベースの電子メール クライアントから一連のメールを削除する場合など、一部のデータ変更が成功し、同じバッチ内の他の変更が失敗する場合があります。 削除プロセスの途中でデータベース エラーが発生した場合、エラーなしで処理されたレコードが削除されたままになる可能性があります。 このような場合、データベース トランザクションをサポートするために DAL を変更する必要はありません。 ただし、原子性が不可欠なバッチ操作シナリオは他にもあります。 顧客が 1 つの銀行口座から別の銀行口座に資金を移動する場合は、2 つの操作を実行する必要があります。1 つ目の口座から資金を差し引いてから 2 番目の口座に追加する必要があります。 銀行は最初のステップが成功しても 2 番目のステップが失敗しても気にしないかもしれませんが、顧客は明らかに混乱します。 このチュートリアルに取り組み、次の 3 つのチュートリアルで構築するインターフェイスのバッチ挿入、更新、および削除でデータベース トランザクションを使用する予定がない場合でも、DAL の機能強化を実装してデータベース トランザクションをサポートすることをお勧めします。

トランザクションの概要

ほとんどのデータベースには トランザクションのサポートが含まれており、複数のデータベース コマンドを 1 つの論理作業単位にグループ化できます。 トランザクションを構成するデータベース コマンドはアトミックであることが保証されます。つまり、すべてのコマンドが失敗するか、すべて成功します。

一般に、トランザクションは次のパターンを使用して SQL ステートメントを使用して実装されます。

  1. トランザクションの開始を示します。
  2. トランザクションを構成する SQL ステートメントを実行します。
  3. 手順 2 のステートメントのいずれかでエラーが発生した場合は、トランザクションをロールバックします。
  4. 手順 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

SqlDataSource-Related チュートリアルの ASP.NET ページを追加する

図 1: SqlDataSource-Related チュートリアルの ASP.NET ページを追加する

他のフォルダーと同様に、 Default.aspx ユーザー コントロールを SectionLevelTutorialListing.ascx 使用して、セクション内のチュートリアルを一覧表示します。 したがって、このユーザー コントロールを に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、またはコレクションが渡されます。 このデータは列挙され、挿入、変更、または削除された行ごとに、 InsertCommandUpdateCommand、または DeleteCommand が実行されます。 DB-Direct パターンでは、TableAdapter には、1 つのレコードの挿入、更新、または削除に必要な列の値が渡されます。 その後、DB Direct パターン メソッドは、渡された値を使用して、適切な InsertCommandUpdateCommand、または DeleteCommand ステートメントを実行します。

使用される更新パターンに関係なく、TableAdapters 自動生成メソッドはトランザクションを使用しません。 既定では、TableAdapter によって実行される各挿入、更新、または削除は、1 つの個別の操作として扱われます。 たとえば、BLL の一部のコードがデータベースに 10 個のレコードを挿入するために、DB-Direct パターンが使用されるとします。 このコードでは、TableAdapter のメソッドを Insert 10 回呼び出します。 最初の 5 回の挿入が成功したが、6 番目の挿入で例外が発生した場合、挿入された最初の 5 つのレコードはデータベースに残ります。 同様に、Batch Update パターンを使用して DataTable 内の挿入、変更、削除された行に対する挿入、更新、および削除を実行する場合、最初のいくつかの変更が成功したが、後でエラーが発生した場合、完了した以前の変更はデータベースに残ります。

特定のシナリオでは、一連の変更にわたって原子性を確保したいと考えています。 これを実現するには、トランザクションの傘の下に 、UpdateCommandDeleteCommand、 を実行する新しいメソッドをInsertCommand追加して、TableAdapter を手動で拡張する必要があります。 「データ アクセス層の作成」では、部分クラスを使用して、型指定された DataSet 内の DataTable の機能を拡張する方法について説明しました。 この手法は、TableAdapters でも使用できます。

型指定された DataSet Northwind.xsd は、フォルダーのDALサブフォルダーにありますApp_Code。 という名前のフォルダーにサブフォルダーを DAL 作成し、 という TransactionSupport 名前 ProductsTableAdapter.TransactionSupport.vb の新しいクラス ファイルを追加します (図 4 を参照)。 このファイルには、 の部分的な実装 ProductsTableAdapter が保持されます。これには、トランザクションを使用してデータ変更を実行するためのメソッドが含まれます。

TransactionSupport という名前のフォルダーと、ProductsTableAdapter.TransactionSupport.vbという名前のクラス ファイルを追加する

図 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 CommitRollback メソッドを呼び出します。

手順 3: トランザクションの傘の下にデータを更新および削除するメソッドを追加する

これらのメソッドが完了したら、トランザクションの傘の下で一連のコマンドを ProductsDataTable 実行するメソッドを または BLL に追加する準備が整いました。 次のメソッドでは、Batch Update パターンを使用して、トランザクションを ProductsDataTable 使用してインスタンスを更新します。 メソッドを呼び出してトランザクションを BeginTransaction 開始し、 ブロックを Try...Catch 使用してデータ変更ステートメントを発行します。 オブジェクトの Update メソッドのAdapter呼び出しによって例外が発生した場合、トランザクションがロールバックされ、例外が再スローされるブロックに実行が転送catchされます。 メソッドは、 Update 指定された ProductsDataTable の行を列挙し、必要な InsertCommandUpdateCommand、および 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き換える必要があります (型のプロパティProductsTableAdapterProductsBLLの名前を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) 作成することです。 このメソッドは、 と ProductsTableAdapterCategoriesTableAdapterインスタンスを作成し、これら 2 つの TableAdapters Connection プロパティを同じSqlConnectionインスタンスに設定します。 その時点で、2 つの TableAdapters のいずれかが への BeginTransaction呼び出しでトランザクションを開始します。 製品を再割り当てしてカテゴリを削除するための TableAdapters メソッドは、必要に応じてトランザクションがコミットまたはロールバックされたブロックで Try...Catch 呼び出されます。

手順 4: ビジネス ロジック レイヤーにメソッドを追加UpdateWithTransactionする

手順 3 では、 UpdateWithTransaction DAL の に ProductsTableAdapter メソッドを追加しました。 対応するメソッドを BLL に追加する必要があります。 プレゼンテーション レイヤーは DAL を直接呼び出して メソッドを呼び出 UpdateWithTransaction すことができますが、これらのチュートリアルでは、DAL をプレゼンテーション レイヤーから分離する階層構造のアーキテクチャを定義するように努めています。 したがって、このアプローチを継続する必要があります。

クラス ファイルを ProductsBLL 開き、対応する DAL メソッドを呼び出す という名前 UpdateWithTransaction のメソッドを追加します。 には、先ほど追加した と、手順 3 でProductsBLLUpdateWithTransaction追加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

注意

これらのメソッドには、 クラス内ProductsBLLDataObjectMethodAttribute他のほとんどのメソッドに割り当てられた属性は含まれません。これらのメソッドは、ASP.NET ページ分離コード クラスから直接呼び出されるためです。 ObjectDataSource DataObjectMethodAttribute の [データ ソースの構成] ウィザードと、どのタブ (SELECT、UPDATE、INSERT、または DELETE) に表示されるメソッドにフラグを設定するために使用されることを思い出してください。 GridView にはバッチ編集または削除の組み込みサポートがないため、コード不要の宣言型アプローチを使用するのではなく、プログラムでこれらのメソッドを呼び出す必要があります。

手順 5: プレゼンテーション 層からデータベース データをアトミックに更新する

レコードのバッチを更新するときにトランザクションに及ぼす影響を示すために、GridView 内のすべての製品を一覧表示するユーザー インターフェイスを作成し、クリックすると製品 CategoryID の値を再割り当てするボタン Web コントロールを含めます。 特に、カテゴリの再割り当ては進行するため、最初のいくつかの製品に有効な CategoryID 値が割り当てられ、他の製品には意図的に存在 CategoryID しない値が割り当てられます。 既存のカテゴリCategoryIDに一致しない 製品CategoryIDでデータベースを更新しようとすると、外部キー制約違反が発生し、例外が発生します。 この例でわかるように、トランザクションを使用すると、外部キー制約違反から発生した例外によって、以前の有効な CategoryID 変更がロールバックされるということです。 ただし、トランザクションを使用しない場合、最初のカテゴリに対する変更は残ります。

まず、フォルダー内のページをTransactions.aspxBatchData開き、GridView をツールボックスからDesignerにドラッグします。 を IDProducts 設定し、スマート タグから という名前 ProductsDataSourceの新しい ObjectDataSource にバインドします。 ObjectDataSource を構成して、クラスの GetProducts メソッドからデータをProductsBLLプルします。 これは読み取り専用の GridView になるため、[UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを [(なし)] に設定し、[完了] をクリックします。

ProductsBLL クラス s GetProducts メソッドを使用するように ObjectDataSource を構成する

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

UPDATE タブ、INSERT タブ、DELETE タブの Drop-Down Lists を に設定します (なし)

図 6: UPDATE、INSERT、および DELETE タブの Drop-Down Lists を (なし) に設定します (フルサイズの画像を表示する 場合は、ここをクリックします)

データ ソースの構成ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が作成されます。 、ProductNameおよび を除くこれらのフィールドをすべて削除し、 プロパティと CategoryName BoundFields HeaderText プロパティの名前をそれぞれ Product プロパティと Category プロパティに変更ProductNameします。ProductIDCategoryNameCategoryID スマート タグから、[ページングを有効にする] オプションをチェックします。 これらの変更を行った後、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 に示すスクリーン ショットのようになります。

ページには GridView と 3 つのボタン Web コントロールが含まれています

図 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 DataBindProducts再バインドするだけです。

2 番目のイベント ハンドラーは、製品 CategoryID を再割り当てし、BLL の新しいトランザクション メソッドを使用して、トランザクションの傘の下でデータベースの更新を実行します。 各製品 s CategoryID は、その と同じ値に任意に設定されることに注意してください ProductID。 これらの製品には有効な CategoryID にマップされる値があるため、これは最初のいくつかの製品ProductIDで正常に動作します。 しかし、sがProductID大きくなり始めると、sとCategoryIDsのこの偶然のProductID重複は適用されなくなりました。

3 番目Clickのイベント ハンドラーは、同じ方法で製品 CategoryID を更新しますが、既定Updateのメソッドを使用してデータベースに更新プログラムをProductsTableAdapter送信します。 このメソッドは Update トランザクション内で一連のコマンドをラップしないため、最初に発生した外部キー制約違反エラーの前に変更が行われると、保持されます。

この動作を示すには、ブラウザーからこのページにアクセスしてください。 図 8 に示すように、最初にデータの最初のページが表示されます。 次に、[カテゴリの変更 (WITH TRANSACTION)] ボタンをクリックします。 これによりポストバックが発生し、すべての製品 CategoryID 値の更新が試みられますが、外部キー制約違反が発生します (図 9 を参照)。

製品が Pageable GridView に表示される

図 8: 製品が Pageable GridView に表示される (フルサイズの画像を表示する をクリックします)

カテゴリを再割り当てすると、外部キー制約違反が発生する

図 9: 外部キー制約違反のカテゴリの結果を再割り当てする (フルサイズの画像を表示する をクリックします)

次に、ブラウザーの [戻る] ボタンをクリックし、[グリッドの更新] ボタンをクリックします。 データを更新すると、図 8 に示すのとまったく同じ出力が表示されます。 つまり、一部の製品 CategoryID が有効な値に変更され、データベースで更新されたにもかかわらず、外部キー制約違反が発生したときにロールバックされました。

次に、[カテゴリの変更 (トランザクションなし)] ボタンをクリックしてみてください。 これにより、同じ外部キー制約違反エラーが発生します (図 9 を参照)。 今回は、値が CategoryID 有効な値に変更された製品はロールバックされません。 ブラウザーの [戻る] ボタンを押し、[グリッドの更新] ボタンをクリックします。 図 10 に示すように、 CategoryID 最初の 8 つの製品の s が再割り当てされています。 たとえば、図 8 では、Chang は 1 の を CategoryID 持っていましたが、図 10 では 2 に再割り当てされています。

一部の製品 CategoryID 値は更新されましたが、他の製品は更新されませんでした

図 10: 一部の製品 CategoryID の値は更新されましたが、その他は更新されませんでした (クリックするとフルサイズの画像が表示されます)

まとめ

既定では、TableAdapter の メソッドは、実行されたデータベース ステートメントをトランザクションのスコープ内でラップしませんが、少しの作業で、トランザクションを作成、コミット、ロールバックするメソッドを追加できます。 このチュートリアルでは、 クラスにProductsTableAdapter、、CommitTransaction、 の 3 つのメソッドを作成しましたRollbackTransactionBeginTransaction これらのメソッドをブロックと共に使用して、 Try...Catch 一連のデータ変更ステートメントをアトミックにする方法について説明しました。 特に、 で ProductsTableAdapterメソッドをUpdateWithTransaction作成しました。このメソッドでは、Batch Update パターンを使用して、指定された の行に必要な変更を実行しますProductsDataTable。 また、 メソッドを DeleteProductsWithTransaction BLL の ProductsBLL クラスに追加しました。このメソッドは、値の ProductIDList入力として受け入れ、それぞれの ProductIDDB-Direct パターン メソッドDeleteを呼び出します。 どちらのメソッドも、まずトランザクションを作成し、ブロック内でデータ変更ステートメントを Try...Catch 実行します。 例外が発生した場合、トランザクションはロールバックされ、それ以外の場合はコミットされます。

手順 5 では、トランザクション バッチ更新と、トランザクションの使用を怠ったバッチ更新の影響を示しました。 次の 3 つのチュートリアルでは、このチュートリアルで説明する基盤に基づいて構築し、バッチ更新、削除、挿入を実行するためのユーザー インターフェイスを作成します。

プログラミングに満足!

もっと読む

このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。

著者について

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行をドロップしてください。