Partager via


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 des bases de données SQL Server et SQL Server Compact. Cette application utilise plusieurs fonctionnalités avancées de Sync Framework qui ne sont pas requises pour la synchronisation de base, telles que l'ajout de plusieurs tables à une étendue, la définition d'un filtre de lignes pour l'une des tables et la définition d'un filtre de colonne pour l'autre table. 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 de SQL Server et SQL Server Compact.

Comme décrit dans Architecture et classes pour la synchronisation de base de données, la synchronisation peut se produire entre deux instances de SqlSyncProvider, 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.

Comparaison de types de fournisseurs

Cette rubrique explique comment synchroniser des bases de données SQL Server et SQL Server Compact à l'aide des deux fournisseurs de synchronisation introduits dans Sync Framework 2.0 : SqlSyncProvider et SqlCeSyncProvider. Sync Framework inclut d'autres fournisseurs qui peuvent synchroniser ces bases de données, mais les nouveaux fournisseurs sont généralement plus adaptés à la tâche pour les raisons suivantes :

  • SqlSyncProvider offre les mêmes possibilités que DbSyncProvider, mais requiert un code bien moins important et une maîtrise moins approfondie des requêtes que Sync Framework utilise pour synchroniser des données. DbSyncProvider convient toujours pour les bases de données non-SQL Server.

  • SqlSyncProvider et SqlCeSyncProvider peuvent être utilisés pour les topologies client-serveur, d'égal à égal et mixtes, alors que DbServerSyncProvider et SqlCeClientSyncProvider conviennent uniquement aux topologies client-serveur. SqlSyncProvider et SqlCeSyncProvider prennent également en charge des fonctionnalités plus évoluées, telles que le traitement par lot de modifications selon la taille des données au lieu du nombre de lignes.

  • SqlSyncProvider et SqlCeSyncProvider sont flexibles et faciles à configurer. Ils vous permettent de synchroniser toutes les éditions de SQL Server, notamment SQL Server Express et SQL Server Compact.

Configuration de nœuds et exécution de la synchronisation

La synchronisation des nœuds dans une topologie comprend deux phases : la configuration des nœuds à synchroniser et l'exécution proprement dite de la synchronisation entre deux nœuds. Pour SqlSyncProvider et SqlCeSyncProvider, la configuration des nœuds se compose de deux tâches :

  1. Définition de ce que vous voulez synchroniser

    Vous définissez ce que vous voulez synchroniser en décrivant une ou plusieurs étendues. Une étendue est un jeu de tables, qui peuvent être filtrées. Les tables peuvent déjà exister dans la base de données ou être décrites à l'aide d'objets Sync Framework, puis générées au moment de l'exécution lorsque le magasin sous-jacent est synchronisé. Pour plus d'informations, consultez « Présentation des étendues », plus loin dans cette rubrique.

    Important

    Lorsque des étendues ont fait l'objet d'une première synchronisation, les métadonnées qui leur sont associées doivent être supprimées et recréées si une étendue doit être modifiée.

  2. Approvisionnement de bases de données pour le suivi des modifications Sync Framework

    Après avoir décrit les tables et étendues, vous utilisez des objets Sync Framework pour appliquer des scripts d'approvisionnement à chaque nœud. Ces scripts créent une infrastructure de suivi des modifications et d'application des modifications qui est constituée de tables de métadonnées, de déclencheurs et de procédures stockées.

Les nœuds, une fois approvisionnés, peuvent être synchronisés. Du point de vue du développeur, définir des options de synchronisation et appeler Synchronize() ne présente pas de difficulté. En arrière-plan, Sync Framework utilise les informations de description des étendues et des tables que vous avez spécifiées afin de générer un objet de configuration pour chaque étendue et chaque adaptateur de synchronisation (un par table). Cela permet à Sync Framework de prendre les informations rendues persistantes dans chaque base de données et de générer les informations dont il a besoin pour chaque session de synchronisation entre deux nœuds. SqlSyncProvider et SqlCeSyncProvider sont tous deux informés des tables de suivi des modifications et autres objets qui ont été créées au cours de l'approvisionnement et génèrent automatiquement les objets DbSyncAdapter requis. Le code requis pour synchroniser des données à l'aide de ces fournisseurs s'en trouve considérablement réduit.

