Como configurar e executar a sincronização de bancos de dados (SQL Server)
Este tópico descreve as partes principais de um aplicativo que usa o Sync Framework para sincronizar bancos de dados do SQL Server e do SQL Server Compact. Este aplicativo usa diversos recursos avançados do Sync Framework que não são exigidos na sincronização básica, como a adição de múltiplas tabelas a um escopo, a definição de um filtro de linha para uma das tabelas e a definição de um filtro de coluna para a outra tabela. O código neste aplicativo destaca as seguintes classes do Sync Framework:
Para obter mais informações sobre como executar o código de exemplo, consulte "Exemplo de aplicativos nos tópicos de instruções" em Sincronizando o SQL Server e o SQL Server Compact.
Conforme descrito em Arquitetura e classes para sincronização de bancos de dados, a sincronização pode ocorrer entre duas instâncias do SqlSyncProvider, duas instâncias do SqlCeSyncProvider ou uma instância de cada. O código de exemplo deste tópico é de um aplicativo de duas camadas; portanto, ele não demonstra a sincronização de duas instâncias do SqlCeSyncProvider, o que requer uma configuração de n camadas. Para obter um exemplo de uma configuração de n camadas, consulte o exemplo de WebSharingAppDemo-CEProviderEndToEnd incluído no SDK do Sync Framework.
Comparando tipos de provedores
Este tópico descreve como sincronizar os bancos de dados do SQL Server e do SQL Server Compact usando os dois provedores de sincronização introduzidos no Sync Framework 2.0: SqlSyncProvider e SqlCeSyncProvider. O Sync Framework inclui outros provedores que podem sincronizar esses bancos de dados, mas, em geral, os novos provedores são mais adequados para a tarefa, pelos seguintes motivos:
SqlSyncProvider é tão eficiente quanto DbSyncProvider, mas requer menos código e um menor entendimento sobre consultas que o Sync Framework utiliza para sincronizar dados. DbSyncProvider ainda é o mais indicado para bancos de dados não SQL Server.
SqlSyncProvider e SqlCeSyncProvider podem ser usados para topologias cliente-servidor, ponto a ponto e mistas, enquanto o DbServerSyncProvider e o SqlCeClientSyncProvider são adequados apenas para topologias cliente/servidor. SqlSyncProvider e SqlCeSyncProvider também suportam recursos mais avançados, como processamento de lotes de alterações baseado no tamanho dos dados em vez de no número de linhas.
SqlSyncProvider e SqlCeSyncProvider são flexíveis e fáceis de configurar. Eles permitem sincronizar todas as edições de SQL Server, inclusive SQL Server Express e SQL Server Compact.
Configurando nós e executando a sincronização
A sincronização de nós em uma topologia pode ser dividida em duas fases: configuração dos nós a serem sincronizados e execução da sincronização entre um par de nós. Para SqlSyncProvider e SqlCeSyncProvider, a configuração de nós consiste em duas tarefas:
Definindo o que você deseja sincronizar
Você define o que deseja sincronizar descrevendo um ou mais escopos. Um escopo é um conjunto de tabelas que podem ser filtradas. As tabelas já podem existir no banco de dados ou elas podem ser descritas usando objetos do Sync Framework e geradas em tempo de execução quando o repositório subjacente estiver sendo sincronizado. Para obter mais informações, consulte "Noções básicas sobre escopos" mais adiante neste tópico.
Importante
Depois que um escopo é sincronizado pela primeira vez, os metadados associados aos escopos existentes devem ser descartados e recriados caso um escopo precise ser alterado.
Provisionando bancos de dados para controle de alterações do Sync Framework
Depois que as tabelas e os escopos forem descritos, use objetos do Sync Framework para aplicar scripts de provisionamento a cada nó. Os scripts criam uma infraestrutura de controle e aplicação de alterações que consiste em tabelas de metadados, gatilhos e procedimentos armazenados.
Depois de provisionados, os nós podem ser sincronizados. Do ponto de vista do desenvolvedor, definir opções de sincronização e chamar o método Synchronize() são tarefas simples. Nos bastidores, o Sync Framework usa o escopo e as informações de descrição de tabela especificadas para criar um objeto de configuração para cada escopo e cada adaptador de sincronização (um por tabela). Isso permite que o Sync Framework obtenha as informações mantidas em cada banco de dados e crie as informações de que necessita para cada sessão de sincronização entre um par de nós. SqlSyncProvider e SqlCeSyncProvider conhecem as tabelas de controle de alteração e outros objetos criados durante o provisionamento, e geram automaticamente os objetos DbSyncAdapter obrigatórios. Isso reduz significativamente o código necessário para sincronizar dados usando esses provedores.
A tabela a seguir lista as classes usadas para configurar bancos de dados e provedores.
SQL Server |
SQL Server Compact |
Descrição |
---|---|---|
Representa um escopo de sincronização, que é um agrupamento lógico de tabelas sincronizadas como uma unidade. |
||
Representa o provisionamento de um banco de dados do SQL Server ou do SQL Server Compact para um escopo específico, que é representado por um objeto DbSyncScopeDescription. |
||
Representa informações de configuração que são usadas pelo SqlSyncProvider ou pelo SqlCeSyncProvider para um escopo específico. |
||
Representa o esquema de uma tabela incluída em um escopo de sincronização. |
||
Representa as propriedades de uma coluna que faz parte de uma tabela incluída em um escopo de sincronização. |
||
Representa o escopo e as informações de tabela de um banco de dados do SQL Server ou do SQL Server Compact envolvido na sincronização. É usado para extrair objetos Description de um banco de dados SQL Server ou SQL Server Compact. |
||
Representa o provisionamento de uma tabela de banco de dados SQL Server ou SQL Server Compact (com filtros opcionais) que é representado por um objeto DbSyncTableDescription. |
||
Representa as informações de configuração do adaptador de sincronização para uma tabela em um banco de dados SQL Server ou SQL Server Compact. |
Além desses tipos principais, há outros quatro tipos importantes a serem considerados:
SqlSyncStoreMetadataCleanup e SqlCeSyncStoreMetadataCleanup habilitam limpar metadados antigos das tabelas de controle de alterações do Sync Framework. Para obter mais informações, consulte Como limpar metadados para sincronização de colaboração (SQL Server).
SqlCeSyncStoreSnapshotInitialization permite gerar um instantâneo de um arquivo de banco de dados existente SQL Server Compact que pode ser usado para inicializar outro banco de dados SQL Server Compact para fins de sincronização. Para obter mais informações, consulte Provisionamento dos clientes neste tópico.
SqlSyncStoreRestore permite atualizar metadados de controle de alterações depois que um banco de dados SQL Server é restaurado de um backup, o que necessário antes de um banco de dados restaurado ser sincronizado novamente. Para obter mais informações, consulte Como fazer backup de um banco de dados e restaurá-lo (SQL Server).
Noções básicas sobre escopos
Dica
Esta seção do tópico fornece informações adicionais sobre os escopos de sincronização. Daqui, você pode ir direto para os "Exemplos de códigos", mas recomendamos que você leia esta seção se pretende usar escopos filtrados ou mais de um escopo em um aplicativo.
É importante compreender que um escopo é a combinação de tabelas e filtros. Por exemplo, você poderia definir um escopo filtrado denominado sales-WA que contém apenas os dados de vendas do estado de Washington da tabela customer_sales. Se você definir outro filtro na mesma tabela, como sales-OR, ele terá um escopo diferente. Se você definir filtros, lembre-se de que o Sync Framework não trata automaticamente da exclusão de linhas que já não atendem a uma condição de filtro. Por exemplo, se um usuário ou um aplicativo atualizar um valor em uma coluna que é usada para filtragem, uma linha será movida de um escopo para outro. A linha é enviada para o novo escopo ao qual ela pertence agora, mas não é excluída do escopo antigo. Seu aplicativo deve lidar com essa situação.
Os escopos podem ser diferentes uns dos outros ou podem ser sobrepostos. Dois escopos serão sobrepostos se compartilharem os mesmos dados. Por exemplo, a tabela products pode ser incluída em um escopo sales e em um escopo inventory. Os escopos podem ser sobrepostos e filtrados. Os seguintes cenários demonstram de que modo a filtragem e a sobreposição podem ocorrer:
Cenário 1:
O escopo 1 é sales-WA. Esse escopo inclui: products; orders, com um filtro de state=WA; e order_details, com um filtro de state=WA.
O escopo 2 é sales-OR. Esse escopo inclui: products; orders, com um filtro de state=OR; e order_details, com um filtro de state=OR.
Nesse cenário, toda a tabela products é compartilhada pelos dois escopos. As tabelas orders e order_details estão em ambos os escopos, mas os filtros não se sobrepõem; portanto, os escopos não compartilham linhas dessas tabelas.
Cenário 2:
O escopo 1 é sales-WA. Esse escopo inclui: products; orders, com um filtro de state=WA; e order_details, com um filtro de state=WA.
O escopo 2 é sales-Northwest. Esse escopo inclui: products; orders, com um filtro de state=WA OR state=ID; e shippers.
Nesse cenário, toda a tabela products é novamente compartilhada pelos dois escopos. A tabela orders está em ambos os escopos e os filtros se sobrepõem; ambos os escopos compartilham as linhas que correspondem ao filtro state=WA. As tabelas shippers e order_details não são compartilhadas entre os escopos.
Existem muitas maneiras diferentes de se definir escopos, mas o princípio a seguir deve ser seguido: todos os dados que são sincronizados entre um par de bancos de dados na topologia de sincronização podem pertencer a um único escopo. Por exemplo, no Cenário 2 acima, o Banco de Dados A e o Banco de Dados B poderiam sincronizar o Escopo 1; e o Banco de Dados A e o Banco de Dados C poderiam sincronizar o Escopo 2. O Banco de Dados A e o Banco de Dados B não podem sincronizar o Escopo 2 porque as linhas products e orders pertencem a ambos os escopos.
Exemplos de códigos
Os exemplos de código desta seção incluem muitos dos objetos descritos acima e abrangem as seguintes áreas:
Descrição do escopo e das tabelas
Provisionamento do servidor
Provisionamento dos clientes
Definição das opções de sincronização
Sincronização dos nós
Depois de abordarmos cada uma dessas áreas, forneceremos um aplicativo de console completo que combina todos esses exemplos e código adicional para sincronizar uma topologia de quatro nós. A topologia consiste em um servidor SQL Server, um cliente SQL Server e dois clientes SQL Server Compact.
Descrevendo o escopo e as tabelas
O exemplo de código a seguir descreve um escopo denominado filtered_customer e acrescenta duas tabelas ao escopo: Customer e CustomerContact. Como as tabelas já existem no banco de dados servidor, o método GetDescriptionForTable é usado para recuperar o esquema do banco de dados do servidor. São incluídas todas as colunas da tabela Customer, mas apenas duas colunas da tabela CustomerContact são incluídas.
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)
Provisionamento do servidor
O exemplo de código a seguir cria um objeto de provisionamento para o escopo filtered_customer e especifica que todos os objetos relacionados à sincronização devem ser criados em um esquema de banco de dados denominado "Sync". Como parte do provisionamento do escopo, o código define um filtro na tabela Customer. Somente as linhas que corresponderem ao filtro serão sincronizadas. O código que define um filtro exige duas partes. A chamada para AddFilterColumn especifica que a coluna CustomerType seja usada para filtragem e que ela seja adicionada à tabela de rastreamento que rastreia alterações para a tabela Customer. A chamada para FilterClause especifica o próprio filtro. A cláusula de filtro é uma cláusula WHERE sem a palavra-chave WHERE. O alias [side] é um alias para a tabela de rastreamento. Nenhum filtro é definido na tabela CustomerContact; portanto, todas as linhas dessa tabela serão sincronizadas. Depois de definidas as opções de provisionamento, o método Apply é chamado para criar a infraestrutura de controle de alterações no banco de dados do servidor e o script de provisionamento é gravado em um arquivo.
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())
Provisionamento dos clientes
Neste aplicativo, os clientes são provisionados de duas formas diferentes:
Inicialização completa de um banco de dados cliente do SQL Server ou do SQL Server Compact baseado em informações de escopo recuperadas do banco de dados do servidor ou de outro cliente.
Os objetos do usuário e os objetos de sincronização são criados no banco de dados do cliente de acordo com as informações de esquema disponibilizadas pelos objetos SqlSyncDescriptionBuilder e SqlCeSyncDescriptionBuilder. Como parte da primeira sessão de sincronização, o banco de dados do cliente é preparado para a sincronização, e todas as linhas são baixadas para o banco de dados do cliente como inserções incrementais.
Inicialização do instantâneo de um banco de dados do cliente do SQL Server Compact com o uso do banco de dados do cliente pré-existente.
A inicialização de instantâneo foi criada para reduzir o tempo necessário para inicializar um banco de dados de cliente. Depois que um banco de dados cliente tiver sido inicializado com o uso da inicialização completa, os bancos de dados subsequentes poderão ser inicializados com o uso de um instantâneo do primeiro banco de dados cliente. Um instantâneo é um banco de dados do SQL Server Compact especialmente preparado que contém esquema de tabela, dados (opcionais) e infraestrutura de controle de alterações. Copie o instantâneo para cada cliente que precisar. Durante a primeira sessão de sincronização para um cliente, os metadados específicos do cliente são atualizados e as alterações ocorridas desde que o instantâneo foi criado são baixadas para o banco de dados cliente.
Importante
Instantâneos devem ser gerados só quando não há atividade no banco de dados do SQL Server Compact. Não há suporte para operações simultâneas de qualquer tipo durante a geração do instantâneo.
O exemplo de código a seguir recupera primeiro as informações de escopo do servidor e usa a tabela base e os esquemas de controle de alterações recuperados para provisionar um bancos de dados de cliente do SQL Server Compact. O código provisiona então um banco de dados de cliente do SQL Server de acordo com as informações de escopo do banco de dados de cliente do 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()
O exemplo de código a seguir gera um instantâneo denominado SyncSampleClient2.sdf do banco de dados SyncSampleClient1.sdf. Em seguida, o código sincroniza SyncSampleClient2.sdf com o banco de dados do servidor.
// 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")
Definindo opções de sincronização
O exemplo de código a seguir é um construtor da classe SampleSyncOrchestrator no aplicativo. O construtor usa dois objetos RelationalSyncProvider porque SqlSyncProvider e SqlCeSyncProvider derivam do RelationalSyncProvider. O código especifica qual provedor é o provedor local e qual é o remoto. Especifica também que as alterações serão carregadas primeiro do banco de dados remoto para o banco de dados local e depois baixadas na ordem inversa.
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
Sincronização dos nós
O exemplo de código a seguir instancia provedores para três sessões de sincronização diferentes: entre o servidor e o cliente do SQL Server; entre o cliente do SQL Server e um dos clientes do SQL Server Compact e entre o servidor e o outro cliente do SQL Server Compact. São sincronizadas sete linhas durante as duas primeiras sessões: todas as quatro linhas de CustomerContact e as três linhas de Customer que atenderem aos critérios de filtragem. As linhas não são sincronizadas na terceira sessão porque o instantâneo já contém todos os dados do primeiro banco de dados do SQL Server Compact. O aplicativo exibe as estatísticas que o método Synchronize() retorna.
Importante
Quando um nó é adicionado a uma comunidade de sincronização, sua primeira sincronização deve ser concluída para que o novo nó seja sincronizado com os outros nós da comunidade. Do contrário, o novo nó pode ser detectado como desatualizado.
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")
Exemplo de código completo
O exemplo de código completo a seguir inclui os exemplos de código descritos anteriormente e outros códigos para exibir estatísticas de sincronização e informações de eventos. O exemplo requer a classe Utility, disponível em Classe de utilitário para tópicos de instruções do provedor de banco de dados.
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