Partager via


Procédure : configurer et exécuter la synchronisation collaborative (non-SQL Server)

Notes

Les rubriques de cette section de la documentation, Synchronisation d'autre bases de données compatibles ADO.NET, montrent comment les bases de données autres que SQL Server peuvent être synchronisées à l'aide de Sync Framework. Dans cette version, SQL Server est utilisé dans les exemples de code, mais le code peut être utilisé pour d'autres bases de données compatibles ADO.NET, en apportant certaines modifications aux objets SQL Server (tels que SqlConnection) et aux requêtes SQL présentées. Pour plus d'informations sur la synchronisation SQL Server, consultez Procédure : configurer et exécuter la synchronisation de bases de données (SQL Server).

Cette rubrique décrit les parties clés d'une application qui utilise Sync Framework pour synchroniser plusieurs bases de données. Le code dans cette application est axé sur les classes Sync Framework suivantes :

Pour plus d'informations sur le mode d'exécution d'un exemple de code, consultez « Exemples d'application dans les rubriques de procédures » dans Synchronisation d'autre bases de données compatibles ADO.NET.

Comme décrit dans Architecture et classes pour la synchronisation de base de données, la synchronisation peut se produire entre deux instances de DbSyncProvider, deux instances de SqlCeSyncProvider ou une instance de chaque. L'exemple de code de cette rubrique est tiré d'une application à deux couches et ne montre donc pas la synchronisation de deux instances de SqlCeSyncProvider, qui requiert une configuration multicouche. Pour obtenir un exemple d'une configuration multicouche, consultez l'exemple WebSharingAppDemo-CEProviderEndToEnd inclus avec le Kit de développement logiciel (SDK) Sync Framework.

La configuration de la synchronisation comprend les étapes suivantes :

  1. Création de tables de suivi, pour stocker des métadonnées, et de procédures stockées pour mettre à jour des données et des métadonnées dans chaque base de données serveur. Pour plus d'informations, consultez Procédure : approvisionner une base de données serveur pour la synchronisation collaborative (non-SQL Server).

  2. Initialisation de chaque base de données serveur avec le schéma, les données et l'infrastructure de suivi des modifications.

L'exécution de la synchronisation comprend les étapes suivantes :

  1. Création d'adaptateurs de synchronisation serveur, d'un fournisseur de synchronisation serveur et d'un fournisseur de synchronisation client.

  2. Initialisation de chaque base de données client.

  3. Création d'un orchestrateur de synchronisation et synchronisation des nœuds.

Initialisation d'une base de données serveur

L'initialisation d'une base de données implique la copie dans chaque base de données du schéma de table et de l'infrastructure de suivi des modifications, et de toutes les données initiales requises. Pour les bases de données synchronisées à l'aide de DbSyncProvider, Sync Framework ne crée pas automatiquement le schéma de table ou l'infrastructure de suivi dans chaque base de données. Une application doit s'assurer que ces objets existent avant d'essayer de synchroniser des nœuds. Vous pouvez utiliser une sauvegarde et une restauration ou toute autre technologie pour copier les objets sur chaque nœud, mais vous ne devez effectuer cette opération que si les modifications ne concernent pas la base de données à partir de laquelle la sauvegarde est effectuée. Si la première base de données est en cours d'utilisation et en cours de mise à jour, il est recommandé de copier uniquement le schéma et l'infrastructure de suivi des modifications sur chaque nœud et d'utiliser Sync Framework pour copier les données. Si vous copiez les données en utilisant une autre méthode, un nœud peut manquer des modifications validées sur le premier nœud. Sync Framework peut initialiser les données sur chaque nœud tant qu'au moins un nœud a les données. L'exemple de code de cette rubrique utilise cette approche : chaque base de données contient deux tables, mais seules celles de SyncSamplesDb_Peer1 contiennent des données. Les données sont copiées vers les autres nœuds pendant la première session de synchronisation.

Pour plus d'informations sur les options d'initialisation pour SqlCeSyncProvider, consultez Initialisation d'une base de données client, plus loin dans cette rubrique.

Exécution de la synchronisation

Les exemples de code de cette section sont divisés en plusieurs catégories :

Adaptateur de synchronisation

Les propriétés suivantes sont définies sur l'objet DbSyncAdapter pour chaque table.

Propriété de l'adaptateur de synchronisation

Utilisation

RowIdColumns

Spécifie les colonnes de clé primaire pour la table. Par exemple, si une table Customer a une clé primaire CustomerId, le code doit ressembler à ce qui suit : adapter.RowIdColumns.Add(CustomerId).

SelectIncrementalChangesCommand

Sélectionne toutes les modifications depuis la précédente session de synchronisation en joignant la table de base et sa table de suivi des modifications. Il s'agit de l'ensemble maximal de modifications pouvant être synchronisé. Cet ensemble de modifications peut être réduit lorsque la connaissance de synchronisation est prise en compte.

InsertCommand, UpdateCommand et DeleteCommand

Appliquent à un nœud les insertions, mises à jour et suppressions sélectionnées sur un autre nœud. Ces modifications ont été sélectionnées en utilisant la requête ou la procédure spécifiée pour la propriété SelectIncrementalChangesCommand.

InsertMetadataCommand, UpdateMetadataCommand et DeleteMetadataCommand

Mettent à jour les tables de suivi des modifications sur chaque nœud pour refléter les modifications qui ont été sélectionnées sur un nœud et appliquées à l'autre. Ces mises à jour permettent à Sync Framework de suivre où et quand les modifications se sont produites.

SelectRowCommand

Sélectionne les métadonnées d'une ligne qui est en conflit pendant la synchronisation.

SelectMetadataForCleanupCommand

Sélectionne les métadonnées qui peuvent être nettoyées sur un nœud. Le nettoyage est en général basé sur la rétention : les métadonnées sont conservées pendant une durée donnée. Toutefois, une application peut utiliser une autre logique pour déterminer quand nettoyer les métadonnées. Pour plus d'informations, consultez Procédure : nettoyer les métadonnées pour la synchronisation collaborative (non-SQL Server).

Les exemples de code de cette section créent des commandes que l'objet DbSyncAdapter exécute pendant la synchronisation. Ces commandes appellent les procédures stockées décrites dans Procédure : approvisionner une base de données serveur pour la synchronisation collaborative (non-SQL Server). Le jeu complet de commandes est inclus dans l'exemple de code complet proposé à la fin de cette rubrique. Dans le code, les paramètres se présentant sous la forme "@" + DbSyncSession sont des variables de session. Par exemple, "@" + DbSyncSession.SyncMinTimestamp est une variable résolue en valeur @sync\_min\_timestamp. Pour plus d'informations, consultez Procédure : utiliser des variables de session pour la synchronisation collaborative (non-SQL Server).

Code d'application pour SelectIncrementalChangesCommand

SqlCommand chgsCustomerCmd = new SqlCommand();
chgsCustomerCmd.CommandType = CommandType.StoredProcedure;
chgsCustomerCmd.CommandText = "Sync.sp_Customer_SelectChanges";
chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncInitialize, SqlDbType.Int);

adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd;
Dim chgsCustomerCmd As New SqlCommand()
With chgsCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_SelectChanges"
    .Parameters.Add("@" & DbSyncSession.SyncMetadataOnly, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncInitialize, SqlDbType.Int)
End With

adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd
Dim chgsCustomerCmd As New SqlCommand()
With chgsCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_SelectChanges"
    .Parameters.Add("@" & DbSyncSession.SyncMetadataOnly, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncInitialize, SqlDbType.Int)
End With

adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd

Code d'application pour UpdateCommand

SqlCommand updCustomerCmd = new SqlCommand();
updCustomerCmd.CommandType = CommandType.StoredProcedure;
updCustomerCmd.CommandText = "Sync.sp_Customer_ApplyUpdate";
updCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
updCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
updCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
updCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

adapterCustomer.UpdateCommand = updCustomerCmd;
Dim updCustomerCmd As New SqlCommand()
With updCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_ApplyUpdate"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
    .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
    .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
    .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
    .Parameters.Add("@" & DbSyncSession.SyncForceWrite, SqlDbType.Int)
End With

adapterCustomer.UpdateCommand = updCustomerCmd
Dim updCustomerCmd As New SqlCommand()
With updCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_ApplyUpdate"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
    .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
    .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
    .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
    .Parameters.Add("@" & DbSyncSession.SyncForceWrite, SqlDbType.Int)
End With

adapterCustomer.UpdateCommand = updCustomerCmd

Code d'application pour UpdateMetadataCommand

SqlCommand updMetadataCustomerCmd = new SqlCommand();
updMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
updMetadataCustomerCmd.CommandText = "Sync.sp_Customer_UpdateMetadata";
updMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd;
Dim updMetadataCustomerCmd As New SqlCommand()
With updMetadataCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_UpdateMetadata"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncCreatePeerKey, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncRowIsTombstone, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
End With

adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd
Dim updMetadataCustomerCmd As New SqlCommand()
With updMetadataCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_UpdateMetadata"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncCreatePeerKey, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt)
    .Parameters.Add("@" & DbSyncSession.SyncRowIsTombstone, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
    .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
End With

adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd

Code d'application pour SelectRowCommand

SqlCommand selRowCustomerCmd = new SqlCommand();
selRowCustomerCmd.CommandType = CommandType.StoredProcedure;
selRowCustomerCmd.CommandText = "Sync.sp_Customer_SelectRow";
selRowCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
selRowCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);

adapterCustomer.SelectRowCommand = selRowCustomerCmd;
Dim selRowCustomerCmd As New SqlCommand()
With selRowCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_SelectRow"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
End With

adapterCustomer.SelectRowCommand = selRowCustomerCmd
Dim selRowCustomerCmd As New SqlCommand()
With selRowCustomerCmd
    .CommandType = CommandType.StoredProcedure
    .CommandText = "Sync.sp_Customer_SelectRow"
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
End With

adapterCustomer.SelectRowCommand = selRowCustomerCmd

Lorsque toutes les commandes ont été définies, l'adaptateur est ajouté au fournisseur à l'aide du code suivant.

sampleProvider.SyncAdapters.Add(adapterCustomer);

Fournisseur de synchronisation serveur

Les propriétés suivantes sont définies sur l'objet DbSyncProvider pour le serveur.

Propriété du fournisseur de synchronisation

Utilisation

ScopeName

Nom des étendues à synchroniser pour une session particulière. Chaque étendue doit déjà exister dans la table d'informations d'étendue de la base de données serveur.

Connection

Connexion via laquelle Sync Framework sélectionne et applique des modifications à la base de données serveur.

SelectNewTimestampCommand

Retourne une valeur d'horodateur utilisée pour sélectionner et appliquer des ensembles de modifications à chaque base de données. Au cours de la session de synchronisation active, la commande fournit une nouvelle valeur d'horodateur. Les modifications apportées après la valeur d'horodateur de la session de synchronisation précédente et avant la nouvelle valeur d'horodateur sont synchronisées. La nouvelle valeur est ensuite stockée et utilisée comme point de départ pour la session suivante.

SelectTableMaxTimestampsCommand

Sélectionne l'horodateur maximal de chaque table de base ou de suivi pour déterminer si la destination de chaque table dispose déjà de toutes les modifications de la source. Si la destination dispose déjà des modifications, Sync Framework peut souvent éviter d'exécuter des requêtes d'énumération, ce qui peut améliorer les performances.

Cette propriété est facultative ; si une commande n'est pas spécifiée, les requêtes d'énumération sont exécutées pour toutes les tables.

SelectScopeInfoCommand

Retourne les informations de la table d'informations d'étendue, telle que la connaissance de synchronisation et la connaissance de nettoyage que Sync Framework requiert.

UpdateScopeInfoCommand

Met à jour les informations dans la table d'informations d'étendue.

SelectOverlappingScopesCommand

Retourne le nom d'étendue et le nom de table de toutes les tables de l'étendue spécifiée qui sont également incluses dans d'autres étendues.

UpdateScopeCleanupTimestampCommand

Met à jour la colonne scope_cleanup_timestamp pour une étendue particulière dans la table d'informations d'étendue, afin de marquer le point jusqu'auquel le nettoyage a été effectué pour l'étendue. Pour plus d'informations, consultez Procédure : nettoyer les métadonnées pour la synchronisation collaborative (non-SQL Server).

Les exemples de code de cette section créent des commandes que l'objet DbSyncProvider exécute pendant la synchronisation. Les commandes pour les propriétés SelectScopeInfoCommand et UpdateScopeInfoCommand sont incluses dans l'exemple de code complet à la fin de cette rubrique.

Code d'application pour ScopeName et Connection

L'exemple de code suivant définit le nom de l'étendue à synchroniser et la connexion sur laquelle effectuer la synchronisation. Dans l'exemple de code complet, l'objet DbSyncProvider contient un objet DbSyncAdapter pour la table Customer. Lorsque cette table est synchronisée pour la première fois, la définition de l'étendue Sales est définie. Une fois qu'une étendue a été synchronisée, il est préférable de ne pas la modifier. La modification de tables dans l'étendue ou dans les clauses de filtrage de ces tables peut en effet aboutir à une non-convergence des données.

SqlConnection peerConnection = new SqlConnection(peerConnString);
sampleDbProvider.Connection = peerConnection;
sampleDbProvider.ScopeName = "Sales";
Dim peerConnection As New SqlConnection(peerConnString)
sampleDbProvider.Connection = peerConnection
sampleDbProvider.ScopeName = "Sales"
Dim peerConnection As New SqlConnection(peerConnString)
sampleDbProvider.Connection = peerConnection
sampleDbProvider.ScopeName = "Sales"

Pour plus d'informations sur les étendues, consultez « Définir les étendues à synchroniser » dans Procédure : approvisionner une base de données serveur pour la synchronisation collaborative (non-SQL Server).

Code d'application pour SelectNewTimestampCommand

L'exemple de code suivant crée une commande pour la propriété SelectNewTimestampCommand.

SqlCommand selectNewTimestampCommand = new SqlCommand();
string newTimestampVariable = "@" + DbSyncSession.SyncNewTimestamp;
selectNewTimestampCommand.CommandText = "SELECT " + newTimestampVariable + " = min_active_rowversion() - 1";
selectNewTimestampCommand.Parameters.Add(newTimestampVariable, SqlDbType.Timestamp);
selectNewTimestampCommand.Parameters[newTimestampVariable].Direction = ParameterDirection.Output;

sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand;
Dim selectNewTimestampCommand As New SqlCommand()
Dim newTimestampVariable As String = "@" & DbSyncSession.SyncNewTimestamp
With selectNewTimestampCommand
    .CommandText = "SELECT " & newTimestampVariable & " = min_active_rowversion() - 1"
    .Parameters.Add(newTimestampVariable, SqlDbType.Timestamp)
    .Parameters(newTimestampVariable).Direction = ParameterDirection.Output
End With

sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand
Dim selectNewTimestampCommand As New SqlCommand()
Dim newTimestampVariable As String = "@" & DbSyncSession.SyncNewTimestamp
With selectNewTimestampCommand
    .CommandText = "SELECT " & newTimestampVariable & " = min_active_rowversion() - 1"
    .Parameters.Add(newTimestampVariable, SqlDbType.Timestamp)
    .Parameters(newTimestampVariable).Direction = ParameterDirection.Output
End With

sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand

Code d'application pour SelectTableMaxTimestampsCommand

L'exemple de code suivant crée une commande pour la propriété SelectTableMaxTimestampsCommand. L'exemple de code complet présente des sessions de synchronisation dans lesquelles aucune nouvelle modification n'a été apportée aux nœuds. Dans ces sessions, l'appel à SelectTableMaxTimestampsCommand indique l'absence de modification de données à synchroniser, de sorte que SelectIncrementalChangesCommand n'est pas appelée.

SqlCommand selTableMaxTsCmd = new SqlCommand();
selTableMaxTsCmd.CommandType = CommandType.Text;
selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " +
                               "MAX(local_update_peer_timestamp) AS max_timestamp " +
                               "FROM Sync.Customer_Tracking";
sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd;
Dim selTableMaxTsCmd As New SqlCommand()
selTableMaxTsCmd.CommandType = CommandType.Text
selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " _
                             & "MAX(local_update_peer_timestamp) AS max_timestamp " _
                             & "FROM Sync.Customer_Tracking"
sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd
Dim selTableMaxTsCmd As New SqlCommand()
selTableMaxTsCmd.CommandType = CommandType.Text
selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " _
                             & "MAX(local_update_peer_timestamp) AS max_timestamp " _
                             & "FROM Sync.Customer_Tracking"
sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd

Fournisseur de synchronisation client

L'exemple de code suivant crée un objet SqlCeSyncProvider, définit l'étendue et la connexion, et inscrit des gestionnaires d'événements. Pour l'objet SqlCeSyncProvider, les adaptateurs sont privés et leurs commandes créées automatiquement selon le schéma initialisé dans la base de données client.

