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

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

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

В разделе Архитектура и классы для синхронизации базы данных упоминается, что синхронизация может выполняться между двумя экземплярами SqlSyncProvider, двумя экземплярами SqlCeSyncProvider либо между экземплярами разных поставщиков. Пример кода в этом разделе взят из двухуровневого приложения, и поэтому в нем не показана синхронизация двух экземпляров SqlCeSyncProvider (для такой синхронизации требуется многоуровневая конфигурация). Пример многоуровневой конфигурации см. в образце WebSharingAppDemo-CEProviderEndToEnd, который входит в пакет SDK для Sync Framework.

Сравнение типов поставщиков

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

  • SqlSyncProvider обладает всеми функциями DbSyncProvider, однако требует значительно меньшего объема кода и не требует столь полного представления о запросах, которые используются Sync Framework для синхронизации данных. DbSyncProvider по-прежнему удобно применять для баз данных, не являющихся базами SQL Server.

  • SqlSyncProvider и SqlCeSyncProvider можно использовать для клиент-серверной, одноранговой и смешанной топологии, а DbServerSyncProvider и SqlCeClientSyncProvider подходят только для клиент-серверной топологии. Кроме того, SqlSyncProvider и SqlCeSyncProvider поддерживают больше расширенных функций, в том числе пакетирование изменений в зависимости от размера данных, а не от количества строк.

  • SqlSyncProvider и SqlCeSyncProvider поддерживают удобную и гибкую настройку. Они позволяют синхронизировать все выпуски SQL Server, включая SQL Server Express и SQL Server Compact.

Настройка узлов и выполнение синхронизации

Синхронизацию узлов в топологии можно разделить на два этапа: настройка синхронизируемых узлов и собственно выполнение синхронизации в паре узлов. Для SqlSyncProvider и SqlCeSyncProvider настройка узлов состоит из двух задач.

  1. Определение объектов для синхронизации

    Синхронизируемые объекты определяются заданием одной или нескольких областей. Область представляет собой набор таблиц, которые можно фильтровать. Таблицы могут уже существовать в базе данных или описываться с помощью объектов Sync Framework, а затем создаваться во время выполнения, когда проводится синхронизация базового хранилища. Дополнительные сведения см. в подразделе «Основные сведения об областях» далее.

    Важное примечаниеВажно!

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

  2. Провизионирование баз данных к отслеживанию изменений Sync Framework

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

После подготовки узлов их можно синхронизировать. С точки зрения разработчика, задание параметров синхронизации и вызов метода Synchronize() выполняются очевидным образом. Платформа Sync Framework внутренним образом использует указанное описание областей и таблиц, чтобы построить объект конфигурации для каждой области и для каждого адаптера синхронизации (по одному на таблицу). Это позволяет платформе Sync Framework получать сведения, сохраняемые в каждой базе данных, и формировать наборы сведений, необходимые для каждого сеанса синхронизации, выполняемого в паре узлов. И поставщик SqlSyncProvider, и поставщик SqlCeSyncProvider работают с таблицами отслеживания изменений и другими объектами, формируемыми во время провизионирования, создавая нужные объекты DbSyncAdapter автоматически. Это значительно сокращает объем кода, необходимого для синхронизации данных с использованием данных поставщиков.

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

SQL Server

SQL Server Compact

Описание

DbSyncScopeDescription

DbSyncScopeDescription

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

SqlSyncScopeProvisioning

SqlCeSyncScopeProvisioning

Представляет провизионирование базы данных SQL Server или SQL Server Compact для определенной области, представленной объектом DbSyncScopeDescription.

SqlSyncProviderScopeConfiguration

SqlCeSyncProviderScopeConfiguration

Представляет сведения о настройке, которые используются SqlSyncProvider или SqlCeSyncProvider для определенной области.

DbSyncTableDescription

DbSyncTableDescription

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

DbSyncColumnDescription

DbSyncColumnDescription

Представляет свойства столбца, который входит в таблицу, включенную в область синхронизации.

SqlSyncDescriptionBuilder

SqlCeSyncDescriptionBuilder

Представляет сведения об области и таблице для базы данных SQL Server или SQL Server Compact, участвующих в синхронизации. Используется для извлечения объектов Description из базы данных SQL Server или SQL Server Compact.

