What is the purpose of TransactionScope blocks?

7

I have a code with several blocks TransactionScope and I do not quite understand its purpose.

ModeloColDataContext dm = DataContextFactory.GetContext(usuario);
    {
        if (documento > 0)
        {
            using (TransactionScope tr = new TransactionScope())
            {
                StringBuilder query = new StringBuilder();
                query.Append("UPDATE TABELA_DOCUMENTOS");
                query.Append(" SET");
                query.Append(" CLIENTE = " + clienteNovo);
                query.Append(" WHERE CLIENTE  = " + clienteAtual);
                query.Append(" AND DOCUMENTO = " + documento);

                var updateDocumento = dm.ExecuteQuery<String>(query.ToString());
                tr.Complete();
            }

            using (TransactionScope tr = new TransactionScope())
            {
                // ......
            }

            using (TransactionScope tr = new TransactionScope())
            {
                // ....
            }
        }
    }

Then the questions:

  • What is it for?
  • And when should we use?
  • asked by anonymous 31.10.2016 / 18:38

    2 answers

    3

    Whenever we talk about transactions we are mainly thinking about atomicity, that is, we want a certain block to be executed or nothing to be executed if there is a problem. A common way to solve the problem when something goes wrong in the middle is to do a roolback undoing what has already been done.

    We also need to ensure that the state is the same while the transaction is occurring, it must be consistent all the time and keep isolating other transactions on the same object. I mention this in What is a race condition? .

    Using the class TransactionScope is a very simple way of saying where a transaction begins and ends. She will be responsible for doing everything necessary to guarantee the necessary characteristics of the transaction. Simple, is not it?

    Using it is very simple yes, that's what's in the question. In essence it does not need anything else. Although, of course, it has some properties and settings to best suit each situation.

    Creating classes that can be transactional already gives a little more work. It can not turn anything into a transaction. The objects that you need to be transactional need to know what to do during the transaction process. In general, you must implement the IEnlistmentNotification interface appropriately, which can not be simple at all.

    Example inspired by a CodeGuru article .

    public class VolatileRM : IEnlistmentNotification {
       private int oldMemberValue;
       public int MemberValue { get; 
           set {
               var currentTx = Transaction.Current;
               if (currentTx != null) {
                   WriteLine("VolatileRM: SetMemberValue - EnlistVolatile");
                   currentTx.EnlistVolatile(this, EnlistmentOptions.None);
               }
               oldMemberValue = MemberValue;
               MemberValue = value;
          }
       }
    
       public void Commit(Enlistment enlistment) {
           WriteLine("VolatileRM: Commit");
           oldMemberValue = 0;
       }
    
       public void InDoubt(Enlistment enlistment)  {
           WriteLine("VolatileRM: InDoubt");
       }
    
       public void Prepare(PreparingEnlistment preparingEnlistment) {
           WriteLine("VolatileRM: Prepare");
           preparingEnlistment.Prepared();
       }
    
       public void Rollback(Enlistment enlistment) {
           WriteLine("VolatileRM: Rollback");
           MemberValue = oldMemberValue;
           oldMemberValue = 0;
       }
    }
    

    These transactions can even be distributed between computers.

    Ricardo gave a comment on a mini tutorial interesting to understand better, which obviously does not it would make sense to reproduce here, but specific questions fit.

        
    31.10.2016 / 19:09
    3

    Goku, since your question makes use of Linq to SQL, then I will only consider the transactions involving Database and I will disregard transactions with installed objects.

    So, to get the good use of a Transaction, we must respect ACID.:

      

    Atomicity: A transaction must be an atomic unit of work; or all of your data modifications are performed or   none of them is executed.¹

    Since transactions must be atomic and for good practice methods must have a sole responsibility, then it is interesting to have only one Transaction per method.

      

    Consistency: When complete, a transaction must leave all data in a consistent state. In a relational database,   all rules must be applied to the transaction modifications for   integrity of the data. All data structures   such as tree indexes B or double lists   should be correct at the end of the transaction.¹

         

    Isolation: Changes made by concurrent transactions must be isolated from modifications made by any other transaction   simultaneously. A transaction recognizes the data as it was   before another concurrent transaction has modified them or recognizes the   data after the second transaction has been completed but not   recognizes an intermediate state. This is called serializability   because it results in the ability to recharge the initial data and   re-run a series of transactions so that the data obtained   are in the same state as they were after transactions   1 were executed.

         

    Durability: Once a transaction has been completed, its effects stay permanently on the system. Modifications persist   even in the event of a system crash.¹

    So with this in mind, then perhaps your method would make the best use of TransactionScope if it were implemented as follows:

    ModeloColDataContext dm = DataContextFactory.GetContext(usuario);
    {
        if (documento > 0)
        {
            try
            {
                using (TransactionScope tr = new TransactionScope())
                {
                    var updateDocumento = dm.ExecuteQuery<String>(@"
                        UPDATE TABELA_DOCUMENTOS
                        SET CLIENTE = {0}
                        WHERE CLIENTE = {1} AND DOCUMENTO = {2}
                    ", clienteNovo, clienteAtual, documento);
    
                    if (updateDocumento == valorEsperado)
                    {
                        // realiza mais algunas operações no Banco
    
                        // note, caso o updateDocumento não tenha o valor esperado ou ocorra algum erro durante as consultas posteriores, o "UPDATE TABELA_DOCUMENTOS..." será desfeito.
                        tr.Complete();
                    }
                }
            }
            catch (Exception err)
            {
                //realiza algum tratamento para o erro
            }
        }
    }
    

    However, if your DMLs commands are completely independent and should be persisted regardless of the result of the others (which is what is happening in your example), then there is no need to use TransactionScope, after all your operations do not need be Atomic.

    P.S .: another point that I found strange in your example was the non-parameterization of the query. When parameterizing you gain in security (avoids SQL injection) and performance (the bank can put the execution plan of the query in cache).

    ¹ - Transactions (Bank Mechanism of Data)

        
    31.10.2016 / 19:41