public SqlCeSyncProvider ConfigureCeSyncProvider(string sqlCeConnString)
{

    SqlCeSyncProvider sampleCeProvider = new SqlCeSyncProvider();

    //Set the scope name
    sampleCeProvider.ScopeName = "Sales";

    //Set the connection
    sampleCeProvider.Connection = new SqlCeConnection(sqlCeConnString);

    //Register event handlers

    //Register the BeginSnapshotInitialization event handler. 
    //It is called when snapshot initialization is about to begin
    //for a particular scope in a Compact database.
    sampleCeProvider.BeginSnapshotInitialization += new EventHandler<DbBeginSnapshotInitializationEventArgs>(sampleCeProvider_BeginSnapshotInitialization);

    //Register the EndSnapshotInitialization event handler. 
    //It is called when snapshot initialization has completed
    //for a particular scope in a Compact database.
    sampleCeProvider.EndSnapshotInitialization += new EventHandler<DbEndSnapshotInitializationEventArgs>(sampleCeProvider_EndSnapshotInitialization);

    return sampleCeProvider;
}
Public Function ConfigureCeSyncProvider(ByVal sqlCeConnString As String) As SqlCeSyncProvider

    Dim sampleCeProvider As New SqlCeSyncProvider()

    'Set the scope name 
    sampleCeProvider.ScopeName = "Sales"

    'Set the connection 
    sampleCeProvider.Connection = New SqlCeConnection(sqlCeConnString)

    'Register event handlers 

    'Register the BeginSnapshotInitialization event handler. 
    'It is called when snapshot initialization is about to begin 
    'for a particular scope in a Compact database. 
    AddHandler sampleCeProvider.BeginSnapshotInitialization, AddressOf sampleCeProvider_BeginSnapshotInitialization

    'Register the EndSnapshotInitialization event handler. 
    'It is called when snapshot initialization has completed 
    'for a particular scope in a Compact database. 
    AddHandler sampleCeProvider.EndSnapshotInitialization, AddressOf sampleCeProvider_EndSnapshotInitialization

    Return sampleCeProvider
End Function

Initialisation d'une base de données client

Avant qu'une base de données client SQL Server Compact puisse commencer à recevoir des modifications d'une autre base de données, la base de données client doit contenir des données de schéma de table et des données initiales, ainsi que l'infrastructure de suivi des modifications dont Sync Framework a besoin. Sync Framework offre deux moyens d'initialiser la base de données :

  • Initialisation complète de la base de données client en utilisant la base de données serveur

    Cela permet la création des tables dans la base de données client à l'aide des tables, colonnes et tables de description de types exposées par un objet DbSyncProvider qui se connecte à une instance de la base de données serveur. La base de données client est ensuite préparée pour la synchronisation et toutes les lignes de la base de données serveur sont téléchargées en tant qu'insertions incrémentielles. Ces insertions sont sélectionnées à partir du serveur en utilisant la requête ou la procédure spécifiée pour la propriété SelectIncrementalChangesCommand.

  • Initialisation de l'instantané de la base de données client en utilisant une base de données client préexistante

    L'initialisation de l'instantané est conçue pour réduire la durée d'initialisation d'une base de données client. Lorsqu'une base de données client a été initialisée à l'aide d'une initialisation complète, les bases de données suivantes peuvent être initialisées à l'aide d'un instantané de cette première base de données client. Un instantané est une base de données SQL Server Compact spécialement préparée qui contient le schéma de table, des données (facultatif) et l'infrastructure de suivi des modifications. Copiez cet instantané sur chaque client qui en a besoin. Pendant la première session de synchronisation pour un client, les métadonnées propres au client sont mises à jour et toutes les modifications apportées depuis la création de l'instantané sont téléchargées vers la base de données client.

Important

Des instantanés ne doivent être générés qu'en l'absence totale d'activité dans la base de données SQL Server Compact. Les opérations simultanées, quel que soit leur type, ne sont pas prises en charge pendant la génération de l'instantané.

L'exemple de code suivant effectue une initialisation complète. Le code vérifie si une base de données client requiert un schéma à chaque synchronisation. Si le fournisseur dispose d'une connexion à une base de données client qui a déjà été initialisée ou constitue une base de données par instantané, la méthode ScopeExists retourne true. Si la méthode retourne false, un schéma est récupéré et appliqué à la base de données client à l'aide de la méthode GetScopeDescription.

private void CheckIfProviderNeedsSchema(SqlCeSyncProvider providerToCheck, DbSyncProvider providerWithSchema)
{

    //If one of the providers is a SqlCeSyncProvider and it needs
    //to be initialized, retrieve the schema from the other provider
    //if that provider is a DbSyncProvider; otherwise configure a
    //DbSyncProvider, connect to the server, and retrieve the schema.
    if (providerToCheck != null)
    {
        SqlCeConnection ceConn = (SqlCeConnection)providerToCheck.Connection;
        SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
        string scopeName = providerToCheck.ScopeName;
        if (!ceConfig.ScopeExists(scopeName))
        {
            DbSyncScopeDescription scopeDesc = providerWithSchema.GetScopeDescription();
            ceConfig.PopulateFromScopeDescription(scopeDesc);
            ceConfig.Apply();
        }
    }

}
private void CheckIfProviderNeedsSchema(SqlCeSyncProvider providerToCheck, DbSyncProvider providerWithSchema)
{

    //If one of the providers is a SqlCeSyncProvider and it needs
    //to be initialized, retrieve the schema from the other provider
    //if that provider is a DbSyncProvider; otherwise configure a
    //DbSyncProvider, connect to the server, and retrieve the schema.
    if (providerToCheck != null)
    {
        SqlCeConnection ceConn = (SqlCeConnection)providerToCheck.Connection; 
        SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
        string scopeName = providerToCheck.ScopeName;
        if (!ceConfig.ScopeExists(scopeName))
        {
            DbSyncScopeDescription scopeDesc = providerWithSchema.GetScopeDescription();
            ceConfig.PopulateFromScopeDescription(scopeDesc);
            ceConfig.Apply();
        }
    }

 }
    Private Sub CheckIfProviderNeedsSchema(ByVal providerToCheck As SqlCeSyncProvider, _
                                           ByVal providerWithSchema As DbSyncProvider)

        'If one of the providers is a SqlCeSyncProvider and it needs 
        'to be initialized, retrieve the schema from the other provider 
        'if that provider is a DbSyncProvider; otherwise configure a 
        'DbSyncProvider, connect to the server, and retrieve the schema. 
        If providerToCheck IsNot Nothing Then
            Dim ceConn As SqlCeConnection = DirectCast(providerToCheck.Connection, SqlCeConnection)
            Dim ceConfig As New SqlCeSyncScopeProvisioning(ceConn)
            Dim scopeName As String = providerToCheck.ScopeName
            If Not ceConfig.ScopeExists(scopeName) Then
                Dim scopeDesc As DbSyncScopeDescription = providerWithSchema.GetScopeDescription()
                ceConfig.PopulateFromScopeDescription(scopeDesc)
                ceConfig.Apply()
            End If

        End If

    End Sub
End Class
    Private Sub CheckIfProviderNeedsSchema(ByVal providerToCheck As SqlCeSyncProvider, _
                                           ByVal providerWithSchema As DbSyncProvider)

        'If one of the providers is a SqlCeSyncProvider and it needs 
        'to be initialized, retrieve the schema from the other provider 
        'if that provider is a DbSyncProvider; otherwise configure a 
        'DbSyncProvider, connect to the server, and retrieve the schema. 
        If providerToCheck IsNot Nothing Then
            Dim ceConn As SqlCeConnection = DirectCast(providerToCheck.Connection, SqlCeConnection)
            Dim ceConfig As New SqlCeSyncScopeProvisioning(ceConn)
            Dim scopeName As String = providerToCheck.ScopeName
            If Not ceConfig.ScopeExists(scopeName) Then
                Dim scopeDesc As DbSyncScopeDescription = providerWithSchema.GetScopeDescription()
                ceConfig.PopulateFromScopeDescription(scopeDesc)
                ceConfig.Apply()
            End If

        End If

    End Sub
End Class

Le code suivant génère un instantané. Le code se connecte à une base de données SQL Server Compact qui vient d'être synchronisée à l'aide de la méthode ConfigureCESyncProvider de l'exemple. Une fois la connexion établie, la méthode GenerateSnapshot est appelée, ce qui crée une copie de la base de données d'origine. Lorsque, par la suite, le fournisseur client se connecte à cette nouvelle copie et que la synchronisation est exécutée, la base de données est initialisée.

//Second session: Synchronize two databases by using one instance of 
//DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
//database is initialized, it is copied by using GenerateSnapshot and then
//used for the third session.
sampleSyncAgent = new SampleSyncAgent(
                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1));
syncStatistics = sampleSyncAgent.Synchronize();
sampleStats.DisplayStats(syncStatistics, "initial");

//Copy the Compact database and save it as SyncSampleClient2.sdf.
SqlCeSyncStoreSnapshotInitialization snapshotInit = new SqlCeSyncStoreSnapshotInitialization();
snapshotInit.GenerateSnapshot(new SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf");

//Make a change that is synchronized during the third session.
Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer");

//Third session: Synchronize the new Compact database. The five rows  
//from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
//change is now downloaded to bring SyncSampleClient2.sdf up to date.
//SyncSampleClient2.sdf will get this row during the next round of
//synchronization sessions.
sampleSyncAgent = new SampleSyncAgent(
                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2));