Le tableau suivant répertorie les classes utilisées pour configurer des bases de données et des fournisseurs.

SQL Server

SQL Server Compact

Description

DbSyncScopeDescription

DbSyncScopeDescription

Représente une étendue de synchronisation, qui est un regroupement logique de tables synchronisées en tant qu'unité.

SqlSyncScopeProvisioning

SqlCeSyncScopeProvisioning

Représente l'approvisionnement d'une base de données SQL Server ou SQL Server Compact pour une étendue donnée représentée par un objet DbSyncScopeDescription.

SqlSyncProviderScopeConfiguration

SqlCeSyncProviderScopeConfiguration

Représente les informations de configuration utilisées par SqlSyncProvider ou SqlCeSyncProvider pour une étendue donnée.

DbSyncTableDescription

DbSyncTableDescription

Représente le schéma d'une table incluse dans une étendue de synchronisation.

DbSyncColumnDescription

DbSyncColumnDescription

Représente les propriétés d'une colonne qui fait partie d'une table incluse dans une étendue de synchronisation.

SqlSyncDescriptionBuilder

SqlCeSyncDescriptionBuilder

Représente les informations d'étendue et de table pour une base de données SQL Server ou SQL Server Compact impliquée dans la synchronisation. Elle est utilisée pour extraire des objets Description d'une base de données SQL Server ou SQL Server Compact.

SqlSyncTableProvisioning

SqlSyncTableProvisioning

Représente l'approvisionnement d'une table (éventuellement avec des filtres) de base de données SQL Server ou SQL Server Compact représentée par un objet DbSyncTableDescription.

SqlSyncProviderAdapterConfiguration

SqlSyncProviderAdapterConfiguration

Représente les informations de configuration de l'adaptateur de synchronisation pour une table dans une base de données SQL Server ou SQL Server Compact.

En plus de ces types principaux, il existe quatre autres types importants à connaître :

Présentation des étendues

Notes

Cette section de la rubrique fournit des informations supplémentaires sur les étendues de synchronisation. Vous pouvez à ce stade aller directement aux « Exemples de code », mais nous vous recommandons de lire cette section si vous envisagez d'utiliser des étendues filtrées ou plusieurs étendues dans une application.

Il est essentiel de comprendre qu'une étendue est la combinaison de tables et de filtres. Par exemple, vous pouvez définir une étendue filtrée nommée sales-WA qui contient uniquement les données des ventes réalisées dans l'état de Washington de la table customer_sales. Si vous définissez un autre filtre sur la même table, comme sales-OR, il s'agit d'une autre étendue. Si vous définissez des filtres, sachez que Sync Framework ne gère pas automatiquement la suppression des lignes qui ne satisfont plus une condition de filtre. Par exemple, si un utilisateur ou une application met à jour une valeur dans une colonne utilisée pour le filtrage, une ligne passe d'une étendue à une autre. La ligne est envoyée à la nouvelle étendue à laquelle elle appartient désormais, mais n'est pas supprimée de l'ancienne étendue. Votre application doit gérer cette situation.

