C#.Net 2.0でトランザクションを実行するためのベストプラクティスは何ですか。使用すべきクラスは何ですか?注目すべき落とし穴は何ですか。すべてのコミットとロールバック。 DBにデータを挿入するときにトランザクションを実行する必要があるかもしれないプロジェクトを開始しているところです。トランザクションに関する基本的なものへの応答やリンクも歓迎します。
トランザクションには主に2つの種類があります。接続トランザクションとアンビエントトランザクション。接続トランザクション(SqlTransactionなど)は、db接続(SqlConnectionなど)に直接関連付けられています。つまり、接続を渡し続ける必要があります-場合によってはOKですが、「作成/使用/解放」は許可されません。使用法、およびクロスデータベース作業を許可しません。例(スペース用にフォーマット):
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つのメソッドは独自の接続(オープン/使用/クローズ/破棄)を処理できますが、何も渡さずに静かにアンビエントトランザクションの一部になることに注意してください。
コードにエラーが発生した場合、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;
}
}
}
}
また、C#自体でトランザクションを実行する代わりに、トランザクションを独自のストアドプロシージャにラップし、そのように処理することもできます。
データベース関連のものだけに必要な場合、一部のORマッパー(NHibernateなど)はデフォルトでデフォルトのtransactinoをサポートします。
また、必要なものにも依存します。基本的な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
からすぐにポップされ、トランザクションは常にコミットまたはロールバックします(実行するよう指示した内容によって異なります)。私たちが直面した最大の問題は、常にコミットすることでした。を使用すると、トランザクションの範囲が制限されます。