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

作成者: 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 ステートメントについて説明します。

注意

名前空間の System.Transactions クラスを使用すると、開発者はTransactionScopeトランザクションのスコープ内で一連のステートメントをプログラムでラップでき、2 つの異なるデータベースや、Microsoft SQL Server データベース、Oracle データベース、Web サービスなどの異種データ ストアなど、複数のソースを含む複雑なトランザクションのサポートが含まれます。 ADO.NET はデータベーストランザクションに対してより具体的であり、多くの場合、リソースを集中的に消費することがはるかに少ないため、クラスのTransactionScope代わりに ADO.NET トランザクションを使用することにしました。 さらに、特定のシナリオでは、クラスは TransactionScope Microsoft 分散トランザクション コーディネーター (MSDTC) を使用します。 MSDTC を取り巻く構成、実装、およびパフォーマンスの問題により、これらのチュートリアルの範囲を超えて、かなり特殊で高度なトピックになります。

ADO.NET で SqlClient プロバイダーを操作する場合、オブジェクトを返すSqlTransactionクラス s BeginTransaction メソッドSqlConnection呼び出しによってトランザクションが開始されます。 トランザクションを構成するデータ変更ステートメントは、ブロック内に try...catch 配置されます。 ブロック内の ステートメントでtryエラーが発生した場合、実行は、オブジェクトの Rollback メソッドを使用してトランザクションをロールバックできるブロックにSqlTransaction転送catchされます。 すべてのステートメントが正常に完了すると、ブロックの末尾tryにある オブジェクトの Commit メソッドSqlTransaction呼び出すと、トランザクションがコミットされます。 次のコード スニペットは、このパターンを示しています。 「 トランザクションを使用したデータベースの一貫性の維持」を参照してください。

// Create the SqlTransaction object
SqlTransaction myTransaction = 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;
}

既定では、型指定された DataSet の TableAdapters はトランザクションを使用しません。 トランザクションのサポートを提供するには、TableAdapter クラスを拡張して、上記のパターンを使用してトランザクションのスコープ内で一連のデータ変更ステートメントを実行する追加のメソッドを含める必要があります。 手順 2 では、部分クラスを使用してこれらのメソッドを追加する方法について説明します。

手順 1: バッチ データ Web ページの操作の作成

データベース トランザクションをサポートするように DAL を拡張する方法を調べ始める前に、まず、このチュートリアルに必要な ASP.NET Web ページと、次の 3 つの Web ページを作成します。 まず、 という名前 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 で構成されています。 DataTable はデータを保持しますが、TableAdapters は、データベースから DataTables にデータを読み取る機能、DataTables に加えられた変更を使用してデータベースを更新する機能などを提供します。 TableAdapters には、データを更新するための 2 つのパターン (Batch Update と DB-Direct と呼ばれる) があることを思い出してください。 バッチ更新パターンでは、TableAdapter に DataSet、DataTable、または DataRows のコレクションが渡されます。 このデータは列挙され、挿入、変更、または削除された行ごとに、 InsertCommandUpdateCommand、または DeleteCommand が実行されます。 DB-Direct パターンでは、TableAdapter には、1 つのレコードの挿入、更新、または削除に必要な列の値が渡されます。 その後、DB Direct パターン メソッドは、渡された値を使用して、適切な InsertCommandUpdateCommand、または DeleteCommand ステートメントを実行します。

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

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

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

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

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

ファイルに次のコードを ProductsTableAdapter.TransactionSupport.cs 入力します。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
    public partial class ProductsTableAdapter
    {
        private SqlTransaction _transaction;
        private SqlTransaction Transaction
        {
            get
            {                
                return this._transaction;
            }
            set
            {
                this._transaction = value;
            }
        }
        public void BeginTransaction()
        {
            // Open the connection, if needed
            if (this.Connection.State != ConnectionState.Open)
                this.Connection.Open();
            // Create the transaction and assign it to the Transaction property
            this.Transaction = this.Connection.BeginTransaction();
            // Attach the transaction to the Adapters
            foreach (SqlCommand command in this.CommandCollection)
            {
                command.Transaction = this.Transaction;
            }
            this.Adapter.InsertCommand.Transaction = this.Transaction;
            this.Adapter.UpdateCommand.Transaction = this.Transaction;
            this.Adapter.DeleteCommand.Transaction = this.Transaction;
        }
        public void CommitTransaction()
        {
            // Commit the transaction
            this.Transaction.Commit();
            // Close the connection
            this.Connection.Close();
        }
        public void RollbackTransaction()
        {
            // Rollback the transaction
            this.Transaction.Rollback();
            // Close the connection
            this.Connection.Close();
        }
   }
}