Des étendues peuvent être distinctes ou se chevaucher. Deux étendues se chevauchent si elles partagent des données communes. Par exemple, la table products peut être incluse dans une étendue sales et une étendue inventory. Les étendues peuvent à la fois se chevaucher et être filtrées. Les scénarios suivants illustrent des situations qui peuvent conjuguer chevauchement et filtrage :

  • Scénario 1 :

    • L'étendue 1 est sales-WA. Cette étendue comprend products, orders avec un filtre state=WA et order_details avec un filtre state=WA.

    • L'étendue 2 est sales-OR. Cette étendue comprend products, orders avec un filtre state=OR et order_details avec un filtre state=OR.

    Dans ce scénario, l'intégralité de la table products est partagée par les deux étendues. Les tables orders et order_details appartiennent aux deux étendues, mais les filtres ne se chevauchent pas, de sorte que les étendues ne partagent pas de lignes de ces tables.

  • Scénario 2 :

    • L'étendue 1 est sales-WA. Cette étendue comprend products, orders avec un filtre state=WA et order_details avec un filtre state=WA.

    • L'étendue 2 est sales-Northwest. Cette étendue comprend products, orders avec un filtre state=WA OR state=ID et shippers.

    Dans ce scénario, à nouveau, l'intégralité de la table products est partagée par les deux étendues. La table orders appartient aux deux étendues et les filtres se chevauchent : les deux étendues partagent les lignes qui satisfont aux critères du filtre state=WA. Les tables shippers et order_details ne sont pas partagées entre les étendues.

Il existe de nombreux moyens de définir des étendues, mais le principe suivant doit être respecté : toutes les données qui sont synchronisées entre deux bases de données dans la topologie de synchronisation ne peuvent appartenir qu'à une seule étendue. Par exemple, dans le scénario 2 ci-dessus, les bases de données A et B pourraient synchroniser l'étendue 1, et les bases de données A et C l'étendue 2. Les bases de données A et B ne peuvent pas aussi synchroniser l'étendue 2, car les lignes products et orders appartiennent aux deux étendues.

Exemples de code

Les exemples de code de cette section incluent bon nombre des objets décrits plus haut et couvrent les domaines suivants :

  • Description de l'étendue et des tables

  • Approvisionnement du serveur

  • Approvisionnement des clients

  • Définition des options de synchronisation

  • Synchronisation des nœuds

Après avoir abordé chacun de ces domaines, nous fournissons une application console complète qui combine chacun de ces exemples et du code supplémentaire qui permet de synchroniser une topologie à quatre nœuds. La topologie consiste en un serveur SQL Server, un client SQL Server et deux clients SQL Server Compact.

Description de l'étendue et des tables

L'exemple de code suivant décrit une étendue nommée filtered_customer et ajoute deux tables à l'étendue : Customer et CustomerContact Les tables existent déjà dans la base de données serveur, de sorte que la méthode GetDescriptionForTable est utilisée pour récupérer le schéma de la base de données serveur. Toutes les colonnes de la table Customer sont incluses, mais seules deux colonnes de la table CustomerContact sont incluses.

DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");

// Definition for Customer.
DbSyncTableDescription customerDescription =
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);

scopeDesc.Tables.Add(customerDescription);

// Definition for CustomerContact, including the list of columns to include.
Collection<string> columnsToInclude = new Collection<string>();
columnsToInclude.Add("CustomerId");
columnsToInclude.Add("PhoneType");
DbSyncTableDescription customerContactDescription =
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);

scopeDesc.Tables.Add(customerContactDescription);
Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")

' Definition for Customer. 
Dim customerDescription As DbSyncTableDescription = _
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)

scopeDesc.Tables.Add(customerDescription)


' Definition for CustomerContact, including the list of columns to include. 
Dim columnsToInclude As New Collection(Of String)()
columnsToInclude.Add("CustomerId")
columnsToInclude.Add("PhoneType")
Dim customerContactDescription As DbSyncTableDescription = _
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)

scopeDesc.Tables.Add(customerContactDescription)

Approvisionnement du serveur

L'exemple de code suivant crée un objet d'approvisionnement pour l'étendue filtered_customer et spécifie que tous les objets relatifs à la synchronisation doivent être créés dans un schéma de la base de données nommé "Sync". Dans le cadre de l'approvisionnement de l'étendue, le code définit un filtre sur la table Customer. Seules les lignes qui correspondent au filtre seront synchronisées. Le code permettant de définir un filtre doit se composer de deux parties. L'appel à AddFilterColumn spécifie que la colonne CustomerType est utilisée pour le filtrage et qu'elle doit être ajoutée à la table de suivi qui effectue le suivi des modifications pour la table Customer. L'appel à FilterClause spécifie le filtre lui-même. La clause de filtre est une clause WHERE sans le mot clé WHERE. L'alias [side] est un alias pour la table de suivi. Aucun filtre n'est défini sur la table CustomerContact ; par conséquent, toutes les lignes de cette table seront synchronisées. Lorsque les options d'approvisionnement ont été définies, la méthode Apply est appelée pour créer l'infrastructure de suivi des modifications dans la base de données serveur et le script d'approvisionnement est écrit dans un fichier.

SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(serverConn, scopeDesc);
serverConfig.ObjectSchema = "Sync";

// Specify which column(s) in the Customer table to use for filtering data, 
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";

// Configure the scope and change-tracking infrastructure.
serverConfig.Apply();

// Write the configuration script to a file. You can modify 
// this script if necessary and run it against the server
// to customize behavior.
File.WriteAllText("SampleConfigScript.txt",
    serverConfig.Script());
Dim serverConfig As New SqlSyncScopeProvisioning(serverConn, scopeDesc)
serverConfig.ObjectSchema = "Sync"

' Specify which column(s) in the Customer table to use for filtering data, 
' and the filtering clause to use against the tracking table. 
' "[side]" is an alias for the tracking table. 
serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"

' Configure the scope and change-tracking infrastructure. 
serverConfig.Apply()

' Write the configuration script to a file. You can modify 
' this script if necessary and run it against the server 
' to customize behavior. 
File.WriteAllText("SampleConfigScript.txt", serverConfig.Script())

Approvisionnement des clients

Dans cette application, les clients sont approvisionnés de deux façons différentes :

  • Initialisation complète d'une base de données client SQL Server ou SQL Server Compact selon les informations d'étendue récupérées auprès du serveur ou d'une autre base de données client.

    Les objets utilisateur et objets de synchronisation sont créés dans la base de données client selon les informations de schéma rendues disponibles par les objets SqlSyncDescriptionBuilder et SqlCeSyncDescriptionBuilder. Dans le cadre de la première session de synchronisation, la base de données client est préparée pour la synchronisation et toutes les lignes sont téléchargées dans la base de données client comme insertions incrémentielles.

  • Initialisation de l'instantané d'une base de données client SQL Server Compact 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 récupère d'abord les informations d'étendue du serveur et utilise la table de base et les schémas de suivi des modifications récupérés pour approvisionner une base de données client SQL Server Compact. Le code approvisionne ensuite une base de données client SQL Server selon les informations d'étendue de la base de données client SQL Server Compact.

// Create a SQL Server Compact database and provision it based on scope
// information that is retrieved from the server. Compact databases
// do not support separate schemas, so we prefix the name of all 
// synchronization-related objects with "Sync" so that they are easy to
// identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply();

// Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
// information that is retrieved from the SQL Server Compact database. We could
// have also retrieved this information from the server.
DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply();
' Create a SQL Server Compact database and provision it based on scope 
' information that is retrieved from the server. Compact databases 
' do not support separate schemas, so we prefix the name of all 
' synchronization-related objects with "Sync" so that they are easy to 
' identify. 
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
Dim clientSqlCe1Desc As DbSyncScopeDescription = _
    SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply()


' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope 
' information that is retrieved from the SQL Server Compact database. We could 
' have also retrieved this information from the server. 
Dim clientSqlDesc As DbSyncScopeDescription = _
    SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply()

L'exemple de code suivant génère un instantané nommé SyncSampleClient2.sdf à partir de la base de données SyncSampleClient1.sdf. Le code synchronise ensuite SyncSampleClient2.sdf avec la base de données serveur.

// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to 
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");

// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains 
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
    new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
    new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
    );
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
' Create a snapshot from the SQL Server Compact database, which will be used to 
' initialize a second Compact database. Again, this database could be provisioned 
' by retrieving scope information from another database, but we want to 
' demonstrate the use of snapshots, which provide a convenient deployment 
' mechanism for Compact databases. 
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")