syncStatistics = sampleSyncAgent.Synchronize();
sampleStats.DisplayStats(syncStatistics, "initial");
'Second session: Synchronize two databases by using one instance of 
'DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
'database is initialized, it is copied by using GenerateSnapshot and then 
'used for the third session. 
sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                      sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1))
syncStatistics = sampleSyncAgent.Synchronize()
sampleStats.DisplayStats(syncStatistics, "initial")

'Copy the Compact database and save it as SyncSampleClient2.sdf. 
Dim snapshotInit As New SqlCeSyncStoreSnapshotInitialization()
snapshotInit.GenerateSnapshot(New SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf")

'Make a change that is synchronized during the third session. 
Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer")

'Third session: Synchronize the new Compact database. The five rows 
'from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
'change is now downloaded to bring SyncSampleClient2.sdf up to date. 
'SyncSampleClient2.sdf will get this row during the next round of 
'synchronization sessions. 
sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                      sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2))
syncStatistics = sampleSyncAgent.Synchronize()
sampleStats.DisplayStats(syncStatistics, "initial")

Notes

Dans cet exemple, la base de données par instantané est générée alors que l'application principale est en cours d'exécution. Dans de nombreux cas, les instantanés sont générés en heures creuses et en dehors de toute autre activité de synchronisation.

Création d'un orchestrateur de synchronisation et synchronisation de bases de données

Le code décrit dans les sections précédentes de cette rubrique montre comment définir les propriétés nécessaires pour la synchronisation. Il faut maintenant synchroniser les nœuds. Les nœuds sont toujours synchronisés par paires, telles que SyncSamplesDb_Peer1 et SyncSampleCe2. Pour une application de production, une copie de l'application est généralement déployée sur chaque nœud pour qu'il soit possible de lancer la synchronisation depuis n'importe quel nœud.

L'exemple de code suivant crée une classe SampleSyncAgent qui dérive de SyncOrchestrator. Le constructeur SampleSyncAgent prend deux objets RelationalSyncProvider. Étant donné que DbSyncProvider et SqlCeSyncProvider dérivent de RelationalSyncProvider, un objet SampleSyncAgent peut synchroniser n'importe quelle combinaison des deux types de fournisseurs. Le code identifie le fournisseur local et le fournisseur distant. Il spécifie ensuite que les modifications doivent être téléchargées d'abord de la base de données distante vers la base de données locale, puis dans le sens inverse. Le code vérifie si l'un des fournisseurs est un objet SqlCeSyncProvider qui requiert l'initialisation du schéma pour une base de données client.

public class SampleSyncAgent : SyncOrchestrator
{
    public SampleSyncAgent(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
    {

        this.LocalProvider = localProvider;
        this.RemoteProvider = remoteProvider;
        this.Direction = SyncDirectionOrder.UploadAndDownload;

        //Check to see if any provider is a SqlCe provider and if it needs to
        //be initialized.
        CheckIfProviderNeedsSchema(localProvider as SqlCeSyncProvider, remoteProvider as DbSyncProvider);
        CheckIfProviderNeedsSchema(remoteProvider as SqlCeSyncProvider, localProvider as DbSyncProvider);

    }

    //For Compact databases that are not initialized with a snapshot,
    //get the schema and initial data from a server database.
    private void CheckIfProviderNeedsSchema(SqlCeSyncProvider providerToCheck, DbSyncProvider providerWithSchema)
    {

        //If one of the providers is a SqlCeSyncProvider and it needs
        //to be initialized, retrieve the schema from the other provider
        //if that provider is a DbSyncProvider; otherwise configure a
        //DbSyncProvider, connect to the server, and retrieve the schema.
        if (providerToCheck != null)
        {
            SqlCeConnection ceConn = (SqlCeConnection)providerToCheck.Connection; 
            SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
            string scopeName = providerToCheck.ScopeName;
            if (!ceConfig.ScopeExists(scopeName))
            {
                DbSyncScopeDescription scopeDesc = providerWithSchema.GetScopeDescription();
                ceConfig.PopulateFromScopeDescription(scopeDesc);
                ceConfig.Apply();
            }
        }

     }
}
Public Class SampleSyncAgent
    Inherits SyncOrchestrator
    Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)

        Me.LocalProvider = localProvider
        Me.RemoteProvider = remoteProvider
        Me.Direction = SyncDirectionOrder.UploadAndDownload

        'Check to see if any provider is a SqlCe provider and if it needs to 
        'be initialized. 
        CheckIfProviderNeedsSchema(TryCast(localProvider, SqlCeSyncProvider), _
                                   TryCast(remoteProvider, DbSyncProvider))
        CheckIfProviderNeedsSchema(TryCast(remoteProvider, SqlCeSyncProvider), _
                                   TryCast(localProvider, DbSyncProvider))

    End Sub

    'For Compact databases that are not initialized with a snapshot, 
    'get the schema and initial data from a server database. 
    Private Sub CheckIfProviderNeedsSchema(ByVal providerToCheck As SqlCeSyncProvider, _
                                           ByVal providerWithSchema As DbSyncProvider)

        'If one of the providers is a SqlCeSyncProvider and it needs 
        'to be initialized, retrieve the schema from the other provider 
        'if that provider is a DbSyncProvider; otherwise configure a 
        'DbSyncProvider, connect to the server, and retrieve the schema. 
        If providerToCheck IsNot Nothing Then
            Dim ceConn As SqlCeConnection = DirectCast(providerToCheck.Connection, SqlCeConnection)
            Dim ceConfig As New SqlCeSyncScopeProvisioning(ceConn)
            Dim scopeName As String = providerToCheck.ScopeName
            If Not ceConfig.ScopeExists(scopeName) Then
                Dim scopeDesc As DbSyncScopeDescription = providerWithSchema.GetScopeDescription()
                ceConfig.PopulateFromScopeDescription(scopeDesc)
                ceConfig.Apply()
            End If

        End If

    End Sub
End Class

Le code suivant configure des sessions de synchronisation en appelant ConfigureDbSyncProvider ou ConfigureCeSyncProvider, méthodes que l'exemple d'application utilise pour définir les propriétés de fournisseur appropriées pour chaque session. Le code appelle ensuite la méthode Synchronize de SampleSyncAgent pour synchroniser chaque paire de bases de données.

//The SampleStats class handles information from the SyncStatistics
//object that the Synchronize method returns.
SampleStats sampleStats = new SampleStats();

try
{
    //Initial synchronization. Instantiate the SyncOrchestrator
    //and call Synchronize.
    SampleSyncProvider sampleSyncProvider = new SampleSyncProvider();
    SyncOrchestrator sampleSyncAgent;
    SyncOperationStatistics syncStatistics;

    //First session: Synchronize two databases by using two instances 
    //of DbSyncProvider.
    sampleSyncAgent = new SampleSyncAgent(
                            sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                            sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2));
    syncStatistics = sampleSyncAgent.Synchronize();
    sampleStats.DisplayStats(syncStatistics, "initial");

    //Second session: Synchronize two databases by using one instance of 
    //DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
    //database is initialized, it is copied by using GenerateSnapshot and then
    //used for the third session.
    sampleSyncAgent = new SampleSyncAgent(
                            sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                            sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1));
    syncStatistics = sampleSyncAgent.Synchronize();
    sampleStats.DisplayStats(syncStatistics, "initial");

    //Copy the Compact database and save it as SyncSampleClient2.sdf.
    SqlCeSyncStoreSnapshotInitialization snapshotInit = new SqlCeSyncStoreSnapshotInitialization();
    snapshotInit.GenerateSnapshot(new SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf");

    //Make a change that is synchronized during the third session.
    Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer");

    //Third session: Synchronize the new Compact database. The five rows  
    //from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
    //change is now downloaded to bring SyncSampleClient2.sdf up to date.
    //SyncSampleClient2.sdf will get this row during the next round of
    //synchronization sessions.
    sampleSyncAgent = new SampleSyncAgent(
                            sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                            sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2));
    syncStatistics = sampleSyncAgent.Synchronize();
    sampleStats.DisplayStats(syncStatistics, "initial");
}


catch (DbOutdatedSyncException ex)
{
    Console.WriteLine("Outdated Knowledge: " + ex.OutdatedPeerSyncKnowledge.ToString() +
                      " Clean up knowledge: " + ex.MissingCleanupKnowledge.ToString());
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
'The SampleStats class handles information from the SyncStatistics 
'object that the Synchronize method returns. 
Dim sampleStats As New SampleStats()

Try
    'Initial synchronization. Instantiate the SyncOrchestrator 
    'and call Synchronize. 
    Dim sampleSyncProvider As New SampleSyncProvider()
    Dim sampleSyncAgent As SyncOrchestrator
    Dim syncStatistics As SyncOperationStatistics

    'First session: Synchronize two databases by using two instances 
    'of DbSyncProvider. 
    sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                          sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2))
    syncStatistics = sampleSyncAgent.Synchronize()
    sampleStats.DisplayStats(syncStatistics, "initial")

    'Second session: Synchronize two databases by using one instance of 
    'DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
    'database is initialized, it is copied by using GenerateSnapshot and then 
    'used for the third session. 
    sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                          sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1))
    syncStatistics = sampleSyncAgent.Synchronize()
    sampleStats.DisplayStats(syncStatistics, "initial")

    'Copy the Compact database and save it as SyncSampleClient2.sdf. 
    Dim snapshotInit As New SqlCeSyncStoreSnapshotInitialization()
    snapshotInit.GenerateSnapshot(New SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf")

    'Make a change that is synchronized during the third session. 
    Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer")

    'Third session: Synchronize the new Compact database. The five rows 
    'from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
    'change is now downloaded to bring SyncSampleClient2.sdf up to date. 
    'SyncSampleClient2.sdf will get this row during the next round of 
    'synchronization sessions. 
    sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                          sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2))
    syncStatistics = sampleSyncAgent.Synchronize()
    sampleStats.DisplayStats(syncStatistics, "initial")
