Aller au contenu principal

21-4 Finalisation et transactions SQL en Dapper

Finalement, il ne nous reste qu'à sauvegarder l'information dans la base de données.

Tel qu'expliqué en introduction, on utilisera une transaction SQL pour effectuer cette sauvegarde. En effet, si la création de l'utilisateur ou encore de ses informations échoue, on ne veut pas laisser la base de données dans un état entre deux: on veut s'assurer que toutes les tâches requises ont été exécutées.

Créer une transaction avec Dapper est très simple.

Dans un premier temps, une fois qu'une connexion a été acquise via _dbContext.CreateConnection(), il ne suffit que d'ouvrir la connexion et d'appeler BeginTransaction():

using (var transaction = connection.BeginTransaction()) {}

On effectue ensuite les requêtes de BD normalement et on termine avec:

transaction.Commit();

Cette dernière commande exécute la transaction.

Dans le cas où une erreur surviendrait, on peut utiliser ceci pour revenir en arrière:

transaction.Rollback();

Adapter CreateAsync de UserRepository

Il nous faudra modifier la fonction de création d'utilisateurs dans UserRepository pour sauvegarder les informations associées. C'est tout là l'avantage d'avoir implémenté Identity nous-mêmes, il est très facile de modifier le comportement pour s'adapter à nos besoins.

  1. Ouvrir la connexion et débuter la transaction. Premièrement, on demande de débuter une transaction, à l'intérieur du using de connexion:

    Snowfall.Data/Repositories/UserRepository.cs
    public async Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
    {
    cancellationToken.ThrowIfCancellationRequested();

    using (var connection = _dbContext.CreateConnection())
    {
    using (var transaction = connection.BeginTransaction())
    {
    try
    {
    transaction.Commit();
    }
    catch (Exception e)
    {
    transaction.Rollback();
    return IdentityResult.Failed();
    }
    }
    }

    return IdentityResult.Success;
    }
    info

    Un second bloc using est utiliser pour la transaction. Le bloc using dispose des différentes ressources utilisées et ferme automatiquement la connexion en cas d'erreur.

    On peut débuter une transaction à partir d'une connexion à l'aide de connection.BeginTransaction(). Cela nous retourne une référence à la transaction.

    Ensuite, un bloc try/catch est utilisé pour la portion responsable d'effectuer les requêtes. Dans le cas d'une erreur, on voudra annuler la transaction via transaction.Rollback().

    Dans le cas où tout est OK, on effectue transaction.Commit() pour compléter la transaction et donc enregistrer les données dans la BD.

  2. Vérifier si des informations client sont présentes. Pour le reste de la fonction, presque rien ne change, on exécute l'insertion dans la BD et l’ID de l'utilisateur est retourné. On doit ensuite vérifier si des informations client sont présentes dans l'utilisateur qu'on désire sauvegarder. Si oui, on devra faire la sauvegarde de ces dernières. Cette vérification est importante puisqu'il n'est pas garanti que tous les utilisateurs auront des informations client. Dans le futur, un administrateur, par exemple, n'en aura pas.

    Snowfall.Data/Repositories/UserRepository.cs
    public async Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
    {
    cancellationToken.ThrowIfCancellationRequested();

    using (var connection = _dbContext.CreateConnection())
    {
    using (var transaction = connection.BeginTransaction())
    {
    try
    {
    string sql = @"
    INSERT INTO application_users (username, normalized_username, email,
    normalized_email, email_confirmed, password_hash, prenom, nom)
    VALUES (@UserName, @NormalizedUserName, @Email, @NormalizedEmail, @EmailConfirmed, @PasswordHash, @Prenom, @Nom)
    RETURNING id";

    user.Id = await connection.QuerySingleAsync<string>(sql, user, transaction: transaction);

    var informationClient = user.InformationClient;
    if (informationClient != null)
    {
    // On doit sauvegarder les infos client ici...
    }

    transaction.Commit();
    }
    catch (Exception e)
    {
    transaction.Rollback();
    return IdentityResult.Failed();
    }
    }
    }

    return IdentityResult.Success;
    }
    info

    Il y a un léger changement à la ligne:

    user.Id = await connection.QuerySingleAsync<string>(sql, user, transaction: transaction);

    En effet, un troisième paramètre transaction est apparu pour signifier que cette requête fait partie de la transaction en cours.

  3. Insérer les infos client. Finalement, si des informations client sont présentes, on peut en faire l'insertion.

    Snowfall.Data/Repositories/UserRepository.cs
    public async Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
    {
    cancellationToken.ThrowIfCancellationRequested();

    using (var connection = _dbContext.CreateConnection())
    {
    using (var transaction = connection.BeginTransaction())
    {
    try
    {
    string sql = @"
    INSERT INTO application_users (username, normalized_username, email,
    normalized_email, email_confirmed, password_hash, prenom, nom)
    VALUES (@UserName, @NormalizedUserName, @Email, @NormalizedEmail, @EmailConfirmed, @PasswordHash, @Prenom, @Nom)
    RETURNING id";

    user.Id = await connection.QuerySingleAsync<string>(sql, user, transaction: transaction);

    var informationClient = user.InformationClient;
    if (informationClient != null)
    {
    sql = @"
    INSERT INTO informations_client (utilisateur_id, adresse, ville, code_postal, province, pays)
    VALUES (@UtilisateurId, @Adresse, @Ville, @CodePostal, @Province, @Pays)
    RETURNING id";

    informationClient.UtilisateurId = user.Id;
    informationClient.Id =
    await connection.QuerySingleAsync<int>(sql, informationClient, transaction: transaction);
    }

    transaction.Commit();
    }
    catch (Exception e)
    {
    transaction.Rollback();
    return IdentityResult.Failed();
    }
    }
    }

    return IdentityResult.Success;
    }

Tester l'inscription

Testez le nouveau processus d'inscription et si tout se passe comme prévu, vous aurez une entrée dans informations_client avec les informations utilisateur entrées.

level up