Commit multiple SqlCommands with SqlTransaction

3.6k views Asked by At

I am trying to pass a list of SqlCommand into a member function that holds the connection to the database.

public void CommitAsTransaction(List<SqlCommand> commands) {
    SqlTransaction transaction = null;
    SqlConnection connection = null;

    try {
        connection = this.CreateSqlConnection();
        connection.Open();

        transaction = connection.BeginTransaction("TransactionID");

        foreach (SqlCommand cmd in commands) {
            cmd.Transaction = transaction;
            cmd.Connection = connection;
            cmd.ExecuteNonQuery();
        }

        transaction.Commit();
    }
    catch (Exception ex) {
        transaction.Rollback();
    }

    connection.Close();
}

This is what I currently have. The error occurs because the command seems to be being executed as in place and the transaction.Commit(); is never reached. I have seen many people doing it like this and am not sure what I am doing wrong.

PS: The issue is that the stored procedures that will be getting executed MUST all be run within a single transaction, I do not control these and they're encrypted, the reason they must be run in a transaction is because they create temp records in a table that has a PK requirement.

2

There are 2 answers

0
Hexadron On BEST ANSWER

Thanks so much. I ended up figuring it out on my own based on other peoples combined answers, as a thank you here is the code I used:

public List<Models.eConnectModels.eConnStatus> CommitAsTransaction(List<SqlCommand> commands) 
{
    SqlTransaction transaction = null;
    SqlConnection connection = null;
    List<eConnStatus> ErrorList = new List<eConnStatus>();
            
    try 
    {
        connection = this.CreateSqlConnection();
        connection.Open();
                
        transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted, "TransactionID");

        foreach (SqlCommand cmd in commands) 
        {
            eConnStatus curErr = new eConnStatus();
            cmd.Transaction = transaction;
            cmd.Connection = connection;
            
            SqlParameter errorString = cmd.Parameters.Add("@oErrString", SqlDbType.VarChar);
            errorString.Direction = ParameterDirection.Output;
            errorString.Size = 8000;

            SqlParameter errorStatus = cmd.Parameters.Add("@O_iErrorState", SqlDbType.Int);
            errorStatus.Direction = ParameterDirection.Output;

            cmd.ExecuteNonQuery();
            curErr.ErrorState = (int)cmd.Parameters["@O_iErrorState"].Value;
            curErr.ErrorMessage = (string)cmd.Parameters["@oErrString"].Value;
            ErrorList.Add(curErr);
        }

        transaction.Commit();
    }
    catch (Exception ex) 
    {
        transaction.Rollback();
        connection.Close();
        throw ex;
    }
    
    connection.Close();
    return ErrorList;
}
0
andrew On

Can you use a transaction scope instead ? Something like:

// place this code inside CommitAsTransaction

using (TransactionScope scope = new TransactionScope())
{
     Boolean AllOK = true;
     SqlConnection connection = this.CreateSQLConnection();
     try
     {
         connection.Open()
     }
     catch (Exception e)
     {
       // deal with it how you need to
       AllOK = false;
     }

     if (AllOK)
     {
        foreach(SQlCommand cmd in Commands)
        {
            try
            {
                 cmd.Connection = connection;
                 cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
               // Deal with it.. 
               AllOK = false;
               break;
            }
        }

        if (AllOK)
        {
           scope.Complete();
           try
           {
               connection.Close();
           }
           catch (Exception e)
           {
             // deal with it
           }
        }
    }
}