Catch ex As DbOutdatedSyncException


    Console.WriteLine(("Outdated Knowledge: " & ex.OutdatedPeerSyncKnowledge.ToString() & " Clean up knowledge: ") _
                      & ex.MissingCleanupKnowledge.ToString())
Catch ex As Exception
    Console.WriteLine(ex.Message)
End Try

Exemple de code complet

L'exemple de code complet ci-dessous inclut les exemples de code décrits précédemment, ainsi que du code supplémentaire qui permet d'afficher des statistiques de synchronisation et des informations relatives aux événements. L'exemple requiert la classe Utility qui est disponible dans Classe d'utilitaire pour les rubriques de procédures sur le fournisseur de bases de données.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServerCe;

namespace Microsoft.Samples.Synchronization
{
    class Program
    {
        static void Main(string[] args)
        {

            //The Utility class handles all functionality that is not
            //directly related to synchronization, such as holding peerConnection 
            //string information and making changes to the server database.
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);

            //The SampleStats class handles information from the SyncStatistics
            //object that the Synchronize method returns.
            SampleStats sampleStats = new SampleStats();

            try
            {
                //Initial synchronization. Instantiate the SyncOrchestrator
                //and call Synchronize.
                SampleSyncProvider sampleSyncProvider = new SampleSyncProvider();
                SyncOrchestrator sampleSyncAgent;
                SyncOperationStatistics syncStatistics;

                //First session: Synchronize two databases by using two instances 
                //of DbSyncProvider.
                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "initial");

                //Second session: Synchronize two databases by using one instance of 
                //DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
                //database is initialized, it is copied by using GenerateSnapshot and then
                //used for the third session.
                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "initial");

