Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Utilisation de la réplication SQL dans le code .NET d'une application mobile - Implémentation & Conseils : PARTIE 2/3 (Synchronisation asynchrone)

Dans la première partie de cet article, nous avons vu de quelle manière s’utilisent les propriétés et les méthodes de la classe SqlCeReplication. Nous avons notamment écrit un simple code C# qui permet de répliquer une base SQL Server Compact à partir d’une base sur un serveur.

Cette seconde partie de l’article traite de l’exécution de la réplication SQL en mode asynchrone.

L’utilisation de la méthode Synchronize (dans la partie 1) nous a permis de répliquer les données en mode synchrone (c'est-à-dire que l’exécution de cette méthode est bloquante pour l’utilisateur).

Il est tout à fait possible d’exécuter la réplication SQL d’une manière asynchrone (interface graphique de l’utilisateur non bloqué durant l’exécution de la réplication).

Certes, le code .NET associé à la réplication en asynchrone est plus lourd et plus complexe à écrire, mais le développeur peut en tirer des avantages précieux surtout en terme de reporting. En effet, l’exécution de la réplication en asynchrone permet de récupérer des informations utiles comme le nom des tables en cours de synchronisation, ou encore de connaitre le pourcentage d’avancement de la réplication…

 

Méthode BeginSynchronize et delegates :

Pour répliquer de manière asynchrone, il est nécessaire de faire appel à la méthode BeginSynchronize au lieu de la méthode Synchronize.

La méthode BeginSynchronize peut prendre en argument plusieurs “delegates” qui vont permettre au développeur de récupérer différents évènements liés à la phase de réplication :

// (...)
repl.BeginSynchronize(new AsyncCallback(SyncCompletedCallback),
                      new OnStartTableUpload(TableUploadCallback),
                      new OnStartTableDownload(TableDownloadCallback),
                      new OnSynchronization(SynchronizingCallback), repl);
// (...)
  • La première delegate de type AsyncCallback notifie que la phase de réplication est achevée. C’est d’ailleurs dans la méthode associée à cette delegate (ici : SyncCompletedCallback) qu’il faudra nécessairement faire appel à la méthode EndSynchronize de l’objet SqlCeReplication.
  • La seconde, de type OnStartTableUpload, est levée à chaque fois qu’une table est en cours d’upload. Elle doit absolument recevoir une chaine de caractères en argument qui est destinée à contenir le nom de la table répliquée.
  • La troisième quant à elle, est levée quand une table est en cours de download. Elle doit absolument recevoir une chaine de caractères en argument qui est destinée à contenir le nom de la table répliquée.
  • Enfin, la dernière permet de notifier régulièrement le pourcentage d’avancement de la réplication.

Il est donc nécessaire d’écrire convenablement les 4 méthodes delegates associées, en respectant la bonne signature :

private void SyncCompletedCallback(IAsyncResult ar)
{
}

private void TableUploadCallback(IAsyncResult ar, string tableName)
{
}

private void TableDownloadCallback(IAsyncResult ar, string tableName)
{
}

private void SynchronizingCallback(IAsyncResult ar, int percent)
{
}

 

Obtention du statut de la réplication & Threads :

Désormais nous pouvons connaitre le pourcentage d’avancement ainsi que les tables en cours de réplication, durant le processus de synchronisation.

Nous pouvons ainsi rajouter un label sur la “form” Windows Mobile destiné à afficher ces différentes informations à l’utilisateur :

image

Comme dans tous les traitements asynchrones, la réplication SQL lancée via l’instruction BeginSynchronize se déroulera dans un thread détaché de l’interface graphique (= processus en arrière-plan). Aussi, il faudra faire appel aux méthodes Invoke pour que le thread de la réplication puisse accéder et mettre à jour les informations du label créé.

Dans un premier temps, nous créons donc un EventHandler updateStatusHandler que nous instancions dans le constructeur de la “form” en précisant la méthode qui va permettre de mettre à jour le statut à l’écran. La variable globale _currentStatus contiendra simplement le statut courant de la réplication, c'est-à-dire le texte à afficher dans le label :

private EventHandler updateStatusHandler;
private string _currentStatus = String.Empty;

public Form1()
{
     InitializeComponent();
     this.updateStatusHandler = new EventHandler(UpdateStatus);
}

private void UpdateStatus(object sender, System.EventArgs e)
{
     this.lblStatus.Text = _currentStatus;
}

Ensuite, il ne reste plus qu’à compléter les différentes delegates présentées précédemment, c’est à dire de mettre à jour le contenu de la variable _currentStatus et de faire un “Invoke” de la delegate updateStatusHandler pour rafraichir les informations :

private void SyncCompletedCallback(IAsyncResult ar)
{
     try
     {
         SqlCeReplication repl = (SqlCeReplication)ar.AsyncState;
         repl.EndSynchronize(ar);
         _currentStatus = "Synchro OK !";
         this.Invoke(updateStatusHandler);
     }
     catch (SqlCeException ex)
     {
         MessageBox.Show(ex.Message);
     }
}

private void TableUploadCallback(IAsyncResult ar, string tableName)
{
     _currentStatus = "Upload table : " + tableName;
     this.Invoke(updateStatusHandler);
}

private void TableDownloadCallback(IAsyncResult ar, string tableName)
{
     _currentStatus = "Download table : " + tableName;
     this.Invoke(updateStatusHandler);
}

private void SynchronizingCallback(IAsyncResult ar, int percent)
{
     _currentStatus = "Synchro : " + percent.ToString() + "%";
     this.Invoke(updateStatusHandler);
}

 

Au final, le code complet de la réplication est le suivant :

using System;
using System.Data.SqlServerCe;
using System.Windows.Forms;

namespace TestReplication2
{
    public partial class Form1 : Form
    {
        private const string _dbName = "technomade_mob.sdf";
        private EventHandler updateStatusHandler;
        private string _currentStatus = String.Empty;

        public Form1()
        {
            InitializeComponent();
            this.updateStatusHandler = new EventHandler(UpdateStatus);
        }

        private void UpdateStatus(object sender, System.EventArgs e)
        {
            this.lblStatus.Text = _currentStatus;
        }

        private void btnSynchro_Click(object sender, EventArgs e)
        {
            SqlCeReplication repl = null;
            try
            {
                repl = new SqlCeReplication();
                repl.InternetUrl = "http://10.10.10.10/webSync/sqlcesa35.dll";
                repl.InternetLogin = "login";
                repl.InternetPassword = "password";
                repl.Publisher = @"INSTANCE_SQL";
                repl.PublisherDatabase = "BASE";
                repl.PublisherSecurityMode = SecurityType.NTAuthentication;
                repl.Publication = "Pub";
                repl.Subscriber = "PDA1";
                repl.HostName = "PDA2";
                repl.SubscriberConnectionString = @"Data Source=" + _dbName
                      + ";Max Database Size=1000;Default Lock Escalation =100;";

                if (!System.IO.File.Exists(_dbName))
                    repl.AddSubscription(AddOption.CreateDatabase);

                repl.BeginSynchronize(new AsyncCallback(SyncCompletedCallback),
                      new OnStartTableUpload(TableUploadCallback),
                      new OnStartTableDownload(TableDownloadCallback),
                      new OnSynchronization(SynchronizingCallback), repl);
            }
            catch (SqlCeException ex)
            {
                // Catch !
            }
            finally
            {
                if (repl != null)
                    repl.Dispose();
            }
        }

        private void SyncCompletedCallback(IAsyncResult ar)
        {
            try
            {
                SqlCeReplication repl = (SqlCeReplication)ar.AsyncState;
                repl.EndSynchronize(ar);
                _currentStatus = "Synchro OK !";
                this.Invoke(updateStatusHandler);
            }
            catch (SqlCeException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void TableUploadCallback(IAsyncResult ar, string tableName)
        {
            _currentStatus = "Upload table : " + tableName;
            this.Invoke(updateStatusHandler);
        }

        private void TableDownloadCallback(IAsyncResult ar, string tableName)
        {
            _currentStatus = "Download table : " + tableName;
            this.Invoke(updateStatusHandler);
        }

        private void SynchronizingCallback(IAsyncResult ar, int percent)
        {
            _currentStatus = "Synchro : " + percent.ToString() + "%";
            this.Invoke(updateStatusHandler);
        }
    }

}

A noter qu’il aurait également été plus astucieux de créer une enumeration des différents statuts de la phase de réplication, au lieu d’utiliser une variable globale de type string.

A suivre…

Pi-R.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

jeudi 10 mars 2011 11:22 by Sofiane1405

# re: Utilisation de la réplication SQL dans le code .NET d'une application mobile - Implémentation & Conseils : PARTIE 2/3 (Synchronisation asynchrone)

Je commence par remercier Mr Cambier pour son excellent livre sur la réplication par fusion

J'attire votre attention sur la présence d'une erreur dans le code de la réplication Asynchrone. En effet, le block finally (try, catch puis finally) doit être retiré. Cela est dû au fait que la variable sqlCeReplication repl à été fermé dans la méthode SyncCompletedCallBack(IAsyncResult ar). La non suppression du block Finally génère une exception SqlCeException.

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01