' The new SQL Server Compact client synchronizes with the server, but 
' no data is downloaded because the snapshot already contains 
' all of the data from the first Compact database. 
syncOrchestrator = New SampleSyncOrchestrator( _
    New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
    New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")

Définition des options de synchronisation

L'exemple de code suivant est un constructeur de la classe SampleSyncOrchestrator dans l'application. Le constructeur prend deux objets RelationalSyncProvider, car SqlSyncProvider et SqlCeSyncProvider dérivent de RelationalSyncProvider. 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.

public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{

    this.LocalProvider = localProvider;
    this.RemoteProvider = remoteProvider;
    this.Direction = SyncDirectionOrder.UploadAndDownload;
}
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)

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

Synchronisation des nœuds

L'exemple de code suivant instancie des fournisseurs pour trois sessions de synchronisation différentes : entre le serveur et le client SQL Server ; entre le client SQL Server et l'un des clients SQL Server Compact ; et entre le serveur et l'autre client SQL Server Compact. Sept lignes sont synchronisées au cours des deux premières sessions : les quatre lignes de CustomerContact et les trois lignes de Customer qui répondent aux critères de filtrage. Les lignes ne sont pas synchronisées dans la troisième session parce que l'instantané contient déjà toutes les données de la première base de données SQL Server Compact. L'application affiche les statistiques retournées par la méthode Synchronize().

Important

Lorsqu'un nœud est ajouté à une communauté de synchronisation, sa première synchronisation doit avoir lieu avant que le nouveau nœud soit synchronisé avec d'autres nœuds de la communauté. Sinon, le nouveau nœud risque d'être détecté comme obsolète.

SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;

// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
    new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
    new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
    );
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");

// Data is downloaded from the SQL Server client to the 
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
    new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
    new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
    );
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");

// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to 
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");

// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains 
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
    new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
    new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
    );
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics

