Transações são necessárias quando realizamos um conjunto de operações em um determinado recursos distribuído (banco de dados, serviço de mensageria, serviço de gerenciamento de arquivos, etc) e queremos garantir a completude deste conjunto de operações ao seu término, evitando que qualquer possível erro durante a execução do conjunto de operações deixe os recursos distribuídos inconsistentes.
As transações devem garantir um conjunto de características, tais características são chamadas de ACID:
– Atomicidade: Uma transação é uma unidade atômica de processamento, ou ela será executada em sua totalidade ou não será de modo nenhum.
– Consistência: Uma transação será preservadora de consistência se sua execução completa fizer o recurso distribuído passe de um estado consistente para outro.
– Isolamento: Uma transação deve ser executada como se estivesse isolada das demais. A execução de uma transação não deve sofrer interferências de quaisquer outra transação concorrente.
– Durabilidade: As mudanças aplicadas ao recurso distribuído por uma transação devem ser persistidas neste recurso distribuído ao término da transação. Essas mudanças não devem ser perdidas em razão de uma falha.
Podemos utilizar as transações de diferentes maneiras. Vou exemplificar três modos:
1 – Dentro de uma procedure de banco de dados: quando for necessária a manipulação de diferentes tabelas dentro de um mesmo bloco de código, então é recomendado o uso de transações em procedures, como abaixo.
-- ABRE UMA TRANSAÇÃO BEGIN TRANSACTION; BEGIN TRY -- COMANDOS SQL END TRY BEGIN CATCH -- SELECIONA DADOS DO ERRO SELECT ERROR_NUMBER() AS ErrorNumber ,ERROR_SEVERITY() AS ErrorSeverity ,ERROR_STATE() AS ErrorState ,ERROR_PROCEDURE() AS ErrorProcedure ,ERROR_LINE() AS ErrorLine ,ERROR_MESSAGE() AS ErrorMessage; -- SE EXISTIR UMA TRANSAÇÃO ENTÃO SERÁ FEITO O ROLLBACK IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; -- SE EXISTIR UM BLOCO DE TRANSAÇÃO ENTÃO É FEITO O COMMIT IF @@TRANCOUNT > 0 COMMIT TRANSACTION;
2 – Dentro de um bloco de código que faça acesso a um banco de dados: quando utilizarmos classe de acesso a dados do ADO.Net, é interessante que façamos uso de classes que herdem de DbTransaction (System.Data.Common), como a classe SqlTransaction, própria para o controle de transações que façam uso dos componentes do namespace System.Data.SqlClient.
// Abre a conexão com o banco de dados using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = connection.CreateCommand(); SqlTransaction transaction; // Cria o bloco de transação transaction = connection.BeginTransaction("SampleTransaction"); // Associa a conexão e a transação com o comando a ser executado command.Connection = connection; command.Transaction = transaction; try { command.CommandText = "-- EXECUTA UM COMANDO"; command.ExecuteNonQuery(); command.CommandText = "-- EXECUTA UM COMANDO"; command.ExecuteNonQuery(); // Efetua o commit da transação transaction.Commit(); Console.WriteLine("Ambos os registros foram registrados no banco de dados"); } catch (Exception ex) { Console.WriteLine("Commit Exception Type: {0}", ex.GetType()); Console.WriteLine(" Message: {0}", ex.Message); // Realiza o rollback da transação caso alguma falha ocorra transaction.Rollback(); } }
3 – Dentro de um bloco de código que trabalhe com diferentes recursos compartilhados: quando fizermos uso de diferentes componentes distribuídos podemos aproveitar o Gerenciador de Transações Distribuídas do Windows (Microsoft Distributed Transaction Coordinator) para nos auxiliar no funcionamento de nossa transação. Tecnologias como: Windows Message Queue, Microsoft SQL Server, Oracle DataBases, WebSphere Message Queue, BizTalk Servers e outros fazem integração com o Gerenciador de Transações Distribuídas do Windows.
// using System.Transactions; // Fazer referência a dll System.Transactions.dll try { // Cria o bloco de transação. using (TransactionScope scope = new TransactionScope()) { using (SqlConnection connection1 = new SqlConnection(connectString1)) { // Abre a conexão com o banco de dados 1 connection1.Open(); // Cria o objeto de comando para execução da primeira instrução no banco de dados SqlCommand command1 = new SqlCommand(commandText1, connection1); returnValue = command1.ExecuteNonQuery(); writer.WriteLine("Linhas afetadas pelo comando 1: {0}", returnValue); // Abre outra conexão, com outro banco de dados. using (SqlConnection connection2 = new SqlConnection(connectString2)) { // Abre a conexão com o banco de dados 2 connection2.Open(); // Executa um segundo comando em outro banco de dados returnValue = 0; SqlCommand command2 = new SqlCommand(commandText2, connection2); returnValue = command2.ExecuteNonQuery(); writer.WriteLine("Linhas afetadas pelo comando 2: {0}", returnValue); } } // Quando o procedimento estiver completo, então a transação é concluída scope.Complete(); } } catch (TransactionAbortedException ex) { writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message); } catch (ApplicationException ex) { writer.WriteLine("ApplicationException Message: {0}", ex.Message); }
Obs.: Sempre que utilizo transações, com Entity Framework ou Enterprise Library, faço uso do TransactionScope.
Referências:
http://msdn.microsoft.com/pt-br/library/ms175976.aspx
http://msdn.microsoft.com/pt-br/library/system.transactions.transactionscope.aspx
http://msdn.microsoft.com/pt-br/library/system.data.common.dbtransaction.aspx
http://technet.microsoft.com/en-us/library/cc759136%28v=ws.10%29.aspx
Elmasri; Navathe; Sistemas de Banco de Dados; 4a Edição; Pearson – Addison Wesley
Por
MSc. Fernando Henrique Inocêncio Borba Ferreira
Microsoft Most Valuable Professional – Data Platform Development
Olá Fernando, ótimo assunto este de transação.
Recentemente desenvolvi um WCF para controle de credito centralizado.
Para tal utilizei TransactionScope, o interessante que utilizei tanto no client como no WCF com EF.
Fiz teste com SQL Server e Oracle, tanto para o client como para o WCF e funcionou perfeitamente utilizando window 7 64bit no client e Windows Server 2008 no WCF.
Quando o pessoal da qualidade testou utilizando o Windows XP 32bit o esquema não funcionou, retorna mensagem informando que é necessário habilitar o controle de transação no Windows.
Procurei por todo lado na net como resolver e ainda não obtive uma solução.
Olá Leandro!
Tudo beleza?
O TransactionScope realmente é muito bacana de ser utilizado.
Para habilitá-lo faça assim:
– Abra o Painel de Controles, e vá em Ferramentas Administrativas
– Acesse o atalho de “Component Services”
– Expanda a árvore “Computers”
– Clique com o botão direito em “My Computer” e clique em “Properties”
– Vá até a aba “MSDTC”
– Marque a opção “Use local coordinator”
Obrigado por comentar!
[]s!