Aller au contenu principal

⚠️ Transactions SQL

En SQL, une transaction est un ensemble d’opérations qui sont exécutées comme une seule unité logique. Cela signifie que toutes les opérations de la transaction doivent réussir pour être validées (commit), ou bien, si une des opérations échoue, on peut annuler (rollback) toutes les modifications pour revenir à l’état initial de la base de données. Les transactions garantissent ainsi l’intégrité et la cohérence des données.

Exemple en SQL pur

Supposons que nous ayons deux tables: Comptes et LogTransactions. Nous voulons transférer un montant de 100$ du compte A (id=1) vers le compte B (id=2), puis enregistrer cette opération dans la table LogTransactions. Nous voulons nous assurer que soit toutes les opérations réussissent, soit aucune ne prend effet si une erreur survient.

BEGIN TRANSACTION;

-- 1. Débiter le compte 1 de 100$
UPDATE Comptes
SET Solde = Solde - 100
WHERE Id = 1;

-- 2. Créditer le compte 2 de 100$
UPDATE Comptes
SET Solde = Solde + 100
WHERE Id = 2;

-- 3. Insérer un log de la transaction dans LogTransactions
INSERT INTO LogTransactions (CompteDebite, CompteCredite, Montant, DateTransaction)
VALUES (1, 2, 100, GETDATE());

-- Si tout s'est bien passé, on confirme la transaction
COMMIT TRANSACTION;
-- Si quelque chose se passe mal, on peut faire : ROLLBACK TRANSACTION;

Dans cet exemple, si l’une des opérations échoue, on peut exécuter un ROLLBACK TRANSACTION; pour annuler les modifications et revenir à l’état d’origine de la base de données.

Exemple avec Dapper

Nous allons faire la même opération avec Dapper. Le principe demeure le même: on ouvre une transaction, on exécute plusieurs requêtes, puis on décide de commit ou de rollback selon qu’une erreur s’est produite ou non.

using System;
using System.Data;
using System.Data.SqlClient;
using Dapper;

class ExempleTransactionsDapper
{
public void TransfertArgent()
{
string connectionString = "Votre_chaine_de_connexion_SQL";

using (IDbConnection connection = new SqlConnection(connectionString))
{
connection.Open();

// Commencer une transaction
using (var transaction = connection.BeginTransaction())
{
try
{
// 1. Débiter le compte 1
string sqlDebiter = "UPDATE Comptes SET Solde = Solde - @montant WHERE Id = @idCompte";
connection.Execute(sqlDebiter, new { montant = 100, idCompte = 1 }, transaction);

// 2. Créditer le compte 2
string sqlCrediter = "UPDATE Comptes SET Solde = Solde + @montant WHERE Id = @idCompte";
connection.Execute(sqlCrediter, new { montant = 100, idCompte = 2 }, transaction);

// 3. Insérer le log de transaction
string sqlLog = @"
INSERT INTO LogTransactions
(CompteDebite, CompteCredite, Montant, DateTransaction)
VALUES
(@debite, @credite, @montant, @dateTransac)";

connection.Execute(sqlLog, new
{
debite = 1,
credite = 2,
montant = 100,
dateTransac = DateTime.Now
}, transaction);

// Tout s'est bien passé, on valide (commit) la transaction
transaction.Commit();
}
catch
{
// En cas d'erreur, on annule (rollback) la transaction
transaction.Rollback();
throw; // Renvoyer l'exception pour gérer l'erreur dans la couche supérieure
}
}
}
}
}

En bref:

  1. On ouvre une transaction via BeginTransaction().
  2. Chaque requête passée à connection.Execute(...) ou connection.Query(...) utilise le même objet transaction pour faire partie du même lot d’opérations.
  3. Si tout se déroule correctement, on valide les changements avec transaction.Commit().
  4. Si un problème survient (exception, erreur quelconque), on annule avec transaction.Rollback().

Au final, le tout permet d'exécuter une série d'opérations SQL "dans un même bloc", en assurant que chaque élément de la chaine soit un succès.