                //Copy the Compact database and save it as SyncSampleClient2.sdf.
                SqlCeSyncStoreSnapshotInitialization snapshotInit = new SqlCeSyncStoreSnapshotInitialization();
                snapshotInit.GenerateSnapshot(new SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf");

                //Make a change that is synchronized during the third session.
                Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer");

                //Third session: Synchronize the new Compact database. The five rows  
                //from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
                //change is now downloaded to bring SyncSampleClient2.sdf up to date.
                //SyncSampleClient2.sdf will get this row during the next round of
                //synchronization sessions.
                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "initial");
            }


            catch (DbOutdatedSyncException ex)
            {
                Console.WriteLine("Outdated Knowledge: " + ex.OutdatedPeerSyncKnowledge.ToString() +
                                  " Clean up knowledge: " + ex.MissingCleanupKnowledge.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            //Make a change in one of the databases.
            Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync2, "Customer");

            try
            {
                //Subsequent synchronization. Changes are now synchronized between all
                //nodes.
                SampleSyncProvider sampleSyncProvider = new SampleSyncProvider();
                SyncOrchestrator sampleSyncAgent;
                SyncOperationStatistics syncStatistics;

                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "subsequent");

                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "subsequent");

                sampleSyncAgent = new SampleSyncAgent(
                                        sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1),
                                        sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2));
                syncStatistics = sampleSyncAgent.Synchronize();
                sampleStats.DisplayStats(syncStatistics, "subsequent");

            }


            catch (DbOutdatedSyncException ex)
            {
                Console.WriteLine("Outdated Knowledge: " + ex.OutdatedPeerSyncKnowledge.ToString() +
                                  " Clean up knowledge: " + ex.MissingCleanupKnowledge.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            //Return data back to its original state.
            Utility.CleanUpNode(Utility.ConnStr_DbSync1);
            Utility.CleanUpNode(Utility.ConnStr_DbSync2);

            //Exit.
            Console.Write("\nPress Enter to close the window.");
            Console.ReadLine();
        }

        //Create a class that is derived from 
        //Microsoft.Synchronization.SyncOrchestrator.
        public class SampleSyncAgent : SyncOrchestrator
        {
            public SampleSyncAgent(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
            {

                this.LocalProvider = localProvider;
                this.RemoteProvider = remoteProvider;
                this.Direction = SyncDirectionOrder.UploadAndDownload;

                //Check to see if any provider is a SqlCe provider and if it needs to
                //be initialized.
                CheckIfProviderNeedsSchema(localProvider as SqlCeSyncProvider, remoteProvider as DbSyncProvider);
                CheckIfProviderNeedsSchema(remoteProvider as SqlCeSyncProvider, localProvider as DbSyncProvider);

            }

            //For Compact databases that are not initialized with a snapshot,
            //get the schema and initial data from a server database.
            private void CheckIfProviderNeedsSchema(SqlCeSyncProvider providerToCheck, DbSyncProvider providerWithSchema)
            {

                //If one of the providers is a SqlCeSyncProvider and it needs
                //to be initialized, retrieve the schema from the other provider
                //if that provider is a DbSyncProvider; otherwise configure a
                //DbSyncProvider, connect to the server, and retrieve the schema.
                if (providerToCheck != null)
                {
                    SqlCeConnection ceConn = (SqlCeConnection)providerToCheck.Connection; 
                    SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
                    string scopeName = providerToCheck.ScopeName;
                    if (!ceConfig.ScopeExists(scopeName))
                    {
                        DbSyncScopeDescription scopeDesc = providerWithSchema.GetScopeDescription();
                        ceConfig.PopulateFromScopeDescription(scopeDesc);
                        ceConfig.Apply();
                    }
                }

             }
        }


        public class SampleSyncProvider
        {

            public SqlCeSyncProvider ConfigureCeSyncProvider(string sqlCeConnString)
            {

                SqlCeSyncProvider sampleCeProvider = new SqlCeSyncProvider();

                //Set the scope name
                sampleCeProvider.ScopeName = "Sales";

                //Set the connection
                sampleCeProvider.Connection = new SqlCeConnection(sqlCeConnString);

                //Register event handlers

                //Register the BeginSnapshotInitialization event handler. 
                //It is called when snapshot initialization is about to begin
                //for a particular scope in a Compact database.
                sampleCeProvider.BeginSnapshotInitialization += new EventHandler<DbBeginSnapshotInitializationEventArgs>(sampleCeProvider_BeginSnapshotInitialization);

                //Register the EndSnapshotInitialization event handler. 
                //It is called when snapshot initialization has completed
                //for a particular scope in a Compact database.
                sampleCeProvider.EndSnapshotInitialization += new EventHandler<DbEndSnapshotInitializationEventArgs>(sampleCeProvider_EndSnapshotInitialization);

                return sampleCeProvider;
            }

            public void sampleCeProvider_CreatingSchema(object sender, CreatingSchemaEventArgs e)
            {
                Console.WriteLine("Full Initialization Process Started.....");
                Console.WriteLine(
                    string.Format("CreatingSchame Event fired for Database {0}", e.Connection.Database)
                    );
            }

            public void sampleCeProvider_BeginSnapshotInitialization(object sender, DbBeginSnapshotInitializationEventArgs e)
            {
                Console.WriteLine("");
                Console.WriteLine("Snapshot initialization process started");
                Console.WriteLine(
                    string.Format("BeginSnapshotInitialization event fired for scope {0}", e.ScopeName)
                    );
            }

            public void sampleCeProvider_EndSnapshotInitialization(object sender, DbEndSnapshotInitializationEventArgs e)
            {
                Console.WriteLine("EndSnapshotInitialization event fired");

                Dictionary<string, DbSnapshotInitializationTableStatistics> tableStats = 
                    e.TableInitializationStatistics;

                foreach (string tableName in tableStats.Keys)
                {

                    DbSnapshotInitializationTableStatistics ts = tableStats[tableName];

                    Console.WriteLine("\tTable Name: " + tableName);

                    Console.WriteLine("\tTotal Rows: " + ts.TotalRows);

                    Console.WriteLine("\tRows Intialized: " + ts.RowsInitialized);

                    Console.WriteLine("\tStart Time: " + ts.StartTime);

                    Console.WriteLine("\tEnd Time: " + ts.EndTime);

                }

                Console.WriteLine("Snapshot initialization process completed");
                Console.WriteLine("");
            }

            public DbSyncProvider ConfigureDbSyncProvider(string peerConnString)
            {

                DbSyncProvider sampleDbProvider = new DbSyncProvider();

                SqlConnection peerConnection = new SqlConnection(peerConnString);
                sampleDbProvider.Connection = peerConnection;
                sampleDbProvider.ScopeName = "Sales";

                //Create a DbSyncAdapter object for the Customer table and associate it 
                //with the DbSyncProvider. Following the DataAdapter style in ADO.NET, 
                //DbSyncAdapter is the equivalent for synchronization. The commands that 
                //are specified for the DbSyncAdapter object call stored procedures
                //that are created in each peer database.
                DbSyncAdapter adapterCustomer = new DbSyncAdapter("Customer");


                //Specify the primary key, which Sync Framework uses
                //to identify each row during synchronization.
                adapterCustomer.RowIdColumns.Add("CustomerId");


                //Specify the command to select incremental changes.
                //In this command and other commands, session variables are
                //used to pass information at runtime. DbSyncSession.SyncMetadataOnly 
                //and SyncMinTimestamp are two of the string constants that
                //the DbSyncSession class exposes. You could also include 
                //@sync_metadata_only and @sync_min_timestamp directly in your 
                //queries:
                //*  sync_metadata_only is used by Sync Framework as an optimization
                //   in some queries.
                //* The value of the sync_min_timestamp session variable is compared to
                //   values in the sync_row_timestamp column in the tracking table to 
                //   determine which rows to select.
                SqlCommand chgsCustomerCmd = new SqlCommand();
                chgsCustomerCmd.CommandType = CommandType.StoredProcedure;
                chgsCustomerCmd.CommandText = "Sync.sp_Customer_SelectChanges";
                chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
                chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
                chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
                chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncInitialize, SqlDbType.Int);

                adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd;

                //Specify the command to insert rows.
                //The sync_row_count session variable is used in this command 
                //and other commands to return a count of the rows affected by an operation. 
                //A count of 0 indicates that an operation failed.
                SqlCommand insCustomerCmd = new SqlCommand();
                insCustomerCmd.CommandType = CommandType.StoredProcedure;
                insCustomerCmd.CommandText = "Sync.sp_Customer_ApplyInsert";
                insCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                insCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
                insCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
                insCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
                insCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

                adapterCustomer.InsertCommand = insCustomerCmd;


                //Specify the command to update rows.
                //The value of the sync_min_timestamp session variable is compared to
                //values in the sync_row_timestamp column in the tracking table to 
                //determine which rows to update.
                SqlCommand updCustomerCmd = new SqlCommand();
                updCustomerCmd.CommandType = CommandType.StoredProcedure;
                updCustomerCmd.CommandText = "Sync.sp_Customer_ApplyUpdate";
                updCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                updCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
                updCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
                updCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
                updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
                updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
                updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

                adapterCustomer.UpdateCommand = updCustomerCmd;


                //Specify the command to delete rows.
                //The value of the sync_min_timestamp session variable is compared to
                //values in the sync_row_timestamp column in the tracking table to 
                //determine which rows to delete.
                SqlCommand delCustomerCmd = new SqlCommand();
                delCustomerCmd.CommandType = CommandType.StoredProcedure;
                delCustomerCmd.CommandText = "Sync.sp_Customer_ApplyDelete";
                delCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
                delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
                delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

                adapterCustomer.DeleteCommand = delCustomerCmd;

                //Specify the command to select any conflicting rows.
                SqlCommand selRowCustomerCmd = new SqlCommand();
                selRowCustomerCmd.CommandType = CommandType.StoredProcedure;
                selRowCustomerCmd.CommandText = "Sync.sp_Customer_SelectRow";
                selRowCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                selRowCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);

                adapterCustomer.SelectRowCommand = selRowCustomerCmd;


                //Specify the command to insert metadata rows.
                //The session variables in this command relate to columns in
                //the tracking table.
                SqlCommand insMetadataCustomerCmd = new SqlCommand();
                insMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
                insMetadataCustomerCmd.CommandText = "Sync.sp_Customer_InsertMetadata";
                insMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);          
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
                insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

                adapterCustomer.InsertMetadataCommand = insMetadataCustomerCmd;


                //Specify the command to update metadata rows.
                SqlCommand updMetadataCustomerCmd = new SqlCommand();
                updMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
                updMetadataCustomerCmd.CommandText = "Sync.sp_Customer_UpdateMetadata";
                updMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
                updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

                adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd;

                //Specify the command to delete metadata rows.
                SqlCommand delMetadataCustomerCmd = new SqlCommand();
                delMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
                delMetadataCustomerCmd.CommandText = "Sync.sp_Customer_DeleteMetadata";
                delMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
                delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
                delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
                delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

                adapterCustomer.DeleteMetadataCommand = delMetadataCustomerCmd;


                //Add the adapter to the provider.
                sampleDbProvider.SyncAdapters.Add(adapterCustomer);


                // Configure commands that relate to the provider itself rather 
                // than the DbSyncAdapter object for each table:
                // * SelectNewTimestampCommand: Returns the new high watermark for 
                //   the current synchronization session.
                // * SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge, 
                //   and a scope version (timestamp).
                // * UpdateScopeInfoCommand: Sets new values for sync knowledge and cleanup knowledge.            
                // * SelectTableMaxTimestampsCommand (optional): Returns the maximum timestamp from each base table 
                //   or tracking table, to determine whether for each table the destination already 
                //   has all of the changes from the source. If a destination table has all the changes,
                //   SelectIncrementalChangesCommand is not called for that table.
                // There are additional commands related to metadata cleanup that are not 
                // included in this application.


                //Select a new timestamp.
                //During each synchronization, the new value and
                //the last value from the previous synchronization
                //are used: the set of changes between these upper and
                //lower bounds is synchronized.
                SqlCommand selectNewTimestampCommand = new SqlCommand();
                string newTimestampVariable = "@" + DbSyncSession.SyncNewTimestamp;
                selectNewTimestampCommand.CommandText = "SELECT " + newTimestampVariable + " = min_active_rowversion() - 1";
                selectNewTimestampCommand.Parameters.Add(newTimestampVariable, SqlDbType.Timestamp);
                selectNewTimestampCommand.Parameters[newTimestampVariable].Direction = ParameterDirection.Output;

                sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand;

                //Specify the command to select local replica metadata.
                SqlCommand selReplicaInfoCmd = new SqlCommand();
                selReplicaInfoCmd.CommandType = CommandType.Text;
                selReplicaInfoCmd.CommandText = "SELECT " +
                                                "scope_id, " +
                                                "scope_local_id, " +
                                                "scope_sync_knowledge, " +
                                                "scope_tombstone_cleanup_knowledge, " +
                                                "scope_timestamp " +
                                                "FROM Sync.ScopeInfo " +
                                                "WHERE scope_name = @" + DbSyncSession.SyncScopeName;
                selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);

                sampleDbProvider.SelectScopeInfoCommand = selReplicaInfoCmd;


                //Specify the command to update local replica metadata. 
                SqlCommand updReplicaInfoCmd = new SqlCommand();
                updReplicaInfoCmd.CommandType = CommandType.Text;
                updReplicaInfoCmd.CommandText = "UPDATE  Sync.ScopeInfo SET " +
                                                "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " +
                                                "scope_id = @" + DbSyncSession.SyncScopeId + ", " +
                                                "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                                "WHERE scope_name = @" + DbSyncSession.SyncScopeName + " AND " +
                                                " ( @" + DbSyncSession.SyncCheckConcurrency + " = 0 OR scope_timestamp = @" + DbSyncSession.SyncScopeTimestamp + "); " +
                                                "SET @" + DbSyncSession.SyncRowCount + " = @@rowcount";
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt);
                updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

                sampleDbProvider.UpdateScopeInfoCommand = updReplicaInfoCmd;


                //Return the maximum timestamp from the Customer_Tracking table.
                //If more tables are synchronized, the query should UNION
                //all of the results. The table name is not schema-qualified
                //in this case because the name was not schema qualified in the
                //DbSyncAdapter constructor.
                SqlCommand selTableMaxTsCmd = new SqlCommand();
                selTableMaxTsCmd.CommandType = CommandType.Text;
                selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " +
                                               "MAX(local_update_peer_timestamp) AS max_timestamp " +
                                               "FROM Sync.Customer_Tracking";
                sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd;

                return sampleDbProvider;
            }
        }

        //Handle the statistics that are returned by the SyncAgent.
        public class SampleStats
        {
            public void DisplayStats(SyncOperationStatistics syncStatistics, string syncType)
            {
                Console.WriteLine(String.Empty);
                if (syncType == "initial")
                {
                    Console.WriteLine("****** Initial Synchronization ******");
                }
                else if (syncType == "subsequent")
                {
                    Console.WriteLine("***** Subsequent Synchronization ****");
                }

                Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
                Console.WriteLine("Total Changes Uploaded: " + syncStatistics.UploadChangesTotal);
                Console.WriteLine("Total Changes Downloaded: " + syncStatistics.DownloadChangesTotal);
                Console.WriteLine("Complete Time: " + syncStatistics.SyncEndTime);
                Console.WriteLine(String.Empty);
            }
        }
    }
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlServerCe
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Data
Imports Microsoft.Synchronization.Data.SqlServerCe

