Transação (Transaction): um conjunto de operações SQL executadas de forma atômica. Ou todas são confirmadas com Commit, ou todas são desfeitas em caso de falha com Rollback, atendendo às propriedades ACID.
No .NET Framework/.NET a implementação é feita com SqlTransaction, exclusivo para o SQL Server.
Pontos importantes do fluxo:
- Abrir a conexão com o banco de dados
conn.Open()- Iniciar a transação pela conexão
conn.BeginTransaction()- Vincular o objeto de transação na propriedade
SqlCommand.Transaction- Executar várias instruções SQL em lote
- Sem exceções:
tx.Commit(); capturar exceções:tx.Rollback()
Exemplo
O código original não tem a lógica de rollback dentro do bloco catch. Veja a versão completa e funcional:
public static int ExecuteSqlTran(List<string> SQLStringList)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
int count = 0;
for (int n = 0; n < SQLStringList.Count; n++)
{
string strsql = SQLStringList[n];
if (strsql.Trim().Length > 1)
{
cmd.CommandText = strsql;
count += cmd.ExecuteNonQuery();
}
}
tx.Commit(); // Confirma a transação se tudo der certo
return count;
}
catch (Exception ex)
{
tx.Rollback(); // Desfaz tudo se qualquer instrução falhar
throw ex; // Relança a exceção para que camadas superiores saibam da falha
}
}
}Code language: C# (cs)
Essa versão monta as instruções SQL concatenando strings e possui risco de injeção de SQL. Ela serve para estudar o funcionamento das transações, mas em projetos de produção dê preferência às consultas parametrizadas.
Exemplo de transação com consultas parametrizadas
Inserimos dados dentro de um laço, usando SqlParameter para evitar injeções. Pontos cruciais:
- No fim de cada repetição chame
sqlCom.Parameters.Clear()para remover parâmetros antigos e evitar erros de acúmulo - Reutilize a mesma instância do objeto
SqlCommand, alterando apenas CommandText e os parâmetros
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = conn;
SqlTransaction st = conn.BeginTransaction();
sqlCom.Transaction = st;
int res = 0;
try
{
for (int i = 0; i < 10; i++)
{
sqlCom.CommandText = "INSERT INTO [dbo].[Article]([Title])VALUES(@Title)";
SqlParameter param = new SqlParameter("@Title", SqlDbType.NVarChar, 250);
param.Value = i + "Este é um novo título" + DateTime.Now;
sqlCom.Parameters.Add(param);
res += sqlCom.ExecuteNonQuery();
sqlCom.Parameters.Clear(); // Limpa os parâmetros para a próxima iteração
}
st.Commit();
}
catch (Exception)
{
st.Rollback(); // Reverte todas as alterações se ocorrer uma exceção
}Code language: C# (cs)
1. Regras obrigatórias que devem ser seguidas
- O método
BeginTransaction()deve ser chamado após abrir a conexão com Open - É preciso definir a propriedade
TransactionnoSqlCommand, senão uma exceção será lançada - Depois de chamar
Commit()ouRollback()a transação é encerrada automaticamente, não faça chamadas repetidas
Observações adicionais
- No .NET Core/.NET 5+, recomenda‑se usar o bloco
usingpara liberar oSqlTransactionde forma automática - Para cenários com grandes volumes de SQL em lote, considere usar
SqlBulkCopy, ele tem desempenho superior ao ExecuteNonQuery dentro de laços - O
SqlTransactionnão suporta transações entre bancos diferentes. Para isso você vai precisar de transações distribuídas comTransactionScope
SqlTransaction transações de banco de dados