Как фильтровать данные для синхронизации базы данных (SQL Server)

В этом разделе показано, как создать фильтры, которые могут быть использованы в Sync Framework для синхронизации баз данных SQL Server, SQL Azure и SQL Server Compact. В примерах, приведенных в этом разделе, рассматриваются в основном следующие классы и члены платформы Sync Framework:

SqlSyncScopeProvisioning

PopulateFromTemplate

FilterClause

FilterParameters

Дополнительные сведения о запуске образцов кода см. в подразделе «Образцы приложений в разделах инструкций» раздела Синхронизация SQL Server и SQL Server Compact.

Основные сведения о фильтрах

Фильтр управляет перечислением элементов поставщиком источника в сеансе синхронизации. При перечислении и отправке элементов поставщику назначения поставщик источника обрабатывает только те элементы, которые проходят фильтрацию. Как правило, фильтр использует значения одного или нескольких полей, пропуская только те строки, поля которых удовлетворяют требованиям фильтрации.

Sync Framework позволяет создавать фильтры двух типов: статические фильтры и фильтры на основе параметров. Статические фильтры определяются в области синхронизации и определяют значения, которые должны содержать поля, чтобы строка прошла фильтр. Статические фильтры кодируются в виде хранимых процедур, которые используются исходной базой данных для перечисления изменений в области. После определения статический фильтр не может быть изменен. Дополнительные сведения и пример использования статического фильтра см. в разделе Как настроить и выполнить синхронизацию базы данных (SQL Server). Фильтры на основе параметров определяются выражением фильтрации и набором параметров, отображаемых на столбцы таблицы в области синхронизации. Фильтр на основе параметров определяется в два этапа. Во-первых, определяется выражение фильтрации и параметры, а также описание области, связанной с фильтром. На этом этапе фильтр и область представлены только в формате шаблона. На втором этапе для фильтра задаются значения параметров и по шаблону создается область синхронизации. Область, созданная на этом этапе, используется поставщиком назначения для синхронизации с исходной базой данных. Исходной базой данных для фильтрации на основе параметров может быть либо база данных SQL Server, либо база данных SQL Azure, а целевой базой данных — база данных SQL Server, SQL Azure или SQL Server Compact.

Как правило, администратор базы данных или разработчик приложения определяет фильтр на основе параметров и подготавливает серверную базу данных для выборочной синхронизации. Также с его помощью можно создать простое средство, например средство подписки с веб-интерфейсом, использующее объекты Sync Framework, что позволяет пользователям указать используемые ими значения параметров фильтрации и выполнить подписку их клиентских баз данных для синхронизации. Создавая средство подписки, администратор базы данных избавляет себя от необходимости создавать фильтры для конкретных пользователей. Вместо этого пользователи могут с помощью этого средства задать нужные им значения параметров и подписываться на синхронизацию по мере необходимости.

Ниже приведен пример процедуры настройки выборочной синхронизации.

  1. Имеется база данных, содержащая информацию о заказчиках, в которой есть столбцы для типа заказчика и штата, в котором он находится.

  2. Определяется шаблон фильтра на основе двух параметров — типа заказчика и штата. Строка будет проходить фильтр только в том случае, если штат и тип заказчика будут равны значениям параметров.

  3. Предположим, некий менеджер по продажам хочет синхронизировать свою клиентскую базу данных с сервером. Она отправляет запрос на создание и синхронизацию отфильтрованной области со значениями параметров для розничных заказчиков из штата Вашингтон.

  4. Продавцу передаются данные только о нужных заказчиках. Это позволяет сократить сетевой трафик и объем памяти, занимаемый клиентской базой данных.

  5. Другой менеджер по продажам запрашивает создание отфильтрованной области, которая пропускает оптовых заказчиков в штате Делавэр, и выполняет синхронизацию. Передаются данные только о тех заказчиках, которые нужны продавцу.

Учтите, что фильтры на основе параметров подходят только для случая, когда элементы не пересекают пределы области фильтра (как в случае, когда в результате изменения значений полей элемент, который ранее проходил фильтр, теперь его не проходит). Такие изменения не будут правильно распространяться в сообществе синхронизации. Предположим, фильтр определен для столбца имени менеджера по продажам. Менеджер по продажам A создает область, отфильтрованную по своему имени, и синхронизирует данные с клиентской базой данных. Его менеджер переназначает одного из его заказчиков менеджеру по продажам B. После следующей синхронизации менеджера по продажам A с базой данных этот переназначенный заказчик не удалится, а по-прежнему будет находиться в его клиентской базе данных и сведения о нем будут неактуальными.

Создание фильтра на основе параметров

