Como filtrar dados para sincronização de bancos de dados (SQL Server)
Este tópico mostra como criar filtros que podem ser usados com o Sync Framework para sincronizar o SQL Server, o SQL Azure e os bancos de dados do SQL Server Compact. Os exemplos deste tópico se concentram nas classes e nos membros do Sync Framework a seguir:
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.
Noções básicas sobre filtros
Um filtro é usado para controlar os itens enumerados pelo provedor de origem em uma sessão de sincronização. Quando os itens são enumerados e enviados ao provedor de destino, o provedor de origem inclui um item apenas quando este passa pelo filtro. Um filtro é tipicamente baseado no valor de um ou mais campos, de forma que uma linha passa pelo filtro apenas quando seus campos de filtro atendem aos requisitos do filtro.
O Sync Framework permite que você crie dois tipos de filtros: filtros estáticos e filtros baseados em parâmetros. Os filtros estáticos são definidos como parte de um escopo de sincronização e definem os valores que os campos do filtro devem conter para que passem pelo filtro. Os filtros estáticos são codificados no procedimento armazenado usado pelo banco de dados de origem para enumerar alterações para o escopo. Depois que um filtro estático é definido, ele não pode ser alterado. Para obter mais informações e um exemplo de como usar um filtro estático, consulte Como configurar e executar a sincronização de bancos de dados (SQL Server). Os filtros baseados em parâmetros são definidos por uma cláusula de filtro e um conjunto de parâmetros que mapeiam colunas da tabela no escopo de sincronização. Um filtro baseado em parâmetros é definido em dois estágios. O primeiro estágio define a cláusula do filtro e os parâmetros, e estabelece a descrição do escopo associado ao filtro. Nesse estágio, o filtro e o escopo estão apenas no formato de modelo. O segundo estágio define os valores dos parâmetros para o filtro e cria o escopo de sincronização a partir do modelo. O escopo criado nesse estágio é aquele que o provedor de destino usa para sincronizar com o banco de dados de origem. O banco de dados de origem para a filtragem baseada em parâmetros pode ser um banco de dados do SQL Server ou do SQL Azure; e o banco de dados de destino pode ser um banco de dados do SQL Server, do SQL Azure ou do SQL Server Compact.
Em um cenário típico de filtragem, um administrador de banco de dados ou desenvolvedor de aplicativo define um filtro baseado em parâmetros e prepara o banco de dados servidor para a sincronização filtrada. Ele também pode criar opcionalmente uma ferramenta simples, como uma ferramenta de assinatura baseada na Web, que usa objetos do Sync Framework para permitir que os usuários especifiquem seus valores de parâmetros de filtro e assinem seus bancos de dados cliente para sincronização. Ao criar uma ferramenta de assinatura, o administrador do banco de dados não precisa estar envolvido na criação de filtros para usuários individuais. Em vez disso, os usuários usam a ferramenta para especificar valores de parâmetros apropriados para eles e assinam a sincronização conforme a necessidade.
Um exemplo de processo para configurar sincronizações filtradas é:
Você tem um banco de dados de informações do cliente que contém colunas para o tipo de cliente e o estado em que ele está localizado.
Então define um modelo de filtro que é baseado em dois parâmetros de filtro: tipo de cliente e estado. Depois especifica que uma linha passe pelo filtro apenas quando seus valores de tipo de cliente e estado forem iguais aos valores dos parâmetros.
Determinada vendedora deseja sincronizar seu banco de dados cliente com o servidor. Ela solicita a criação de um escopo filtrado com valores de parâmetro para clientes de varejo em Washington e que faça a sincronização.
Ela recebe apenas os dados dos clientes que deseja, reduzindo o tráfego da rede e a quantidade de memória usada em seu banco de dados cliente.
Um outro vendedor solicita um escopo filtrado que passe clientes de atacado em Delaware e faça a sincronização. Ele recebe apenas os dados dos clientes que deseja.
Lembre-se de que os filtros baseados em parâmetros são adequados apenas quando os itens não serão inseridos ou removidos do escopo do filtro, como quando o valor de um campo muda para que um item que antes passava pelo filtro não passe mais. Alterações desse tipo não serão propagadas corretamente em toda a comunidade de sincronização. Por exemplo, um filtro é definido com base em uma coluna de nome do vendedor. O vendedor A cria um escopo filtrado pelo seu nome e sincroniza dados para seu banco de dados cliente. O gerente reatribui um dos clientes do vendedor A ao vendedor B. Quando o vendedor A fizer a sincronização novamente com o banco de dados, o cliente reatribuído ainda aparecerá em seu banco de dados cliente com dados desatualizados, em vez de ser removido.
Criando um filtro baseado em parâmetros
Os filtros baseados em parâmetros são criados em duas etapas. Primeiro, os modelos de filtro e escopo são definidos. Em seguida, um escopo filtrado é criado com valores específicos para os parâmetros do filtro. Esse processo de duas etapas oferece as seguintes vantagens:
Facilidade de configuração. Um modelo de filtro é definido uma vez. Criar um modelo de filtro é a única ação que exige permissão para criar procedimentos armazenados no servidor de banco de dados.
Facilidade de assinatura. Os clientes especificam valores de parâmetros para criar e assinar escopos filtrados conforme a necessidade. Esta etapa exige apenas permissão para inserir linhas em tabelas de sincronização no servidor de banco de dados.
**Facilidade de manutenção.**Mesmo quando diversos parâmetros estão combinados e muitos escopos filtrados são criados, a manutenção é simples, pois é usado um único procedimento baseado em parâmetros para enumerar as alterações.
Definindo um modelo de filtro
O primeiro passo para criar um filtro baseado em parâmetros é definir um modelo de filtro que possa posteriormente ser usado para criar escopos filtrados. Um modelo de filtro é armazenado no banco de dados de origem e exige a criação de tabelas de sincronização e procedimentos armazenados. Portanto, são necessárias as devidas permissões no banco de dados de origem.
Um modelo de filtro é definido junto com a definição de um escopo de sincronização. Você define um modelo de filtro para uma tabela no escopo da seguinte forma:
Adicione uma coluna de filtro a um objeto SqlSyncTableProvisioning no escopo de sincronização usando AddFilterColumn. Isso adiciona a coluna de filtro à tabela de rastreamento que controla as alterações na tabela base.
Defina um ou mais parâmetros de filtro adicionando objetos SqlParameter à coleção FilterParameters do objeto SqlSyncTableProvisioning. Isso adiciona os parâmetros especificados à lista de argumentos do procedimento armazenado que enumera as alterações durante a sincronização.
Adicione uma cláusula de filtro que defina a relação entre os valores de parâmetro e os valores da coluna definindo a propriedade FilterClause do objeto SqlSyncTableProvisioning. A cláusula de filtro é uma cláusula WHERE sem a palavra-chave WHERE. O alias [side] é um alias para a tabela de rastreamento. Os parâmetros correspondem aos parâmetros especificados na coleção FilterParameters. Nesse ponto, você está definindo apenas a relação entre os parâmetros do filtro e as colunas. Os valores reais para os parâmetros serão especificados posteriormente, quando o escopo filtrado for criado.
Em seguida, os modelos de filtro e escopo são aplicados ao banco de dados de origem usando o método Apply do objeto SqlSyncScopeProvisioning, e nesse momento as tabelas de sincronização e os procedimentos armazenados apropriados são criados.
Na cláusula de filtro, os aliases [base] e [side] são definidos pelo Sync Framework. [base] se refere ao nome base da tabela e [side] se refere à tabela de controle de alterações. Por exemplo, a tabela Customer é filtrada com base na coluna CustomerType. Por padrão, [base] é um alias para [Customer] e [side] é um alias para [Customer_tracking]. Como a coluna CustomerType existe tanto na tabela base quanto na tabela de controle, as referências a ela devem ser qualificadas na cláusula de filtro; do contrário, a coluna é ambígua e haverá um erro. Também é possível usar os nomes de tabela reais dos aliases [base] e [side], como [Customer_tracking].[CustomerType] = @customertype.
O exemplo a seguir define um modelo de filtro e o aplica ao banco de dados de origem:
// Create a scope named "customertype_template", 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.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customertype_template");
// Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter.";
// Definition for tables.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn);
scopeDesc.Tables.Add(customerContactDescription);
// Create a provisioning object for "customertype_template" that can be used to create a template
// from which filtered synchronization scopes can be created. 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 serverTemplate = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
serverTemplate.ObjectSchema = "Sync";
// Specify the column 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.
// The CustomerType column that defines the filter is set up as a parameter in this template.
// An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverTemplate.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = @customertype";
SqlParameter param = new SqlParameter("@customertype", SqlDbType.NVarChar, 100);
serverTemplate.Tables["Sales.Customer"].FilterParameters.Add(param);
// Create the "customertype_template" template in the database.
// This action creates tables and stored procedures in the database, so appropriate database permissions are needed.
serverTemplate.Apply();
' Create a scope named "customertype_template", 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.
Dim scopeDesc As New DbSyncScopeDescription("customertype_template")
' Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter."
' Definition for tables.
Dim customerDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
Dim customerContactDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn)
scopeDesc.Tables.Add(customerContactDescription)
' Create a provisioning object for "customertype_template" that can be used to create a template
' from which filtered synchronization scopes can be created. 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 serverTemplate As New SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template)
serverTemplate.ObjectSchema = "Sync"
' Specify the column 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.
' The CustomerType column that defines the filter is set up as a parameter in this template.
' An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverTemplate.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = @customertype"
Dim param As New SqlParameter("@customertype", SqlDbType.NVarChar, 100)
serverTemplate.Tables("Sales.Customer").FilterParameters.Add(param)
' Create the "customertype_template" template in the database.
' This action creates tables and stored procedures in the database, so appropriate permissions are needed.
serverTemplate.Apply()
Criando um escopo filtrado
Antes que um cliente possa usar o filtro para sincronizar com o servidor, ele deve primeiro definir valores específicos para os parâmetros do filtro. Para isso, o cliente primeiro popula um objeto SqlSyncScopeProvisioning a partir do modelo de filtro no servidor, e nomeia o escopo filtrado chamando PopulateFromTemplate. Em seguida, o cliente define os valores de parâmetro do filtro definindo as propriedades de valor dos membros da coleção FilterParameters no objeto SqlSyncTableProvisioning. Por fim, o cliente aplica o escopo filtrado ao servidor chamando o método Apply do objeto SqlSyncScopeProvisioning. Aplicar um escopo filtrado ao banco de dados servidor adiciona linhas às tabelas de sincronização, sendo necessário apenas a permissão para inserir linhas a essas tabelas.
Depois que o escopo filtrado é especificado no banco de dados servidor, você provisiona um banco de dados cliente chamando GetDescriptionForScope para obter a descrição do escopo nomeado, carregando a descrição do escopo em um objeto SqlSyncScopeProvisioning e aplicando a descrição ao banco de dados cliente chamando Apply.
O código para definir os valores de parâmetro do filtro, aplicar o filtro recém-especificado ao banco de dados servidor e provisionar o banco de dados cliente pode ser facilmente encapsulado em uma ferramenta separada que reúne os valores de parâmetro do filtro de um usuário e assina o banco de dados cliente do usuário para sincronização filtrada.
O exemplo a seguir define um valor de parâmetro para um filtro, aplica-o ao banco de dados servidor e provisiona o banco de dados cliente com o escopo filtrado para prepará-lo para sincronização:
// Create a synchronization scope for retail customers.
// This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
// the permissions needed on the server.
SqlSyncScopeProvisioning serverProvRetail = new SqlSyncScopeProvisioning(serverConn);
serverProvRetail.ObjectSchema = "Sync";
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template");
serverProvRetail.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Retail";
serverProvRetail.UserDescription = "Customer data includes only retail customers.";
serverProvRetail.Apply();
// Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
// information that is retrieved from the server.
DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", null, "Sync", serverConn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply();
' Create a synchronization scope for retail customers.
' This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
' the permissions needed on the server.
Dim serverProvRetail As New SqlSyncScopeProvisioning(serverConn)
serverProvRetail.ObjectSchema = "Sync"
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template")
serverProvRetail.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Retail"
serverProvRetail.UserDescription = "Customer data includes only retail customers."
serverProvRetail.Apply()
' Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
' information that is retrieved from the server.
Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", Nothing, "Sync", serverConn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply()
Sincronizando um cliente usando um escopo filtrado
Depois que o escopo filtrado for definido e o banco de dados cliente for provisionado, o cliente poderá ser sincronizado criando objetos SqlSyncProvider para o escopo filtrado nos bancos de dados cliente e servidor, associando os provedores a um objeto SyncOrchestrator e chamando o método Synchronize.
O exemplo a seguir executa uma sincronização filtrada de dois bancos de dados:
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync"),
new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
Exemplo
O exemplo a seguir inclui os exemplos de código descritos anteriormente e o código adicional para executar a sincronização. 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);
// Create a scope named "customertype_template", 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.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customertype_template");
// Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter.";
// Definition for tables.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn);
scopeDesc.Tables.Add(customerContactDescription);
// Create a provisioning object for "customertype_template" that can be used to create a template
// from which filtered synchronization scopes can be created. 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 serverTemplate = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
serverTemplate.ObjectSchema = "Sync";
// Specify the column 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.
// The CustomerType column that defines the filter is set up as a parameter in this template.
// An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverTemplate.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = @customertype";
SqlParameter param = new SqlParameter("@customertype", SqlDbType.NVarChar, 100);
serverTemplate.Tables["Sales.Customer"].FilterParameters.Add(param);
// Create the "customertype_template" template in the database.
// This action creates tables and stored procedures in the database, so appropriate database permissions are needed.
serverTemplate.Apply();
// Create a synchronization scope for retail customers.
// This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
// the permissions needed on the server.
SqlSyncScopeProvisioning serverProvRetail = new SqlSyncScopeProvisioning(serverConn);
serverProvRetail.ObjectSchema = "Sync";
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template");
serverProvRetail.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Retail";
serverProvRetail.UserDescription = "Customer data includes only retail customers.";
serverProvRetail.Apply();
// Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
// information that is retrieved from the server.
DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", null, "Sync", serverConn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply();
// Create a SQL Server Compact database and provision it based on a new scope that is created from template
// information coupled with a new customer type to use for the filter.
// 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);
// First create the scope in the server database based on the filter template.
// Again, note that this step requires only permission to insert rows in the synchronization tables.
SqlSyncScopeProvisioning serverProvWholesale = new SqlSyncScopeProvisioning(serverConn);
serverProvWholesale.ObjectSchema = "Sync";
serverProvWholesale.PopulateFromTemplate("WholesaleCustomers", "customertype_template");
serverProvWholesale.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Wholesale";
serverProvWholesale.UserDescription = "Customer data includes only wholesale customers.";
serverProvWholesale.Apply();
// Now provision the SQL Server Compact database with the new filtered scope.
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("WholesaleCustomers", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.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("RetailCustomers", clientSqlConn, null, "Sync"),
new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the server to the
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("WholesaleCustomers", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Make changes on the server:
// 1 insert affects RetailCustomers scope
// 1 update affects WholesaleCustomers scope
// 1 delete affects RetailCustomers scope
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");
// Synchronize again. Three changes were made on the server.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("WholesaleCustomers", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync"),
new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
serverConn.Close();
serverConn.Dispose();
clientSqlConn.Close();
clientSqlConn.Dispose();
clientSqlCe1Conn.Close();
clientSqlCe1Conn.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
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)
' Create a scope named "customertype_template", 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.
Dim scopeDesc As New DbSyncScopeDescription("customertype_template")
' Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter."
' Definition for tables.
Dim customerDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
Dim customerContactDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn)
scopeDesc.Tables.Add(customerContactDescription)
' Create a provisioning object for "customertype_template" that can be used to create a template
' from which filtered synchronization scopes can be created. 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 serverTemplate As New SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template)
serverTemplate.ObjectSchema = "Sync"
' Specify the column 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.
' The CustomerType column that defines the filter is set up as a parameter in this template.
' An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverTemplate.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = @customertype"
Dim param As New SqlParameter("@customertype", SqlDbType.NVarChar, 100)
serverTemplate.Tables("Sales.Customer").FilterParameters.Add(param)
' Create the "customertype_template" template in the database.
' This action creates tables and stored procedures in the database, so appropriate permissions are needed.
serverTemplate.Apply()
' Write the template creation script to a file. You can modify
' this script if necessary and run it against the server
' to customize behavior.
File.WriteAllText("SampleTemplateScript.txt", serverTemplate.Script())
' Create a synchronization scope for retail customers.
' This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
' the permissions needed on the server.
Dim serverProvRetail As New SqlSyncScopeProvisioning(serverConn)
serverProvRetail.ObjectSchema = "Sync"
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template")
serverProvRetail.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Retail"
serverProvRetail.UserDescription = "Customer data includes only retail customers."
serverProvRetail.Apply()
' Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
' information that is retrieved from the server.
Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", Nothing, "Sync", serverConn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply()
' Create a SQL Server Compact database and provision it based on a new scope that is created from template
' information coupled with a new customer type to use for the filter.
' 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)
' First create the scope in the server database based on the filter template.
' Again, note that this step requires only permission to insert rows to the synchronization tables.
Dim serverProvWholesale As New SqlSyncScopeProvisioning(serverConn)
serverProvWholesale.ObjectSchema = "Sync"
serverProvWholesale.PopulateFromTemplate("WholesaleCustomers", "customertype_template")
serverProvWholesale.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Wholesale"
serverProvWholesale.UserDescription = "Customer data includes only wholesale customers."
serverProvWholesale.Apply()
' Now provision the SQL Server Compact database with the new filtered scope.
Dim clientSqlCe1Desc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("WholesaleCustomers", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.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("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the server to the
' first SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator(New SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"), New SqlSyncProvider("WholesaleCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Make changes on the server:
' 1 insert affects RetailCustomers scope
' 1 update affects WholesaleCustomers scope
' 1 delete affects RetailCustomers scope
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")
' Synchronize again. Three changes were made on the server.
syncOrchestrator = New SampleSyncOrchestrator(New SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"), New SqlSyncProvider("WholesaleCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
serverConn.Close()
serverConn.Dispose()
clientSqlConn.Close()
clientSqlConn.Dispose()
clientSqlCe1Conn.Close()
clientSqlCe1Conn.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