トランザクション内のデータベース変更をラップする (C#)
このチュートリアルは、データのバッチの更新、削除、および挿入を見る 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 ステートメントについて説明します。
注意
名前空間の 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
図 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 で構成されています。 DataTable はデータを保持しますが、TableAdapters は、データベースから DataTables にデータを読み取る機能、DataTables に加えられた変更を使用してデータベースを更新する機能などを提供します。 TableAdapters には、データを更新するための 2 つのパターン (Batch Update と DB-Direct と呼ばれる) があることを思い出してください。 バッチ更新パターンでは、TableAdapter に DataSet、DataTable、または DataRows のコレクションが渡されます。 このデータは列挙され、挿入、変更、または削除された行ごとに、 InsertCommand
、 UpdateCommand
、または DeleteCommand
が実行されます。 DB-Direct パターンでは、TableAdapter には、1 つのレコードの挿入、更新、または削除に必要な列の値が渡されます。 その後、DB Direct パターン メソッドは、渡された値を使用して、適切な InsertCommand
、 UpdateCommand
、または DeleteCommand
ステートメントを実行します。
使用される更新パターンに関係なく、TableAdapters 自動生成メソッドではトランザクションは使用されません。 既定では、TableAdapter によって実行される各挿入、更新、または削除は、1 つの個別の操作として扱われます。 たとえば、BLL の一部のコードでデータベースに 10 個のレコードを挿入するために、DB-Direct パターンが使用されるとします。 このコードでは、TableAdapter s メソッドを Insert
10 回呼び出します。 最初の 5 回の挿入が成功したが、6 番目の挿入で例外が発生した場合、挿入された最初の 5 つのレコードはデータベースに残ります。 同様に、バッチ更新パターンを使用して DataTable 内の挿入、変更、削除された行の挿入、更新、削除を実行する場合、最初のいくつかの変更が成功したが、後でエラーが発生した場合、完了した以前の変更はデータベースに残ります。
特定のシナリオでは、一連の変更にわたって原子性を確保する必要があります。 これを実現するには、トランザクションの傘の下に 、UpdateCommand
DeleteCommand
、 を実行する新しいメソッドをInsertCommand
追加して、TableAdapter を手動で拡張する必要があります。 「データ アクセス層の作成」では、部分クラスを使用して、型指定された DataSet 内の DataTable の機能を拡張することを確認しました。 この手法は、TableAdapters でも使用できます。
型指定された DataSet Northwind.xsd
は、フォルダーのDAL
サブフォルダーにありますApp_Code
。 という名前のフォルダーにサブフォルダーを DAL
作成し、 という TransactionSupport
名前 ProductsTableAdapter.TransactionSupport.cs
の新しいクラス ファイルを追加します (図 4 を参照)。 このファイルには、 の部分的な実装 ProductsTableAdapter
が保持されます。これには、トランザクションを使用してデータ変更を実行するためのメソッドが含まれます。
図 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
、 内で追加されたメンバーが名前空間の クラスに追加されることをコンパイラにProductsTableAdapter
NorthwindTableAdapters
示します。 ファイルの 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 Commit
と Rollback
メソッドを呼び出します。
手順 3: トランザクションの傘下のデータを更新および削除するメソッドを追加する
これらのメソッドが完了したら、トランザクションの傘下で一連のコマンドを実行するメソッドまたは BLL にメソッド ProductsDataTable
を追加する準備ができました。 次のメソッドでは、Batch Update パターンを使用して、トランザクションを ProductsDataTable
使用してインスタンスを更新します。 メソッドを呼び出してトランザクションを BeginTransaction
開始し、 ブロックを try...catch
使用してデータ変更ステートメントを発行します。 オブジェクトの Update
メソッドをAdapter
呼び出すと例外が発生した場合、トランザクションがロールバックされ、例外が再スローされるブロックに実行が転送catch
されます。 メソッドは、 Update
指定された ProductsDataTable
の行を列挙し、必要な InsertCommand
、 UpdateCommand
、および 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 に作成することです。 このメソッドは、 と ProductsTableAdapter
のCategoriesTableAdapter
インスタンスを作成し、これら 2 つの TableAdapters Connection
プロパティを同じSqlConnection
インスタンスに設定します。 その時点で、2 つの TableAdapter のいずれかを呼び出してトランザクションを開始します BeginTransaction
。 製品を再割り当てしてカテゴリを削除するための TableAdapters メソッドは、必要に応じてトランザクションがコミットまたはロールバックされたブロックで try...catch
呼び出されます。
手順 4: メソッドをUpdateWithTransaction
ビジネス ロジック レイヤーに追加する
手順 3 では、 UpdateWithTransaction
DAL の に ProductsTableAdapter
メソッドを追加しました。 対応するメソッドを BLL に追加する必要があります。 プレゼンテーション レイヤーは DAL を直接呼び出してメソッドを呼び出 UpdateWithTransaction
すことができますが、これらのチュートリアルでは、プレゼンテーションレイヤーから DAL を分離する階層構造のアーキテクチャを定義するように努めています。 したがって、このアプローチを継続する必要があります。
クラス ファイルを ProductsBLL
開き、対応する DAL メソッドを呼び出す という名前 UpdateWithTransaction
のメソッドを追加します。 に 2 つの新しいメソッドが追加されました。このメソッドProductsBLL
UpdateWithTransaction
は、先ほど追加した と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;
}
}
注意
これらのメソッドには、 クラス内ProductsBLL
のDataObjectMethodAttribute
他のほとんどのメソッドに割り当てられた属性は含まれません。これらのメソッドは、ASP.NET ページ分離コード クラスから直接呼び出されるためです。 ObjectDataSource の [データ ソースの構成] ウィザードおよびタブ (SELECT、UPDATE、INSERT、または DELETE) に表示されるメソッドにフラグを設定するために使用されることを DataObjectMethodAttribute
思い出してください。 GridView にはバッチ編集または削除の組み込みサポートがないため、コード不要の宣言型アプローチを使用するのではなく、プログラムでこれらのメソッドを呼び出す必要があります。
手順 5: プレゼンテーション 層からデータベース データをアトミックに更新する
レコードのバッチを更新するときにトランザクションに与える影響を示すために、GridView 内のすべての製品を一覧表示するユーザー インターフェイスを作成し、ボタン Web コントロールを含め、クリックすると製品 CategoryID
の値を再割り当てします。 特に、カテゴリの再割り当てが進行し、最初のいくつかの製品に有効な CategoryID
値が割り当てられ、他の製品には意図的に存在 CategoryID
しない値が割り当てられます。 既存のカテゴリCategoryID
と一致しない製品CategoryID
でデータベースを更新しようとすると、外部キー制約違反が発生し、例外が発生します。 この例では、トランザクションを使用すると、外部キー制約違反から発生した例外によって、以前の有効な CategoryID
変更がロールバックされるということです。 ただし、トランザクションを使用しない場合、最初のカテゴリに対する変更は残ります。
まず、フォルダー内のページをTransactions.aspx
BatchData
開き、GridView をツールボックスからDesignerにドラッグします。 を ID
に Products
設定し、スマート タグから という名前 ProductsDataSource
の新しい ObjectDataSource にバインドします。 ObjectDataSource を構成して、クラスの GetProducts
メソッドからデータをProductsBLL
プルします。 これは読み取り専用の GridView であるため、[更新]、[挿入]、[削除] タブのドロップダウン リストを [(なし)] に設定し、[完了] をクリックします。
図 5: 図 5: クラス s GetProducts
メソッドを使用ProductsBLL
するように ObjectDataSource を構成する (フルサイズの画像を表示する 場合はクリックします)
図 6: UPDATE、INSERT、DELETE タブの Drop-Down Lists を (なし) に設定します (フルサイズの画像を表示する場合はクリックします)
データ ソースの構成ウィザードが完了すると、Visual Studio によって、製品データ フィールドの BoundFields と CheckBoxField が作成されます。 、ProductName
、、および をProductID
除くこれらのフィールドをすべて削除し、 プロパティと CategoryName
CategoryName
BoundFields HeaderText
プロパティの名前をそれぞれ Product プロパティと Category プロパティに変更ProductName
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 コントロールを追加します。 最初の 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 に示すスクリーン ショットのようになります。
図 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 の Click
DataBind
メソッドを呼び出すことによって、データを 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 を参照)。
図 8: Pageable GridView に製品が表示される (フルサイズの画像を表示するをクリックします)
図 9: 外部キー制約違反のカテゴリの結果を再割り当てする (フルサイズの画像を表示する をクリックします)
ブラウザーの [戻る] ボタンをクリックし、[グリッドの更新] ボタンをクリックします。 データを更新すると、図 8 とまったく同じ出力が表示されます。 つまり、一部の製品 CategoryID
が有効な値に変更され、データベースで更新された場合でも、外部キー制約違反が発生したときにロールバックされました。
次に、[カテゴリの変更 (トランザクションなし)] ボタンをクリックしてみてください。 これにより、同じ外部キー制約違反エラーが発生します (図 9 を参照)、今度は値が CategoryID
有効な値に変更された製品はロールバックされません。 ブラウザーの [戻る] ボタンをクリックし、[グリッドの更新] ボタンをクリックします。 図 10 に示すように、 CategoryID
最初の 8 つの製品の s が再割り当てされています。 たとえば、図 8 では、Chang a CategoryID
は 1 でしたが、図 10 では 2 に再割り当てされています。
図 10: 一部の製品 CategoryID
の値は更新されましたが、他の製品の値は更新されませんでした (フルサイズの画像を表示するには、ここをクリックします)
まとめ
既定では、TableAdapter のメソッドは、実行されたデータベース ステートメントをトランザクションのスコープ内でラップしませんが、少しの作業で、トランザクションを作成、コミット、ロールバックするメソッドを追加できます。 このチュートリアルでは、 クラスBeginTransaction
CommitTransaction
にProductsTableAdapter
、および の 3 つのメソッドを作成しましたRollbackTransaction
。 これらのメソッドをブロックと共に使用して、 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 は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。
特別な感謝
このチュートリアル シリーズは、多くの役立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Dave Gardner、ヒルトン ギセナウ、テレサ マーフィーでした。 今後の MSDN 記事の確認に関心がありますか? その場合は、 に行mitchell@4GuysFromRolla.comをドロップしてください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示