' Data is downloaded from the server to the SQL Server client. 
syncOrchestrator = New SampleSyncOrchestrator( _
    New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
    New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")

' Data is downloaded from the SQL Server client to the 
' first SQL Server Compact client. 
syncOrchestrator = New SampleSyncOrchestrator( _
    New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
    New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")

' Create a snapshot from the SQL Server Compact database, which will be used to 
' initialize a second Compact database. Again, this database could be provisioned 
' by retrieving scope information from another database, but we want to 
' demonstrate the use of snapshots, which provide a convenient deployment 
' mechanism for Compact databases. 
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")

' The new SQL Server Compact client synchronizes with the server, but 
' no data is downloaded because the snapshot already contains 
' all of the data from the first Compact database. 
syncOrchestrator = New SampleSyncOrchestrator( _
    New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
    New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")

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.ObjectModel;
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.SqlServer;
using Microsoft.Synchronization.Data.SqlServerCe;

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

            // Create the connections over which provisioning and synchronization
            // are performed. 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.
            SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
            SqlConnection clientSqlConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);
            SqlCeConnection clientSqlCe1Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync1);
            SqlCeConnection clientSqlCe2Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync2);

            // Create a scope named "filtered_customer", and add two tables to the scope.
            // GetDescriptionForTable gets the schema of each table, so that tracking 
            // tables and triggers can be created for that table. For Customer, we add
            // the entire table. For CustomerContact, we add only two of the columns.
            DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");

            // Definition for Customer.
            DbSyncTableDescription customerDescription =
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);

            scopeDesc.Tables.Add(customerDescription);

            // Definition for CustomerContact, including the list of columns to include.
            Collection<string> columnsToInclude = new Collection<string>();
            columnsToInclude.Add("CustomerId");
            columnsToInclude.Add("PhoneType");
            DbSyncTableDescription customerContactDescription =
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);

            scopeDesc.Tables.Add(customerContactDescription);

            // Create a provisioning object for "filtered_customer". We specify that
            // all synchronization-related objects should be created in a 
            // database schema named "Sync". If you specify a schema, it must already exist
            // in the database.
            SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(serverConn, scopeDesc);
            serverConfig.ObjectSchema = "Sync";

            // Specify which column(s) in the Customer table to use for filtering data, 
            // and the filtering clause to use against the tracking table.
            // "[side]" is an alias for the tracking table.
            serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
            serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";

            // Configure the scope and change-tracking infrastructure.
            serverConfig.Apply();

            // Write the configuration script to a file. You can modify 
            // this script if necessary and run it against the server
            // to customize behavior.
            File.WriteAllText("SampleConfigScript.txt",
                serverConfig.Script());


            // Provision each of the client databases.           

            // Create a SQL Server Compact database and provision it based on scope
            // information that is retrieved from the server. Compact databases
            // do not support separate schemas, so we prefix the name of all 
            // synchronization-related objects with "Sync" so that they are easy to
            // identify.
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
            DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
            SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc);
            clientSqlCe1Config.ObjectPrefix = "Sync";
            clientSqlCe1Config.Apply();

            // Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
            // information that is retrieved from the SQL Server Compact database. We could
            // have also retrieved this information from the server.
            DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
            SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
            clientSqlConfig.ObjectSchema = "Sync";
            clientSqlConfig.Apply();


            // Initial synchronization sessions. 7 rows are synchronized:
            // all rows (4) from CustomerContact, and the 3 rows from Customer 
            // that satisfy the filtering criteria.
            SampleSyncOrchestrator syncOrchestrator;
            SyncOperationStatistics syncStats;

            // Data is downloaded from the server to the SQL Server client.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
                new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");

            // Data is downloaded from the SQL Server client to the 
            // first SQL Server Compact client.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
                new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");

            // Create a snapshot from the SQL Server Compact database, which will be used to
            // initialize a second Compact database. Again, this database could be provisioned
            // by retrieving scope information from another database, but we want to 
            // demonstrate the use of snapshots, which provide a convenient deployment
            // mechanism for Compact databases.
            SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
            syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");

            // The new SQL Server Compact client synchronizes with the server, but
            // no data is downloaded because the snapshot already contains 
            // all of the data from the first Compact database.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
                new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");


            // Make changes on the server: 1 insert, 1 update, and 1 delete.
            Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");

            // Synchronize again. Three changes were made on the server, but
            // only two of them applied to rows that are in the "filtered_customer"
            // scope. The other row is not synchronized.
            // Notice that the order of synchronization is different from the initial
            // sessions, but the two changes are propagated to all nodes.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
                new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
                new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync")
            );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"),
                new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
            );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            serverConn.Close();
            serverConn.Dispose();
            clientSqlConn.Close();
            clientSqlConn.Dispose();
            clientSqlCe1Conn.Close();
            clientSqlCe1Conn.Dispose();
            clientSqlCe2Conn.Close();
            clientSqlCe2Conn.Dispose();

            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server);
            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client);

            Console.Write("\nPress any key to exit.");
            Console.Read();

        }

    }

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

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

        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.ObjectModel
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.SqlServer
Imports Microsoft.Synchronization.Data.SqlServerCe

