Aplica-se a: Framework 1.0/1.1/C#
Nos dias de hoje, todas pessoas que trabalham com informática, sabem de uma maneira geral, que aplicativos de computador manipulam dados.
Pra falar verdade, COMPUTADORES manipulam dados.
Que tipo de manipulação é esta? Em geral, inserções, alterações e exclusões de dados..
Agora, para os desenvolvedores é relativamente fácil escrever códigos que façam isto (ainda mais com o Microsoft Visual Studio .NET), mas existe uma questão que é negligenciada e por isto não deixam as aplicações 100% íntegras.
A questão dita acima é o conceito de "Transações nos Bancos de Dados".
1 - Explanação do conceito de Transação
1.1 - EXISTE UM PROBLEMA...
Quando um aplicativo tenta executar um comando em um banco de dados, este comando pode não ser executado de maneira correta, por alguns dos simples motivos abaixo:
Erro de parse em todo o comando. A sintaxe SQL (Structured Query Language) não estava correta.
Erro de parse em apenas uma parte do comando.
Execução parcial. Um dos recursos do banco de dados ficou indisponível durante a execução do comando, não permitindo que todo o comando seja executado.
Quando ocorre um erro, e este impossibilita a total execução do comando/conjunto de comandos, temos um erro não tão crítico quanto um erro que impossibilita APENAS UMA PARTE do comando de ser executado.
Porque ?
Por que se apenas uma parte dos dados que deveriam ser alterados, foram de fato alterados então temos uma inconsistência dos dados no banco. Uma inconsistência de dados vai totalmente contra a regra de negócio estabelecida para a aplicação.
É por isto que eu digo "quando nada é alterado, é melhor", pois a regra de negócios do aplicativo não foi burlada, trazendo resultados inimagináveis para o usuário do aplicativo.
Não conheço exemplo melhor do que a transferência de dinheiro de uma conta para outra.
Suponha que o João entre no Internet Banking e vá transferir R$ 200,00 para a conta do Pedro.
Esta operação envolve 2 tarefas:
a) Debitar R$ 200,00 na conta do João.
b) Creditar R$ 200,00 na conta do Pedro.
Para que a operação seja bem sucedida, as 2 tarefas devem ser executadas. Imagine as consequências se apenas A fosse executada, ou se apenas B.
Esta situação fica ainda pior, quando os comandos viram conjunto de comandos, ou seja, linhas e mais linhas de códigos SQL que dependem uma da outra, sendo executadas uma após a outra.
Veja um exemplo simples e hipotético envolvendo uma transação de dados como nas tarefas acima citadas. Transações bancárias acontecem todos os dias e em milhares de vezes.
Aqui criamos um métodos em C# que retorna um valor boolean indicando se a operação foi ou não executada com sucesso.
public bool EfetuarLançamento(string cliente_origem,string cliente_destino, decimal valor)
{
SqlConnection conn = new SqlConnection(cnstring);
conn.Open();
SqlTransaction trans = conn.BeginTransaction();
bool ok;
try
{
SqlCommand cmd1 = conn.CreateCommand();
cmd1.CommandText = "update conta set saldo= saldo +" & valor & "where cliente_destino='" cliente_destino + "'";
cmd1.Connection = conn;
cmd1.CommandType = CommandType.Text;
cmd1.Transaction = trans;
cmd1.ExecuteNonQuery();
cmd1.CommandText = "update conta set saldo = saldo -" & valor & "where cliente_origem='" cliente_origem + "'";
cmd1.ExecuteNonQuery();
trans.Commit();
ok=true;
}
catch (SqlException ex)
{
trans.Rollback();
Response.Write ("Ocorre um erro na operação " + ex.Message);
ok=false;
}
finally
{
conn.Close();
conn.Dispose();
}
return ok;
}
No método instanciamos uma variável do tipo SqlConnection para efetuarmos a conexão com a base de dados. Em seguida um objeto de transação foi criado através do método BeginTransaction do objeto Connection e atribuímos a uma variável do tipo SqlTransaction.
Na sequencia criamos um objeto comando responsável por enviar e executar os comandos sql no banco de dados. Observe que passamos ao objeto Command a transação que criamos através da propriedade Transaction do objeto Command. Assim a manipulação dos dados estará envolvida em uma transação. O primeiro comando executado faz o lançamento de crédito na conta de destino, logo após redefinimos o comando sql do objeto Command para efetuar o segundo comando que é efetuar o lançamento de débito na conta de origem.
Todo o código está envolvido em um try/catch, método de tratamento de erros do framework. Se toda a operação, ou seja os dois comandos forem executados com sucesso no banco de dados a operação é concluída e o método Commit da transação é executado retornando um valor verdadeiro com sinal de operação com sucesso.
Caso contrário, por exemplo o primeiro comando for executado com sucesso e o segundo comando falhar, o bloco de tratamento de erros interceptará o algoritmo e será executado o bloco catch chamando o método RollBack do objeto Transaction. Com isso a transação toda é cancelada, portanto, as duas contas permanecerão inalteradas, evitando "discussão" no banco por parte dos clientes se é que podemos dizer assim. Nesse caso, o método acima retornará false indicando que houve falha na operação. O Bloco finnaly é utilizado para efetuar a liberação de recursos, fechamento de conexões. Esse bloco sempre é executado indepentemente de falha ou sucesso da operação.
No framework 2.0 temos a classe TransactionScope que efetua o controle de transações. Veremos em um proxímo post.
Dessa forma a operação fica assegurada pela transação que ela será totalmente executada com exito ou nao executada em caso de falhas.
Posted
29-12-2006 3:06
por
Ubirajara Castro