Vous consultez une page technique concernant le logiciel de gestion Mercator. Celle-ci contient des informations spécifiques destinées aux professionnels de Mercator. Souhaitez-vous être redirigés vers des informations plus générales ?


   Ne plus poser cette question

Accès asynchrone à la base de données SQL avec l'API de Mercator

0000003158     -      17/04/2023

Mercator dispose depuis toujours d'une API permettant d'accéder facilement à sa base de données SQL :

  • MercatorSqlConnection : se connecter à la base de données,
  • Zselect, Zselect<T> et ZselectDirect : obtenir des données,
  • SqlExec : exécuter un script SQL,
  • BulkDataTable : uploader vers le serveur SQL via bulk une DataTable.

Toutes ces classes et méthodes fonctionnent en mode synchrone. Cela induit que durant la communication avec le serveur SQL, le thread de base de Mercator est bloqué. Pour l'utilisateur, cela signifie que l'interface apparaît comme "gelée". Cela peut être source d'inconfort si le délai d'attente imposé à l'utilisateur est long.

Ces méthodes existent dans l'API de Mercator en mode asynchrone. Elles sont donc awaitables, ce qui leur permet de poursuivre leur exécution alors que l'utilisateur récupère la main sur le thread de base. En d'autres termes, l'utilisateur peut continuer à faire autre chose dans Mercator, par exemple cliquer sur un bouton "Annuler" qui va interrompre le processus. Il est aussi recommandé d'utiliser ces méthodes dans des environnements nativement asynchrones tels que ASP.net core.

L'API de Mercator propose une extension (présente dans MercatorExtensions de MercatorTunnel.dll) sur System.Data.SqlClient.SqlConnection afin de faciliter la connexion asynchrone.

Zoom
using (SqlConnection conn = new SqlConnection())
{
    var t = await conn.ConnectToMercatorAsync(MercatorUi.Globals.RepData);
    if (!t.Result)
    {
         MercatorUi.Dialogs.Stop(t.Error);
        return;
    }
    ...
}

 

Ces méthodes asynchrones existent dans l'API de Mercator :

  • ZselectAsyncZselectAsync<T> et ZselectDirectAsync : obtenir des données,
  • SqlExecAsync : exécuter un script SQL,
  • BulkDataTableAsync : uploader vers le serveur SQL via bulk une DataTable.

Contrairement à leur équivalent synchrone, ces méthodes n'affichent pas de boîte de dialogue en cas d'erreur. De même, leur résultat est toujours une classe comprenant ces propriétés

  • Error : chaîne d'erreur, le cas échéant
  • Result : le résultat si pas d'erreur (DataSet pour un SqlExecAsync, par exemple)

Cette classe contient aussi une méthode Zstoperror qui permet d'afficher la boîte de dialogue habituelle en cas d'erreur.

Exemple d'utilisation :

Zoom
RetZselectAsync retZselectAsync = await Api.ZselectAsync(MercatorUi.Globals.RepData, reqSql);
if (retZselectAsync.Result == null)
{
    retZselectAsync.Zstoperror();
    return;
}
DataSet dataSet = retZselectAsync.Result;
...

 

Ces méthodes acceptent aussi un paramètre CancellationTokenSource qui permet d'effectuer une demande d'interruption du processus en cours. Cette demande se fait via :

Zoom
cancellationTokenSource.Cancel();

Note : CancellationTokenSource est IDisposable. Il est recommandé de l'utiliser dans un bloc using.


Dans bien des cas, il est souhaitable de ne pas bloquer le thread de base mais aussi de ne pas permettre à l'utilisateur d'effectuer d'autres manipulations que d'interrompre le processus qui vient d'être déclenché. En effet, les fonctionnements asynchrones peuvent parfois produire des comportements inattendus si le code mis en place ne gère pas toutes ces situations correctement. (Par exemple, l'écran qui devra recevoir les résultats obtenus par ZselectAsync n'existe plus car l'utilisateur l'a fermé).

Afin de faciliter cela, Mercator met à disposition cette classe : 

MercatorUi._BaseClasses.ExclusiveBackgroundWorkerAsync

Son constructeur reçoit ces paramètres :

  • actionAsync : de type Func<Task>, qui contient l'action asynchrone à exécuter
  • formWhereToCenterLoadingCircle : fenêtre qui servira de repère pour le centrage d'un loadingCircle. Ce paramètre est optionnel. S'il n'est pas défini, le centrage sera effectué sur la fenêtre principale de Mercator
  • cancellationTokenSource : ce paramètre est optionnel. S'il est passé, un bouton "Annuler" sera présent sous le loadingCircle. Il permettra à l'utilisateur d'interrompre le processus.

Ci-dessous un exemple d'implémentation :

Zoom
using (System.Threading.CancellationTokenSource cancellationTokenSource = new System.Threading.CancellationTokenSource())
{
    RetZselectAsync r = null;
    MercatorUi._BaseClasses.ExclusiveBackgroundWorkerAsync ebwa = new MercatorUi._BaseClasses.ExclusiveBackgroundWorkerAsync(
                async () => r = await Api.ZselectAsync(Globals.RepData, reqSql, cancellationTokenSource: cancellationTokenSource),
                sigForm,
                cancellationTokenSource: cancellationTokenSource);
    if (cancellationTokenSource.IsCancellationRequested)
    {
        // l'utilisateur à cliqué sur Annuler
        return;
    }
    else if (r == null)
    {
        // une exception s'est produite dans le code de l'actionAsync
        Dialogs.Stop(ebwa.ExceptionDuringDoWork?.Message ?? "Unknown error!");
        return;
    }
    else if (r.Result == null)
    {
        // une erreur SQL s'est produite
        r.Zstoperror();
        return;
    }
    else
    {
        // tout va bien !
        DataSet dataSet = r.Result;
    }
}

 

Note : cet exemple peut être utilisé pour d'autres opérations asynchrones que l'accès à la base de données. Par exemple, pour l'utilisation d'un webservice.