C# .Net 2.0 でトランザクションを実行するためのベスト プラクティスは何ですか。使用する必要があるクラスは何ですか? 注意すべき落とし穴などは何ですか。コミットとロールバックのすべて。DB にデータを挿入するときにいくつかのトランザクションを実行する必要があるプロジェクトを開始したところです。トランザクションに関する基本的なことでも、応答やリンクは大歓迎です。
5 に答える
トランザクションには主に 2 種類あります。接続トランザクションとアンビエント トランザクション。接続トランザクション (SqlTransaction など) は db 接続 (SqlConnection など) に直接関連付けられています。つまり、接続を渡し続ける必要があります。場合によっては問題ありませんが、「作成/使用/解放」は許可されません。クロスデータベースの作業を許可しません。例(スペース用にフォーマット):
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) を処理できますが、何も渡さなくても暗黙のうちにアンビエント トランザクションの一部になることに注意してください。
If your code errors, Dispose() will be called without Complete(), so it will be rolled back. The expected nesting etc is supported, although you can't roll-back an inner transaction yet complete the outer transaction: if anybody is unhappy, the transaction is aborted.
The other advantage of TransactionScope is that it isn't tied just to databases; any transaction-aware provider can use it. WCF, for example. Or there are even some TransactionScope-compatible object models around (i.e. .NET classes with rollback capability - perhaps easier than a memento, although I've never used this approach myself).
All in all, a very, very useful object.
Some caveats:
- SQL Server 2000 では、TransactionScope はすぐに DTC に送られます。これは SQL Server 2005 以降で修正されており、DTC に昇格したときに 2 つのソースなどと通信するまで LTM を使用できます (オーバーヘッドがはるかに少なくなります)。
- 接続文字列を微調整する必要がある可能性があることを意味するグリッチがあります
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# 自体でトランザクションを実行する代わりに、トランザクションを独自のストアド プロシージャにラップして、その方法で処理することもできます。
db 関連のものだけが必要な場合は、一部の OR マッパー (NHibernate など) がデフォルトですぐに使えるトランザクションをサポートします。
また、必要なものによっても異なります。基本的な 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
、トランザクションは常にコミットまたはロールバックします (何をするように指示したかによって異なります)。私たちが直面した最大の問題は、常にコミットするようにすることでした。を使用すると、トランザクションの範囲が限定されます。