ここでのクラス宣言のキーワード (keyword)はpartial、 内で追加されたメンバーが名前空間の クラスに追加されることをコンパイラにProductsTableAdapterNorthwindTableAdapters示します。 ファイルの using 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: トランザクションの傘下のデータを更新および削除するメソッドを追加する

これらのメソッドが完了したら、トランザクションの傘下で一連のコマンドを実行するメソッドまたは BLL にメソッド ProductsDataTable を追加する準備ができました。 次のメソッドでは、Batch Update パターンを使用して、トランザクションを ProductsDataTable 使用してインスタンスを更新します。 メソッドを呼び出してトランザクションを BeginTransaction 開始し、 ブロックを try...catch 使用してデータ変更ステートメントを発行します。 オブジェクトの Update メソッドをAdapter呼び出すと例外が発生した場合、トランザクションがロールバックされ、例外が再スローされるブロックに実行が転送catchされます。 メソッドは、 Update 指定された ProductsDataTable の行を列挙し、必要な InsertCommandUpdateCommand、および DeleteCommand を実行することで、Batch Update パターンを実装することを思い出してください。 これらのコマンドのいずれかがエラーになる場合、トランザクションはロールバックされ、トランザクションの有効期間中に行われた以前の変更が元に戻されます。 ステートメントが Update エラーなしで完了した場合、トランザクションは完全にコミットされます。

public int UpdateWithTransaction(Northwind.ProductsDataTable dataTable)
{
    this.BeginTransaction();
    try
    {
        // Perform the update on the DataTable
        int returnValue = this.Adapter.Update(dataTable);
        // If we reach here, no errors, so commit the transaction
        this.CommitTransaction();
        return returnValue;
    }
    catch
    {
        // If we reach here, there was an error, so rollback the transaction
        this.RollbackTransaction();
        throw;
    }
}

UpdateWithTransaction 部分クラスを ProductsTableAdapter 使用して、 メソッドを クラスに ProductsTableAdapter.TransactionSupport.cs追加します。 または、このメソッドをビジネス ロジック 層の ProductsBLL クラスに追加し、いくつかの構文的な変更を加えることができます。 つまり、、、 でthis.BeginTransaction()this.CommitTransaction()this.RollbackTransaction()これをキーワード (keyword)は に置き換Adapterえる必要があります (これは 型ProductsTableAdapterのプロパティProductsBLLの名前であることをAdapter思い出してください)。

メソッドは UpdateWithTransaction Batch Update パターンを使用しますが、次のメソッドに示すように、一連の DB-Direct 呼び出しをトランザクションのスコープ内で使用することもできます。 メソッドはDeleteProductsWithTransaction、 型intの を入力List<T>として受け取ります。これは、ProductID削除する s です。 メソッドは のBeginTransaction呼び出しを介してトランザクションを開始し、 ブロック内でtry、指定されたリストを反復処理して、各ProductID値の DB-Direct パターン Delete メソッドを呼び出します。 への呼び出し Delete のいずれかが失敗した場合、制御は、トランザクションがロールバックされ、例外が再スローされるブロックに転送 catch されます。 すべての呼び出しが Delete 成功した場合、トランザクションはコミットされます。 このメソッドを クラスに ProductsBLL 追加します。

public void DeleteProductsWithTransaction
    (System.Collections.Generic.List<int> productIDs)
{
    // Start the transaction
    Adapter.BeginTransaction();
    try
    {
        // Delete each product specified in the list
        foreach (int productID in productIDs)
        {
            Adapter.Delete(productID);
        }
        // Commit the transaction
        Adapter.CommitTransaction();
    }
    catch
    {
        // There was an error - rollback the transaction
        Adapter.RollbackTransaction();
        throw;
    }
}

複数の TableAdapter にトランザクションを適用する

このチュートリアルで調べたトランザクション関連のコードを使用すると、 に対する ProductsTableAdapter 複数のステートメントをアトミック操作として扱えます。 しかし、異なるデータベース テーブルに対する複数の変更をアトミックに実行する必要がある場合はどうでしょうか。 たとえば、カテゴリを削除するときに、最初に現在の製品を他のカテゴリに再割り当てすることをお勧めします。 製品の再割り当てとカテゴリの削除の 2 つの手順は、アトミック操作として実行する必要があります。 ただし、 ProductsTableAdapter にはテーブルを Products 変更するためのメソッドのみが含まれており、 CategoriesTableAdapter にはテーブルを変更 Categories するためのメソッドのみが含まれています。 では、トランザクションが両方の TableAdapters をどのように包含できますか?

