What is the purpose of Transactionscope blocks?

Asked

Viewed 2,458 times

8

I have a code with several blocks TransactionScope and I didn’t 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())
            {
                // ....
            }
        }
    }

So the questions:

  1. What good is?
  2. And when should we use?
  • 1

    I can’t stop to make a good example right now, so I’m just going to put a comment on that link, I think here you can understand cool, sorry I didn’t create a good answer.

  • @Ricardo, if it were in Portuguese it would be cool.

  • @Gokussjgod, are you creating a Transaction by command? or is this just an example?

  • @Tobymosque, this was already in a class I’m giving maintenance..

2 answers

5

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

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

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

Since the transactions must be atomic and by good practice the methods must have a single responsibility, then it is interesting that only one Transaction per method.

Consistency: When completed, a transaction should leave all data in a consistent state. In a relational database, all rules shall be applied to modifications of the transaction to maintain full data integrity. All data structures internal, such as B-tree indices or double lists linked, must be correct at the end of the transaction.¹

Isolation: Modifications made by simultaneous transactions should be isolated from modifications made by any other transaction simultaneous. A transaction recognizes the data in the state they were in before another simultaneous transaction has modified or recognized 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 reload the initial data and forward a series of transactions so that the data obtained are in the same state as they were after the transactions originals were executed.¹

Durability: After a transaction has been completed, its effects remain permanently on the system. Modifications persist even in the case of a system crash.¹

So with this in mind, then perhaps your method would make better 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 must be searched regardless of the outcome of the others (which is what is happening in your example), then there is no need to use Transactionscope, after all its operations need not be Atomic.

P.S.: Another point that I found strange in your example was the non-sparameterization of the query. By parameterizing you gain in security (avoids SQL injection) and performance (the database can cache the query execution plan).

¹ - Transactions (Data Bank Mechanism)

  • Actually I think it was not a good example the code snippet, but see that the query ( query. Append( .. )is not created from the scope, and it is used in all Transactionscope.

4


Whenever we talk about transactions we are thinking mainly 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 make a rollback undoing what had already been done.

We also need to ensure that the status is the same while the transaction is taking place, it needs to be consistent at all times and maintain isolation from other transactions on the same object. I’m talking about it in What is a racing condition?.

Using the class TransactionScope is a very simple way to tell where a transaction starts and ends. She will be responsible for doing whatever is necessary to ensure the necessary features of the transaction. Simple, no?

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

Creating classes that can be transactional already takes a little more work. You can’t turn anything into a transaction. The objects you need that are transactional need to know what to do during the transaction process. In general need to implement the interface IEnlistmentNotification properly, which may not be at all simple.

Example inspired by a article by the Codeguru.

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;
   }
}

I put in the Github for future reference.

These transactions can even be distributed between computers.

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.