Namespace Microsoft.Samples.Synchronization

    Class Program

        Public Shared Sub Main(ByVal args As String())

            ' Create the connections over which provisioning and synchronization 
            ' are performed. 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. 
            Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
            Dim clientSqlConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)
            Dim clientSqlCe1Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync1)
            Dim clientSqlCe2Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync2)

            ' Create a scope named "filtered_customer", and add two tables to the scope. 
            ' GetDescriptionForTable gets the schema of each table, so that tracking 
            ' tables and triggers can be created for that table. For Customer, we add 
            ' the entire table. For CustomerContact, we add only two of the columns. 
            Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")

            ' Definition for Customer. 
            Dim customerDescription As DbSyncTableDescription = _
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)

            scopeDesc.Tables.Add(customerDescription)


            ' Definition for CustomerContact, including the list of columns to include. 
            Dim columnsToInclude As New Collection(Of String)()
            columnsToInclude.Add("CustomerId")
            columnsToInclude.Add("PhoneType")
            Dim customerContactDescription As DbSyncTableDescription = _
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)

            scopeDesc.Tables.Add(customerContactDescription)

            ' Create a provisioning object for "filtered_customer". We specify that 
            ' all synchronization-related objects should be created in a 
            ' database schema named "Sync". If you specify a schema, it must already exist 
            ' in the database. 
            Dim serverConfig As New SqlSyncScopeProvisioning(serverConn, scopeDesc)
            serverConfig.ObjectSchema = "Sync"

            ' Specify which column(s) in the Customer table to use for filtering data, 
            ' and the filtering clause to use against the tracking table. 
            ' "[side]" is an alias for the tracking table. 
            serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
            serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"

            ' Configure the scope and change-tracking infrastructure. 
            serverConfig.Apply()

            ' Write the configuration script to a file. You can modify 
            ' this script if necessary and run it against the server 
            ' to customize behavior. 
            File.WriteAllText("SampleConfigScript.txt", serverConfig.Script())


            ' Provision each of the client databases. 

            ' Create a SQL Server Compact database and provision it based on scope 
            ' information that is retrieved from the server. Compact databases 
            ' do not support separate schemas, so we prefix the name of all 
            ' synchronization-related objects with "Sync" so that they are easy to 
            ' identify. 
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
            Dim clientSqlCe1Desc As DbSyncScopeDescription = _
                SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
            Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc)
            clientSqlCe1Config.ObjectPrefix = "Sync"
            clientSqlCe1Config.Apply()


            ' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope 
            ' information that is retrieved from the SQL Server Compact database. We could 
            ' have also retrieved this information from the server. 
            Dim clientSqlDesc As DbSyncScopeDescription = _
                SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
            Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
            clientSqlConfig.ObjectSchema = "Sync"
            clientSqlConfig.Apply()


            ' Initial synchronization sessions. 7 rows are synchronized: 
            ' all rows (4) from CustomerContact, and the 3 rows from Customer 
            ' that satisfy the filtering criteria. 
            Dim syncOrchestrator As SampleSyncOrchestrator
            Dim syncStats As SyncOperationStatistics

            ' Data is downloaded from the server to the SQL Server client. 
            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
                New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")

            ' Data is downloaded from the SQL Server client to the 
            ' first SQL Server Compact client. 
            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
                New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")

            ' Create a snapshot from the SQL Server Compact database, which will be used to 
            ' initialize a second Compact database. Again, this database could be provisioned 
            ' by retrieving scope information from another database, but we want to 
            ' demonstrate the use of snapshots, which provide a convenient deployment 
            ' mechanism for Compact databases. 
            Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
            syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")

            ' The new SQL Server Compact client synchronizes with the server, but 
            ' no data is downloaded because the snapshot already contains 
            ' all of the data from the first Compact database. 
            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
                New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")


            ' Make changes on the server: 1 insert, 1 update, and 1 delete. 
            Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")


            ' Synchronize again. Three changes were made on the server, but 
            ' only two of them applied to rows that are in the "filtered_customer" 
            ' scope. The other row is not synchronized. 
            ' Notice that the order of synchronization is different from the initial 
            ' sessions, but the two changes are propagated to all nodes. 

            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
                New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")

            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
                New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")

            syncOrchestrator = New SampleSyncOrchestrator( _
                New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"), _
                New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")


            serverConn.Close()
            serverConn.Dispose()
            clientSqlConn.Close()
            clientSqlConn.Dispose()
            clientSqlCe1Conn.Close()
            clientSqlCe1Conn.Dispose()
            clientSqlCe2Conn.Close()
            clientSqlCe2Conn.Dispose()

            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server)
            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client)

            Console.Write(vbLf & "Press any key to exit.")
            Console.Read()
        End Sub

    End Class

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

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

        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 Namespace

Voir aussi

Autres ressources

Architecture et classes pour la synchronisation de base de données