1 つのオプションは、名前付き DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) にメソッドをCategoriesTableAdapter追加し、そのメソッドでストアド プロシージャを呼び出し、両方で製品を再割り当てし、ストアド プロシージャ内で定義されたトランザクションのスコープ内のカテゴリを削除することです。 今後のチュートリアルでは、ストアド プロシージャでトランザクションを開始、コミット、ロールバックする方法について説明します。

もう 1 つのオプションは、 メソッドを含む DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) ヘルパー クラスを DAL に作成することです。 このメソッドは、 と ProductsTableAdapterCategoriesTableAdapterインスタンスを作成し、これら 2 つの TableAdapters Connection プロパティを同じSqlConnectionインスタンスに設定します。 その時点で、2 つの TableAdapter のいずれかを呼び出してトランザクションを開始します BeginTransaction。 製品を再割り当てしてカテゴリを削除するための TableAdapters メソッドは、必要に応じてトランザクションがコミットまたはロールバックされたブロックで try...catch 呼び出されます。

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

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

クラス ファイルを ProductsBLL 開き、対応する DAL メソッドを呼び出す という名前 UpdateWithTransaction のメソッドを追加します。 に 2 つの新しいメソッドが追加されました。このメソッドProductsBLLUpdateWithTransactionは、先ほど追加した とDeleteProductsWithTransaction、手順 3 で追加されました。

public int UpdateWithTransaction(Northwind.ProductsDataTable products)
{
    return Adapter.UpdateWithTransaction(products);
}
public void DeleteProductsWithTransaction
    (System.Collections.Generic.List<int> productIDs)
{
    // Start the transaction
    Adapter.BeginTransaction();
    try
    {
        // Delete each product specified in the list
        foreach (int productID in productIDs)
            Adapter.Delete(productID);
        // Commit the transaction
        Adapter.CommitTransaction();
    }
    catch
    {
        // There was an error - rollback the transaction
        Adapter.RollbackTransaction();
        throw;
    }
}

注意

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

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

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

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

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

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

[更新]、[挿入]、[削除] タブの Drop-Down Lists を [(なし)] に設定します

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

データ ソースの構成ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が作成されます。 、ProductName、、および をProductID除くこれらのフィールドをすべて削除し、 プロパティと CategoryNameCategoryName BoundFields HeaderText プロパティの名前をそれぞれ Product プロパティと Category プロパティに変更ProductNameCategoryIDします。 スマート タグから、[ページングを有効にする] オプションをチェックします。 これらの変更を行った後、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 コントロールを追加します。 最初の Button s Text プロパティを [グリッドの更新] に設定し、2 番目のプロパティを [分類項目の変更 (トランザクションあり)] に設定し、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 つのボタン Click の各イベントのイベント ハンドラーを作成し、次のコードを使用します。

protected void RefreshGrid_Click(object sender, EventArgs e)
{
    Products.DataBind();
}
protected void ModifyCategoriesWithTransaction_Click(object sender, EventArgs e)
{
    // Get the set of products
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    // Update each product's CategoryID
    foreach (Northwind.ProductsRow product in products)
    {
        product.CategoryID = product.ProductID;
    }
    // Update the data using a transaction
    productsAPI.UpdateWithTransaction(products);
    // Refresh the Grid
    Products.DataBind();
}
protected void ModifyCategoriesWithoutTransaction_Click(object sender, EventArgs e)
{
    // Get the set of products
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    // Update each product's CategoryID
    foreach (Northwind.ProductsRow product in products)
    {
        product.CategoryID = product.ProductID;
    }
    // Update the data WITHOUT using a transaction
    NorthwindTableAdapters.ProductsTableAdapter productsAdapter = 
        new NorthwindTableAdapters.ProductsTableAdapter();
    productsAdapter.Update(products);
    // Refresh the Grid
    Products.DataBind();
}

refresh Button のイベント ハンドラーは、GridView の ClickDataBind メソッドを呼び出すことによって、データを GridView にProducts再バインドするだけです。

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

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

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

製品が Pageable GridView に表示される

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

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

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

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

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

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

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

まとめ

既定では、TableAdapter のメソッドは、実行されたデータベース ステートメントをトランザクションのスコープ内でラップしませんが、少しの作業で、トランザクションを作成、コミット、ロールバックするメソッドを追加できます。 このチュートリアルでは、 クラスBeginTransactionCommitTransactionProductsTableAdapter、および の 3 つのメソッドを作成しましたRollbackTransaction。 これらのメソッドをブロックと共に使用して、 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 は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。

特別な感謝

このチュートリアル シリーズは、多くの役立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dave Gardner、ヒルトン ギセナウ、テレサ マーフィーでした。 今後の MSDN 記事の確認に関心がありますか? その場合は、 に行mitchell@4GuysFromRolla.comをドロップしてください。