C#.Net 2.0でトランザクションを実行するためのベストプラクティスは何ですか。使用する必要があるクラスは何ですか?気を付けなければならない落とし穴などは何ですか。データをDBに挿入するときにトランザクションを実行する必要があるプロジェクトを開始しています。トランザクションについての基本的なことについての応答やリンクも歓迎します。
C#.Net 2.0でトランザクションを実行するためのベストプラクティスは何ですか。使用する必要があるクラスは何ですか?気を付けなければならない落とし穴などは何ですか。データをDBに挿入するときにトランザクションを実行する必要があるプロジェクトを開始しています。トランザクションについての基本的なことについての応答やリンクも歓迎します。
回答:
トランザクションには主に2種類あります。接続トランザクションとアンビエントトランザクション。接続トランザクション(SqlTransactionなど)は、db接続(SqlConnectionなど)に直接関連付けられています。つまり、接続の受け渡しを継続する必要があります-場合によってはOKですが、「作成/使用/解放」は許可されません。使用、およびクロスDB作業を許可しません。例(スペース用にフォーマット済み):
using (IDbTransaction tran = conn.BeginTransaction()) {
try {
// your code
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
面倒すぎず、接続「conn」に限定されています。別のメソッドを呼び出す場合は、「conn」を渡す必要があります。
代替は、アンビエントトランザクションです。.NET 2.0の新機能であるTransactionScopeオブジェクト(System.Transactions.dll)では、さまざまな操作を使用できます(適切なプロバイダーがアンビエントトランザクションに自動的に参加します)。これにより、既存の(非トランザクション)コードに簡単にレトロフィットしたり、複数のプロバイダーと話したりすることができます(ただし、複数のプロバイダーと話すとDTCが関与します)。
例えば:
using(TransactionScope tran = new TransactionScope()) {
CallAMethodThatDoesSomeWork();
CallAMethodThatDoesSomeMoreWork();
tran.Complete();
}
ここで、2つのメソッドは独自の接続(open / use / close / dispose)を処理できますが、何も渡さなくても暗黙的にアンビエントトランザクションの一部になることに注意してください。
コードにエラーがある場合、Dispose()はComplete()なしで呼び出されるため、ロールバックされます。予想されるネストなどがサポートされていますが、内部トランザクションをロールバックすることはできませんが、外部トランザクションは完了します。誰かが不満を持っている場合、トランザクションは中止されます。
TransactionScopeのもう1つの利点は、データベースだけに関連付けられていないことです。トランザクション対応プロバイダーは、それを使用できます。たとえば、WCF。あるいは、TransactionScope互換のオブジェクトモデルもいくつかあります(つまり、ロールバック機能を備えた.NETクラス-おそらく私はこの方法を使用したことがありませんが、記念品より簡単です)。
全体として、非常に便利なオブジェクトです。
いくつかの警告:
protected void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
{
connection1.Open();
// Start a local transaction.
SqlTransaction sqlTran = connection1.BeginTransaction();
// Enlist a command in the current transaction.
SqlCommand command = connection1.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute two separate commands.
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
command.ExecuteNonQuery();
command.CommandText =
"insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
command.ExecuteNonQuery();
// Commit the transaction.
sqlTran.Commit();
Label3.Text = "Both records were written to database.";
}
catch (Exception ex)
{
// Handle the exception if the transaction fails to commit.
Label4.Text = ex.Message;
try
{
// Attempt to roll back the transaction.
sqlTran.Rollback();
}
catch (Exception exRollback)
{
// Throws an InvalidOperationException if the connection
// is closed or the transaction has already been rolled
// back on the server.
Label5.Text = exRollback.Message;
}
}
}
}
db関連のものだけで必要な場合は、一部のORマッパー(NHibernateなど)がデフォルトでそのままの状態でtransactinosをサポートします。
また、必要なものによっても異なります。基本的なSQLトランザクションの場合、コードでBEGIN TRANSとCOMMIT TRANSを使用してTSQLトランザクションを試すことができます。これが最も簡単な方法ですが、複雑なため、適切にコミット(およびロールバック)するように注意する必要があります。
私は次のようなものを使用します
SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
...
Do SQL stuff here passing my trans into my various SQL executers
...
trans.Commit // May not be quite right
}
障害が発生すると、エラーが発生using
し、トランザクションは常にコミットまたはロールバックされます(実行する指示に応じて異なります)。私たちが直面した最大の問題は、常にコミットすることでした。を使用すると、トランザクションの範囲が制限されます。