Фильтры на основе параметров создаются в два этапа. Сначала нужно определить фильтр и шаблоны областей. Затем создается отфильтрованная область, содержащая конкретные значения параметров фильтра. Этот двухэтапный процесс имеет следующие преимущества.

  • Простота настройки. Шаблон фильтра определяется один раз. Это единственное действие, требующее разрешения на создание хранимых процедур на сервере базы данных.

  • Простота подписки. Клиенты по мере необходимости задают значения параметров для создания и подписки на отфильтрованные области. Этот этап требует только разрешения на вставку строк в таблицы синхронизации на сервере базы данных.

  • **Простота обслуживания.**Даже при сочетании нескольких параметров и создании массы отфильтрованных областей само по себе обслуживание является простым, поскольку перечислением изменений на основе параметров занимается единственная процедура.

Определение шаблона фильтра

Первый этап создания фильтра на основе параметров — это определение шаблона фильтра, который позже может быть использован для создания отфильтрованных областей. Шаблон фильтра сохраняется в исходной базе данных и требует создания таблиц синхронизации и хранимых процедур. Поэтому нужны соответствующие разрешения в исходной базе данных.

Шаблон фильтра определяется вместе с определением области синхронизации. Шаблон фильтра для таблицы в области определяется следующим образом:

  • Добавьте столбец фильтра в объект SqlSyncTableProvisioning области синхронизации с помощью метода AddFilterColumn. При этом к таблице отслеживания, которая используется для отслеживания изменений в базовой таблице, добавляется столбец фильтра.

  • Определите один или несколько параметров фильтра, добавив объекты SqlParameter в коллекцию FilterParameters объекта SqlSyncTableProvisioning. При этом указанные параметры будут добавлены к списку аргументов хранимой процедуры, выполняющей перечисление изменений при выполнении синхронизации.

  • Добавьте предложение фильтрации, которое определяет связи между значениями параметров фильтра и столбцами, установив свойство FilterClause объекта SqlSyncTableProvisioning. Выражением фильтрации является предложение WHERE без ключевого слова WHERE. Псевдонимом [side] является псевдоним для таблицы отслеживания. Параметры, совпадающие с параметрами, указанными в коллекции FilterParameters. На этом этапе можно определить только связи между параметрами фильтра и столбцами. Фактические значения параметров будут заданы позже, при создании отфильтрованной области.

Затем шаблоны фильтра и области применяются к базе данных-источнику вызовом методаApply объекта SqlSyncScopeProvisioning. На этом этапе создаются таблицы и хранимые процедуры синхронизации.

В предложении фильтрации псевдонимы [base] и [side] определяются платформой Sync Framework. Псевдоним [base] указывает на базовое имя таблицы, а [side] — на таблицу отслеживания изменений. Например, фильтрация таблицы Customer выполняется по столбцу CustomerType. По умолчанию [base] является псевдонимом [Customer], а [side] — псевдонимом [Customer_tracking]. Поскольку столбец CustomerType существует и в базовой таблице, и в таблице отслеживания изменений, ссылки на него должны указываться в предложении фильтра. В противном случае ссылка на столбец будет неоднозначной, что приведет к возникновению ошибки. Вместо псевдонимов [base] и [side] можно также использовать имена существующих таблиц, например [Customer_tracking].[CustomerType] = @customertype.

В следующем примере показано определение шаблона фильтра и применение его к базе данных-источнику:

// 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()

Создание отфильтрованной области

Прежде чем клиент сможет использовать фильтр для синхронизации с сервером, он должен задать конкретные значения для параметров фильтра. Для этого клиент сначала заполняет объект SqlSyncScopeProvisioning из шаблона фильтра на сервере и имена отфильтрованной области, вызывая метод PopulateFromTemplate. Затем клиент определяет значения параметров фильтра, устанавливая свойства элементов коллекции FilterParameters в объекте SqlSyncTableProvisioning. Наконец, клиент применяет отфильтрованную область к серверу, вызывая метод Apply объекта SqlSyncScopeProvisioning. При применении отфильтрованной области к серверной базе данных будут добавлены строки в таблицы синхронизации, что потребует только разрешения на вставку строк в эти таблицы.

После того как отфильтрованная область задана на серверной базе данных, нужно провизионировать клиентскую базу данных, вызвав метод GetDescriptionForScope для получения описания именованной области, загрузить его в объект SqlSyncScopeProvisioning, а затем применить описание области к клиентской базе данных, вызвав метод 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 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()

Синхронизация клиента с помощью отфильтрованной области

После определения отфильтрованной области и провизионирования клиентской базы данных можно выполнить синхронизацию клиента, создав объекты SqlSyncProvider для отфильтрованной области в клиентской и серверной базах данных, связав поставщики с объектом SyncOrchestrator и вызвав метод Synchronize.

В следующем примере показано выполнение синхронизации двух баз данных:

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()

Пример

Приведенный ниже пример кода содержит все ранее описанные примеры и дополнительный код для выполнения синхронизации. Для работы примеру необходим класс Utility, который можно найти в разделе Инструкции по классу Utility для поставщика базы данных.

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

См. также

Другие ресурсы

Синхронизация SQL Server и SQL Server Compact