Class Program

    Shared Sub Main(ByVal args As String())

        'The Utility class handles all functionality that is not 
        'directly related to synchronization, such as holding connection 
        'string information and making changes to the server database.
        Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
        Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)

        'The SampleStats class handles information from the SyncStatistics 
        'object that the Synchronize method returns. 
        Dim sampleStats As New SampleStats()

        Try
            'Initial synchronization. Instantiate the SyncOrchestrator 
            'and call Synchronize. 
            Dim sampleSyncProvider As New SampleSyncProvider()
            Dim sampleSyncAgent As SyncOrchestrator
            Dim syncStatistics As SyncOperationStatistics

            'First session: Synchronize two databases by using two instances 
            'of DbSyncProvider. 
            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "initial")

            'Second session: Synchronize two databases by using one instance of 
            'DbSyncProvider and one instance of SqlCeSyncProvider. After the Compact 
            'database is initialized, it is copied by using GenerateSnapshot and then 
            'used for the third session. 
            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "initial")

            'Copy the Compact database and save it as SyncSampleClient2.sdf. 
            Dim snapshotInit As New SqlCeSyncStoreSnapshotInitialization()
            snapshotInit.GenerateSnapshot(New SqlCeConnection(Utility.ConnStr_SqlCeSync1), "SyncSampleClient2.sdf")

            'Make a change that is synchronized during the third session. 
            Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync1, "Customer")

            'Third session: Synchronize the new Compact database. The five rows 
            'from SyncSampleClient1.sdf are already in SyncSampleClient2.sdf. The new 
            'change is now downloaded to bring SyncSampleClient2.sdf up to date. 
            'SyncSampleClient2.sdf will get this row during the next round of 
            'synchronization sessions. 
            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "initial")
        Catch ex As DbOutdatedSyncException


            Console.WriteLine(("Outdated Knowledge: " & ex.OutdatedPeerSyncKnowledge.ToString() & " Clean up knowledge: ") _
                              & ex.MissingCleanupKnowledge.ToString())
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try

        'Make a change in one of the databases. 
        Utility.MakeDataChangesOnNode(Utility.ConnStr_DbSync2, "Customer")

        Try
            'Subsequent synchronization. Changes are now synchronized between all 
            'nodes. 
            Dim sampleSyncProvider As New SampleSyncProvider()
            Dim sampleSyncAgent As SyncOrchestrator
            Dim syncStatistics As SyncOperationStatistics

            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync2))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "subsequent")

            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync1))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "subsequent")

            sampleSyncAgent = New SampleSyncAgent(sampleSyncProvider.ConfigureDbSyncProvider(Utility.ConnStr_DbSync1), _
                                                  sampleSyncProvider.ConfigureCeSyncProvider(Utility.ConnStr_SqlCeSync2))
            syncStatistics = sampleSyncAgent.Synchronize()
            sampleStats.DisplayStats(syncStatistics, "subsequent")

        Catch ex As DbOutdatedSyncException


            Console.WriteLine(("Outdated Knowledge: " & ex.OutdatedPeerSyncKnowledge.ToString() & " Clean up knowledge: ") _
                              & ex.MissingCleanupKnowledge.ToString())
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try

        'Return data back to its original state. 
        Utility.CleanUpNode(Utility.ConnStr_DbSync1)
        Utility.CleanUpNode(Utility.ConnStr_DbSync2)

        'Exit. 
        Console.Write(vbLf & "Press Enter to close the window.")
        Console.ReadLine()
    End Sub

    'Create a class that is derived from 
    'Microsoft.Synchronization.SyncOrchestrator. 
    Public Class SampleSyncAgent
        Inherits SyncOrchestrator
        Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)

            Me.LocalProvider = localProvider
            Me.RemoteProvider = remoteProvider
            Me.Direction = SyncDirectionOrder.UploadAndDownload

            'Check to see if any provider is a SqlCe provider and if it needs to 
            'be initialized. 
            CheckIfProviderNeedsSchema(TryCast(localProvider, SqlCeSyncProvider), _
                                       TryCast(remoteProvider, DbSyncProvider))
            CheckIfProviderNeedsSchema(TryCast(remoteProvider, SqlCeSyncProvider), _
                                       TryCast(localProvider, DbSyncProvider))

        End Sub

        'For Compact databases that are not initialized with a snapshot, 
        'get the schema and initial data from a server database. 
        Private Sub CheckIfProviderNeedsSchema(ByVal providerToCheck As SqlCeSyncProvider, _
                                               ByVal providerWithSchema As DbSyncProvider)

            'If one of the providers is a SqlCeSyncProvider and it needs 
            'to be initialized, retrieve the schema from the other provider 
            'if that provider is a DbSyncProvider; otherwise configure a 
            'DbSyncProvider, connect to the server, and retrieve the schema. 
            If providerToCheck IsNot Nothing Then
                Dim ceConn As SqlCeConnection = DirectCast(providerToCheck.Connection, SqlCeConnection)
                Dim ceConfig As New SqlCeSyncScopeProvisioning(ceConn)
                Dim scopeName As String = providerToCheck.ScopeName
                If Not ceConfig.ScopeExists(scopeName) Then
                    Dim scopeDesc As DbSyncScopeDescription = providerWithSchema.GetScopeDescription()
                    ceConfig.PopulateFromScopeDescription(scopeDesc)
                    ceConfig.Apply()
                End If

            End If

        End Sub
    End Class


    Public Class SampleSyncProvider

        Public Function ConfigureCeSyncProvider(ByVal sqlCeConnString As String) As SqlCeSyncProvider

            Dim sampleCeProvider As New SqlCeSyncProvider()

            'Set the scope name 
            sampleCeProvider.ScopeName = "Sales"

            'Set the connection 
            sampleCeProvider.Connection = New SqlCeConnection(sqlCeConnString)

            'Register event handlers 

            'Register the BeginSnapshotInitialization event handler. 
            'It is called when snapshot initialization is about to begin 
            'for a particular scope in a Compact database. 
            AddHandler sampleCeProvider.BeginSnapshotInitialization, AddressOf sampleCeProvider_BeginSnapshotInitialization

            'Register the EndSnapshotInitialization event handler. 
            'It is called when snapshot initialization has completed 
            'for a particular scope in a Compact database. 
            AddHandler sampleCeProvider.EndSnapshotInitialization, AddressOf sampleCeProvider_EndSnapshotInitialization

            Return sampleCeProvider
        End Function

        Public Sub sampleCeProvider_CreatingSchema(ByVal sender As Object, ByVal e As CreatingSchemaEventArgs)
            Console.WriteLine("Full initialization process started...")
            Console.WriteLine(String.Format("CreatingSchema event fired for database {0}", e.Connection.Database))
        End Sub

        Public Sub sampleCeProvider_BeginSnapshotInitialization(ByVal sender As Object, ByVal e As DbBeginSnapshotInitializationEventArgs)
            Console.WriteLine("")
            Console.WriteLine("Snapshot initialization process started...")
            Console.WriteLine(String.Format("BeginSnapshotInitialization event fired for scope {0}", e.ScopeName))
        End Sub

        Public Sub sampleCeProvider_EndSnapshotInitialization(ByVal sender As Object, ByVal e As DbEndSnapshotInitializationEventArgs)
            Console.WriteLine("EndSnapshotInitialization event fired")

            Dim tableStats As Dictionary(Of String, DbSnapshotInitializationTableStatistics) = e.TableInitializationStatistics

            For Each tableName As String In tableStats.Keys

                Dim ts As DbSnapshotInitializationTableStatistics = tableStats(tableName)

                Console.WriteLine(vbTab & "Table Name: " & tableName)

                Console.WriteLine(vbTab & "Total Rows: " & ts.TotalRows)

                Console.WriteLine(vbTab & "Rows Intialized: " & ts.RowsInitialized)

                Console.WriteLine(vbTab & "Start Time: " & ts.StartTime)

                Console.WriteLine(vbTab & "End Time: " & ts.EndTime)

            Next

            Console.WriteLine("Snapshot initialization process completed")
            Console.WriteLine("")
        End Sub

        Public Function ConfigureDbSyncProvider(ByVal peerConnString As String) As DbSyncProvider

            Dim sampleDbProvider As New DbSyncProvider()

            Dim peerConnection As New SqlConnection(peerConnString)
            sampleDbProvider.Connection = peerConnection
            sampleDbProvider.ScopeName = "Sales"

            'Create a DbSyncAdapter object for the Customer table and associate it 
            'with the DbSyncProvider. Following the DataAdapter style in ADO.NET, 
            'DbSyncAdapter is the equivalent for synchronization. The commands that 
            'are specified for the DbSyncAdapter object call stored procedures 
            'that are created in each peer database. 
            Dim adapterCustomer As New DbSyncAdapter("Customer")


            'Specify the primary key, which Sync Framework uses 
            'to identify each row during synchronization. 
            adapterCustomer.RowIdColumns.Add("CustomerId")


            'Specify the command to select incremental changes. 
            'In this command and other commands, session variables are 
            'used to pass information at runtime. DbSyncSession.SyncMetadataOnly 
            'and SyncMinTimestamp are two of the string constants that 
            'the DbSyncSession class exposes. You could also include 
            '@sync_metadata_only and @sync_min_timestamp directly in your 
            'queries: 
            '* sync_metadata_only is used by Sync Framework as an optimization 
            ' in some queries. 
            '* The value of the sync_min_timestamp session variable is compared to 
            ' values in the sync_row_timestamp column in the tracking table to 
            ' determine which rows to select. 
            Dim chgsCustomerCmd As New SqlCommand()
            With chgsCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_SelectChanges"
                .Parameters.Add("@" & DbSyncSession.SyncMetadataOnly, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncInitialize, SqlDbType.Int)
            End With

            adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd

            'Specify the command to insert rows. 
            'The sync_row_count session variable is used in this command 
            'and other commands to return a count of the rows affected by an operation. 
            'A count of 0 indicates that an operation failed. 
            Dim insCustomerCmd As New SqlCommand()
            With insCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_ApplyInsert"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
                .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
                .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
            End With

            adapterCustomer.InsertCommand = insCustomerCmd


            'Specify the command to update rows. 
            'The value of the sync_min_timestamp session variable is compared to 
            'values in the sync_row_timestamp column in the tracking table to 
            'determine which rows to update. 
            Dim updCustomerCmd As New SqlCommand()
            With updCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_ApplyUpdate"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
                .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
                .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
                .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
                .Parameters.Add("@" & DbSyncSession.SyncForceWrite, SqlDbType.Int)
            End With

            adapterCustomer.UpdateCommand = updCustomerCmd


            'Specify the command to delete rows. 
            'The value of the sync_min_timestamp session variable is compared to 
            'values in the sync_row_timestamp column in the tracking table to 
            'determine which rows to delete. 
            Dim delCustomerCmd As New SqlCommand()
            With delCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_ApplyDelete"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
                .Parameters.Add("@" & DbSyncSession.SyncForceWrite, SqlDbType.Int)
            End With

            adapterCustomer.DeleteCommand = delCustomerCmd

            'Specify the command to select any conflicting rows. 
            Dim selRowCustomerCmd As New SqlCommand()
            With selRowCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_SelectRow"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
            End With

            adapterCustomer.SelectRowCommand = selRowCustomerCmd


            'Specify the command to insert metadata rows. 
            'The session variables in this command relate to columns in 
            'the tracking table. 
            Dim insMetadataCustomerCmd As New SqlCommand()
            With insMetadataCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_InsertMetadata"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncCreatePeerKey, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowIsTombstone, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
            End With

            adapterCustomer.InsertMetadataCommand = insMetadataCustomerCmd


            'Specify the command to update metadata rows. 
            Dim updMetadataCustomerCmd As New SqlCommand()
            With updMetadataCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_UpdateMetadata"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncScopeLocalId, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncCreatePeerKey, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowIsTombstone, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
            End With

            adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd

            'Specify the command to delete metadata rows. 
            Dim delMetadataCustomerCmd As New SqlCommand()
            With delMetadataCustomerCmd
                .CommandType = CommandType.StoredProcedure
                .CommandText = "Sync.sp_Customer_DeleteMetadata"
                .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
            End With

            adapterCustomer.DeleteMetadataCommand = delMetadataCustomerCmd


            'Add the adapter to the provider. 
            sampleDbProvider.SyncAdapters.Add(adapterCustomer)


            ' Configure commands that relate to the provider itself rather 
            ' than the DbSyncAdapter object for each table: 
            ' * SelectNewTimestampCommand: Returns the new high watermark for 
            ' the current synchronization session. 
            ' * SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge, 
            ' and a scope version (timestamp). 
            ' * UpdateScopeInfoCommand: Sets new values for sync knowledge and cleanup knowledge. 
            ' * SelectTableMaxTimestampsCommand (optional): Returns the maximum timestamp from each base table 
            ' or tracking table, to determine whether for each table the destination already 
            ' has all of the changes from the source. If a destination table has all the changes, 
            ' SelectIncrementalChangesCommand is not called for that table. 
            ' There are additional commands related to metadata cleanup that are not 
            ' included in this application. 


            'Select a new timestamp. 
            'During each synchronization, the new value and 
            'the last value from the previous synchronization 
            'are used: the set of changes between these upper and 
            'lower bounds is synchronized. 
            Dim selectNewTimestampCommand As New SqlCommand()
            Dim newTimestampVariable As String = "@" & DbSyncSession.SyncNewTimestamp
            With selectNewTimestampCommand
                .CommandText = "SELECT " & newTimestampVariable & " = min_active_rowversion() - 1"
                .Parameters.Add(newTimestampVariable, SqlDbType.Timestamp)
                .Parameters(newTimestampVariable).Direction = ParameterDirection.Output
            End With

            sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand

            'Specify the command to select local replica metadata. 
            Dim selReplicaInfoCmd As New SqlCommand()
            With selReplicaInfoCmd
                .CommandType = CommandType.Text
                .CommandText = "SELECT " _
                             & "scope_id, " _
                             & "scope_local_id, " _
                             & "scope_sync_knowledge, " _
                             & "scope_tombstone_cleanup_knowledge, " _
                             & "scope_timestamp " _
                             & "FROM Sync.ScopeInfo " _
                             & "WHERE scope_name = @" + DbSyncSession.SyncScopeName
                .Parameters.Add("@" & DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100)
            End With

            sampleDbProvider.SelectScopeInfoCommand = selReplicaInfoCmd


            'Specify the command to update local replica metadata. 
            Dim updReplicaInfoCmd As New SqlCommand()
            With updReplicaInfoCmd
                .CommandType = CommandType.Text
                .CommandText = "UPDATE  Sync.ScopeInfo SET " _
                             & "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " _
                             & "scope_id = @" + DbSyncSession.SyncScopeId + ", " _
                             & "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " _
                             & "WHERE scope_name = @" + DbSyncSession.SyncScopeName + " AND " _
                             & " ( @" + DbSyncSession.SyncCheckConcurrency + " = 0 OR scope_timestamp = @" + DbSyncSession.SyncScopeTimestamp + "); " _
                             & "SET @" + DbSyncSession.SyncRowCount + " = @@rowcount"
                .Parameters.Add("@" & DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000)
                .Parameters.Add("@" & DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000)
                .Parameters.Add("@" & DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100)
                .Parameters.Add("@" & DbSyncSession.SyncCheckConcurrency, SqlDbType.Int)
                .Parameters.Add("@" & DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier)
                .Parameters.Add("@" & DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt)
                .Parameters.Add("@" & DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output
            End With

            sampleDbProvider.UpdateScopeInfoCommand = updReplicaInfoCmd


            'Return the maximum timestamp from the Customer_Tracking table. 
            'If more tables are synchronized, the query should UNION 
            'all of the results. The table name is not schema-qualified 
            'in this case because the name was not schema qualified in the 
            'DbSyncAdapter constructor. 
            Dim selTableMaxTsCmd As New SqlCommand()
            selTableMaxTsCmd.CommandType = CommandType.Text
            selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " _
                                         & "MAX(local_update_peer_timestamp) AS max_timestamp " _
                                         & "FROM Sync.Customer_Tracking"
            sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd

            Return sampleDbProvider
        End Function
    End Class

    'Handle the statistics that are returned by the SyncAgent. 
    Public Class SampleStats
        Public Sub DisplayStats(ByVal syncStatistics As SyncOperationStatistics, ByVal syncType As String)
            Console.WriteLine([String].Empty)
            If syncType = "initial" Then
                Console.WriteLine("****** Initial Synchronization ******")
            ElseIf syncType = "subsequent" Then
                Console.WriteLine("***** Subsequent Synchronization ****")
            End If

            Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
            Console.WriteLine("Total Changes Uploaded: " & syncStatistics.UploadChangesTotal)
            Console.WriteLine("Total Changes Downloaded: " & syncStatistics.DownloadChangesTotal)
            Console.WriteLine("Complete Time: " & syncStatistics.SyncEndTime)
            Console.WriteLine([String].Empty)
        End Sub
    End Class
End Class

Voir aussi

Autres ressources

Procédure : approvisionner une base de données serveur pour la synchronisation collaborative (non-SQL Server)

Synchronisation d'autre bases de données compatibles ADO.NET