SqlSyncTableProvisioning

SqlSyncTableProvisioning

Представляет провизионирование таблицы базы данных SQL Server или SQL Server Compact (с дополнительными фильтрами), которая представлена объектом DbSyncTableDescription.

SqlSyncProviderAdapterConfiguration

SqlSyncProviderAdapterConfiguration

Представляет конфигурацию адаптера синхронизации для таблицы в базе данных SQL Server или SQL Server Compact.

Помимо этих основных типов, следует помнить о четырех других важных типах.

Основные сведения об областях

Примечание

В этом разделе приводятся дополнительные сведения об областях синхронизации. Можно сразу перейти к подразделу «Примеры кода», однако если в приложении планируется использование фильтруемых областей или нескольких областей, рекомендуется ознакомиться с текущим подразделом.

Важно понимать, что область представляет собой сочетание таблиц и фильтров. Например, можно определить фильтруемую область с именем sales-WA, которая содержит только данные по продажам для штата Вашингтон из таблицы customer_sales. Если определить в той же таблице другой фильтр, например sales-OR, получится другая область. При определении фильтров учитывайте, что Sync Framework не выполняет автоматическое удаление строк, которые перестали соответствовать условию фильтра. Например, если пользователь или приложение обновляет значение в столбце, который используется для фильтрации, строка перемещается из одной области в другую. Строка передается в новую область, в которой она должна находиться, при этом строка не удаляется из прежней области. В этом случае обработка строк должна выполняться приложением.

Области могут различаться полностью или перекрываться. Области перекрывают друг друга, если они содержат одинаковые данные. Например, таблица products могла бы быть включена в область sales и в область inventory. Области могут как перекрываться, так и быть отфильтрованными. Следующие сценарии демонстрируют случаи, когда могут происходить фильтрация и перекрывание:

  • Сценарий 1.

    • Область 1 — sales-WA. В эту область входит products; orders с фильтром state=WA и order_details с фильтром state=WA.

    • Область 2 — sales-OR. В эту область входит products; orders с фильтром state=OR и order_details с фильтром state=OR.

    В данном сценарии вся таблица products является общей для обеих областей. Таблицы orders и order_details находятся в обеих областях, но фильтры не перекрывают друг друга, поэтому области не имеют одинаковых строк для этих таблиц.

  • Сценарий 2.

    • Область 1 — sales-WA. В эту область входит products; orders с фильтром state=WA и order_details с фильтром state=WA.

    • Область 2 — sales-Northwest. В эту область входит products; orders с фильтром state=WA OR state=ID и shippers.

    В данном сценарии вся таблица products снова является общей для обеих областей. Таблица orders входит в обе области, и фильтры перекрываются: обе области содержат строки, которые проходят фильтр state=WA. Таблицы shippers и order_details не являются общими для двух областей.

Области можно определять большим числом различных способов, однако необходимо придерживаться следующего правила: любые данные, синхронизируемые между парой баз данных в топологии синхронизации, могут принадлежать только одной области. Например, в приведенном выше сценарии 2 база данных А и база данных Б могут быть синхронизированы с областью 1; а база данных А и база данных В — с областью 2. База данных A и база данных Б не могут быть синхронизированы с областью 2, поскольку строки products и orders относятся к обеим областям.

Примеры кода

Примеры кода из этого раздела включают многие из описанных выше объектов и относятся к следующим областям.

  • Описание области и таблиц

  • Провизионирование сервера

  • Провизионирование клиентов

  • Задание параметров синхронизации

  • Синхронизация узлов

Изучив эти разделы, мы получим завершенное приложение командной строки, которое объединяет все эти примеры, а также дополнительный код, выполняющий синхронизацию топологии из четырех узлов. Топология состоит из сервера SQL Server, клиента SQL Server и двух клиентов SQL Server Compact.

Описание области и таблиц

В следующем примере кода описывается область filtered_customer, в которую добавляются две таблицы: Customer и CustomerContact. Эти таблицы уже существуют в серверной базе данных, поэтому для получения схемы из серверной базы данных используется метод GetDescriptionForTable. Включены все столбцы из таблицы Customer, а из таблицы CustomerContact включено только два столбца.

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)

Провизионирование сервера

В следующем примере кода создается объект провизионирования для области filtered_customer и указывается, что все объекты, связанные с синхронизацией, должны быть созданы в схеме базы данных с именем "Sync". В ходе провизионирования области код определяет фильтр для таблицы Customer. Синхронизироваться будут только строки, выполняющие условие фильтра. В коде для определения фильтра необходимы два компонента. При вызове метода AddFilterColumn указывается, что столбец CustomerType используется для фильтрации и что он должен быть добавлен к таблице отслеживания, которая используется для отслеживания изменений в таблице Customer. Вызов FilterClause указывает сам фильтр. Выражением фильтрации является предложение WHERE без ключевого слова WHERE. Псевдонимом [side] является псевдоним для таблицы отслеживания. Для таблицы CustomerContact фильтр не определяется, и поэтому синхронизироваться будут все строки из этой таблицы. После определения параметров провизионирования вызывается метод Apply, чтобы создать инфраструктуру отслеживания изменений в серверной базе данных, а скрипт провизионирования записывается в файл.

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

Провизионирование клиентов

В этом приложении провизионирование клиентов выполняется двумя различными способами.

  • Полная инициализация клиентской базы данных SQL Server или SQL Server Compact, основанная на сведениях области, получаемых от сервера или другой клиентской базы данных.

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

  • Инициализация моментального снимка клиентской базы данных SQL Server Compact с использованием существующей клиентской базы данных.

    Инициализация моментальных снимков используется для уменьшения времени, необходимого для инициализации клиентской базы данных. После того как одна клиентская база данных инициализирована путем полной инициализации, другие базы данных можно инициализировать с использованием моментального снимка первой клиентской базы данных. Моментальный снимок — это специально подготовленная база данных SQL Server Compact, содержащая схему таблиц, данные (необязательно) и инфраструктуру отслеживания изменений. Скопируйте этот моментальный снимок на каждый клиент, где требуется снимок. В ходе первого сеанса синхронизации для клиента обновляются метаданные, относящиеся к клиенту, и все изменения, произошедшие с момента создания моментального снимка, загружаются в клиентскую базу данных.

Важное примечаниеВажно!

Моментальные снимки следует создавать только во время, когда в базе данных SQL Server Compact не выполняются никакие действия. Выполнение операций любого типа параллельно с созданием моментального снимка не поддерживается.

Следующий пример кода сначала получает сведения области с сервера, а затем с помощью базовой таблицы и полученных схем отслеживания изменений выполняется подготовка клиентской базы данных SQL Server Compact. Затем код готовит клиентскую базу данных SQL Server на основе сведений области из клиентской базы данных 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()

В следующем примере кода создается моментальный снимок с именем SyncSampleClient2.sdf из базы данных SyncSampleClient1.sdf. Затем код синхронизирует SyncSampleClient2.sdf с серверной базой данных.

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

Задание параметров синхронизации

Следующий пример кода представляет собой конструктор из класса SampleSyncOrchestrator в приложении. Конструктор принимает два объекта RelationalSyncProvider, поскольку SqlSyncProvider и SqlCeSyncProvider являются производными от класса RelationalSyncProvider. Код указывает, какой поставщик является локальным, а какой — удаленным. Затем он указывает, что изменения следует сначала передать из удаленной базы данных в локальную, а затем в обратном направлении.

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

Синхронизация узлов

В следующем примере кода создаются экземпляры поставщиков для трех различных сеансов синхронизации: между сервером и клиентом SQL Server, между клиентом SQL Server и клиентом SQL Server Compact, между сервером и другим клиентом SQL Server Compact. В первых двух сеансах синхронизируется семь строк: все четыре строки из таблицы CustomerContact, а также три строки из таблицы Customer, которые соответствуют условию фильтрации. В третьем сеансе строки не синхронизируются, поскольку в моментальном снимке уже содержатся все данные из первой базы данных SQL Server Compact. Приложение выводит статистику, возвращаемую методом Synchronize().

Важное примечаниеВажно!

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

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

Полный пример кода

В следующий полный пример кода входят примеры, описанные ранее, а также дополнительный код, который выводит статистику синхронизации и сведения о событиях. Для работы примеру необходим класс 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);
            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

См. также

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

Архитектура и классы для синхронизации базы данных