简体   繁体   中英

Handle transaction when invoking SQL stored procedure in side a C# loop

I'm working on a system which is already developed by some other developers. And in that system they have invoked a stored procedure which use to insert records in side a loop in C# without using User define table types.

And what I need to do is to add a transaction to this scenario. But the problem is I have no idea about the place to have the transaction.

I would like to know whether have it in the C# code warping up the loop or inside the stored procedure.

You can only have it inside the C# loop. A transaction started inside the procedure must commit before the procedures exits. SQL Server checks the @@TRANCOUNT before and after running a procedure and if the results don't match an exception is raised. So is impossible for a procedure to start a transaction that has to be committed by caller.

The easiest think to do is to wrap your C# code in a transaction scope:

using(var scope =  new TransactionScope(
      TransactionScopeOption.Required, 
      new TransactionOptions() {
         IsolationLevel = IsolationLevel.ReadCommitted
      })) {
   // Do the work here
   scope.Complete();
}

Note that passing the IsolationLevel = IsolationLevel.ReadCommitted is critical .

In this case Transaction needs to begin in the C# code, you have following options:

  • Use the Transaction from the Connection object :

     using (var conn = new SqlConnection(connString)) { conn.Open(); using (IDbTransaction tran = conn.BeginTransaction()) { try { // transactional code... using (SqlCommand cmd = conn.CreateCommand()) { // Loop the Insert operation here cmd.CommandText = "INSERT Operation"; cmd.Transaction = tran as SqlTransaction; cmd.ExecuteNonQuery(); } tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw; } } } 
  • Use Implicit or Explicit Ambient transaction
  • Use TransactionScope (Implicit) Ambient transaction (On Exception it's an automatic rollback)

     using (var scope = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted })) { using (var conn = new SqlConnection(connString)) { conn.Open(); using (SqlCommand cmd = conn.CreateCommand()) { // Loop the Insert operation here cmd.CommandText = "INSERT Operation"; cmd.ExecuteNonQuery(); } } scope.Complete(); } 
    • Use CommittableTransaction (Explicit) Ambient transaction

       var tran = new CommittableTransaction(); using (var conn = new SqlConnection(connString)) { conn.Open(); try { conn.EnlistTransaction(tran); using (SqlCommand cmd = conn.CreateCommand()) { // Loop the Insert operation here cmd.CommandText = "INSERT Operation"; cmd.ExecuteNonQuery(); } tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw; } } 

For Ambient transactions , please remember that if you open more than one connection, then it will get promoted from local to global transaction , thus requiring MSDTC and that has a performance impact.

Check the following link:

http://www.codeproject.com/Articles/690136/All-About-TransactionScope

In SQL SERVER

BEGIN TRY
BEGIN TRAN 

-- DO YOUR INSERT /UPDATE /DELETE HERE 

COMMIT TRAN T1
END TRY
BEGIN CATCH 
-- // ROLL BACK IF ERROR OCCURS

ROLLBACK TRAN T1
END CATCH

In C#

using(TransactionScope tran = new TransactionScope()) {

     // DO YOUR INSERT /UPDATE /DELETE HERE 

    tran.Complete();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM