다음을 통해 공유


방법: 공동 작업 동기화 구성 및 실행(SQL Server)

이 항목에서는 Sync Framework를 사용하여 SQL Server 및 SQL Server Compact 데이터베이스를 동기화하는 응용 프로그램의 주요 부분을 살펴봅니다. 이 응용 프로그램의 코드에서는 다음과 같은 Sync Framework 클래스를 중점적으로 다룹니다.

샘플 코드를 실행하는 방법에 대한 자세한 내용은 SQL Server와 SQL Server Compact 동기화의 "방법 항목의 예제 응용 프로그램"을 참조하십시오.

공동 작업 동기화에 대한 아키텍처 및 클래스에서 설명한 대로 동기화는 두 SqlSyncProvider 인스턴스, 두 SqlCeSyncProvider 인스턴스 또는 두 종류의 인스턴스 사이에서 발생할 수 있습니다. 이 항목에 있는 예제 코드는 2 계층 응용 프로그램에서 사용되는 것이므로 N 계층 구성을 필요로 하는 두 SqlCeSyncProvider 인스턴스 동기화는 확인할 수 없습니다. N 계층 구성의 예는 Sync Framework SDK에 포함된 WebSharingAppDemo-CEProviderEndToEnd 샘플을 참조하십시오.

공급자 유형 비교

이 항목에서는 Sync Framework 2.0에 도입된 두 가지 동기화 공급자 SqlSyncProviderSqlCeSyncProvider를 사용하여 SQL Server 및 SQL Server Compact 데이터베이스를 동기화하는 방법에 대해 설명합니다. Sync Framework에는 이러한 데이터베이스를 동기화할 수 있는 다른 공급자가 포함되어 있지만 다음과 같은 이유로 새로운 공급자가 일반적으로 태스크에 더욱 적합합니다.

  • SqlSyncProvider의 기능은 DbSyncProvider와 같지만 Sync Framework에서 데이터를 동기화하는 데 필요한 코드의 양이 적고 쿼리에 대한 지식이 많이 필요하지 않습니다. DbSyncProvider는 SQL Server 이외의 데이터베이스에 여전히 적합합니다.

  • SqlSyncProviderSqlCeSyncProvider는 클라이언트-서버, 피어 투 피어 및 혼합 토폴로지에 사용할 수 있는 반면 DbServerSyncProviderSqlCeClientSyncProvider는 클라이언트-서버 토폴로지에만 적합합니다. SqlSyncProviderSqlCeSyncProvider는 행 수 대신 데이터 크기에 따라 변경 내용을 일괄 처리하는 등의 고급 기능도 지원합니다.

  • SqlSyncProviderSqlCeSyncProvider는 유연하며 설치하기 쉽습니다. 이러한 공급자를 사용하여 SQL Server Express 및 SQL Server Compact를 포함한 모든 버전의 SQL Server를 동기화할 수 있습니다.

노드 설치 및 동기화 실행

토폴로지에서 노드를 동기화하는 두 가지 단계는 동기화할 노드를 설치하고 노드 쌍 간에 실제로 동기화를 실행하는 것입니다. SqlSyncProviderSqlCeSyncProvider의 경우 노드 설치는 두 가지 태스크로 구성됩니다.

  1. 동기화할 항목 정의

    하나 이상의 범위를 설명하여 동기화할 항목을 정의합니다. 범위는 일부 또는 전부가 필터링될 수 있는 테이블 집합입니다. 테이블은 데이터베이스에 이미 있거나 Sync Framework 개체를 사용하여 설명될 수 있습니다. 그런 다음 기본 저장소가 동기화될 때 런타임에 생성됩니다. 자세한 내용은 이 항목 뒷부분의 "범위 이해"를 참조하십시오.

    중요

    일단 범위가 동기화된 후 범위가 변경되어야 하면 기존 범위와 연결된 메타데이터를 삭제하고 다시 만들어야 합니다.

  2. Sync Framework 변경 내용 추적을 위한 데이터베이스 프로비전

    테이블 및 범위를 설명한 후 Sync Framework 개체를 사용하여 각 노드에 프로비전 스크립트를 적용합니다. 스크립트에서는 메타데이터 테이블, 트리거 및 저장 프로시저로 구성된 변경 내용 추적 및 변경 내용 적용 인프라를 만듭니다.

노드를 프로비전한 후 동기화할 수 있습니다. 개발자의 관점에서 보면 동기화 옵션 설치와 Synchronize() 호출은 매우 간단합니다. 내부적으로는 Sync Framework에서 사용자가 지정한 범위 및 테이블 설명 정보를 사용하여 각 범위 및 각 동기화 어댑터에 대한 구성 개체를 테이블당 하나씩 작성합니다. 이렇게 하면 Sync Framework에서 각 데이터베이스에 유지되는 정보를 가져오고 노드 쌍 간의 각 동기화 세션에 필요한 정보를 작성할 수 있습니다. SqlSyncProviderSqlCeSyncProvider 모두 프로비전하는 동안 만들어진 변경 내용 추적 테이블 및 기타 개체를 인식하며 필요한 DbSyncAdapter 개체를 자동으로 생성합니다. 이렇게 하면 이러한 공급자를 사용하여 데이터를 동기화하는 데 필요한 코드를 대폭 줄일 수 있습니다.

다음 표에서는 데이터베이스 및 공급자를 설치하는 데 사용되는 클래스를 나열합니다.

SQL Server SQL Server Compact 설명

DbSyncScopeDescription

DbSyncScopeDescription

한 단위로 동기화되는 테이블의 논리적인 그룹인 동기화 범위를 나타냅니다.

SqlSyncScopeProvisioning

SqlCeSyncScopeProvisioning

DbSyncScopeDescription 개체가 나타내는 특정 범위에 대한 SQL Server 또는 SQL Server Compact 데이터베이스 프로비전을 나타냅니다.

SqlSyncProviderScopeConfiguration

SqlCeSyncProviderScopeConfiguration

SqlSyncProvider 또는 SqlCeSyncProvider가 사용하는 특정 범위에 대한 구성 정보를 나타냅니다.

DbSyncTableDescription

DbSyncTableDescription

동기화 범위에 포함된 테이블의 스키마를 나타냅니다.

DbSyncColumnDescription

DbSyncColumnDescription

동기화 범위에 포함된 테이블의 일부인 열의 속성을 나타냅니다.

SqlSyncDescriptionBuilder

SqlCeSyncDescriptionBuilder

동기화와 관련된 SQL Server 또는 SQL Server Compact 데이터베이스에 대한 범위 및 테이블 정보를 나타냅니다. Description 개체를 SQL Server 또는 SQL Server Compact 데이터베이스 외부로 추출하는 데 사용됩니다.

SqlSyncTableProvisioning

SqlSyncTableProvisioning

DbSyncTableDescription 개체가 나타내는 SQL Server 또는 SQL Server Compact 데이터베이스 테이블(선택적 필터 포함)의 프로비전을 나타냅니다.

SqlSyncProviderAdapterConfiguration

SqlSyncProviderAdapterConfiguration

SQL Server 또는 SQL Server Compact 데이터베이스의 테이블에 대한 동기화 어댑터 구성 정보를 나타냅니다.

이러한 기본 형식 외에 알아 두어야 할 중요한 기타 형식 4개는 다음과 같습니다.

범위 이해

참고

항목의 이 섹션에서는 동기화 범위에 대한 추가 정보를 제공합니다. 지금 바로 "코드 예제"로 이동할 수 있지만 응용 프로그램에서 여러 범위 또는 필터링된 범위를 사용할 계획이라면 이 섹션을 읽어 보는 것이 좋습니다.

범위는 테이블과 필터의 조합이라는 점을 이해하는 것이 중요합니다. 예를 들어 customer_sales 테이블에서 워싱턴 주의 판매 데이터만 포함하는 sales-WA라는 필터링된 범위를 정의할 수 있습니다. 동일한 테이블에 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 테이블이 두 범위에서 공유됩니다. ordersorder_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 필터를 충족하는 행을 공유합니다. shippersorder_details 테이블은 범위 간에 공유되지 않습니다.

범위를 정의하는 방법은 다양하지만 동기화 토폴로지의 데이터베이스 쌍 사이에 동기화되는 모든 데이터는 한 범위에만 속할 수 있다는 원칙은 지켜야 합니다. 위의 시나리오 2를 예로 들면 데이터베이스 A와 데이터베이스 B가 범위 1을 동기화하고 데이터베이스 A와 데이터베이스 C가 범위 2를 동기화할 수 있습니다. 두 범위에 속하는 productsorders 행 때문에 데이터베이스 A와 데이터베이스 B가 범위 2를 동기화할 수 없습니다.

코드 예제

이 섹션의 코드 예제에서는 위에 설명된 개체가 다수 포함되고 다음 영역을 처리합니다.

  • 범위 및 테이블 설명

  • 서버 프로비전

  • 클라이언트 프로비전

  • 동기화 옵션 설정

  • 노드 동기화

이러한 각 영역을 처리한 후 이러한 각 예제와 추가 코드를 조합하여 네 가지 노드 토폴로지를 동기화하는 전체 콘솔 응용 프로그램을 제공합니다. 토폴로지는 SQL Server 서버, SQL Server 클라이언트 및 두 SQL Server Compact 클라이언트로 구성됩니다.

범위 및 테이블 설명

다음 코드 예제에서는 filtered_customer라는 범위를 설명하고 CustomerCustomerContact라는 두 테이블을 이 범위에 추가합니다. 테이블은 서버 데이터베이스에 이미 있으므로 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 테이블에 대한 필터를 정의합니다. 필터와 일치하는 행만 동기화됩니다. CustomerContact 테이블에는 필터가 정의되어 있지 않으므로 해당 테이블의 모든 행이 동기화됩니다. 프로비전 옵션을 정의한 후 Apply 메서드를 호출하여 서버 데이터베이스에 변경 내용 추적 인프라를 만들고 프로비전 스크립트를 파일로 작성합니다.

SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
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(serverConn);

// 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("SyncSamplesDb_SqlPeer1"));
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
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(serverConn)

' 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("SyncSamplesDb_SqlPeer1"))

클라이언트 프로비전

이 응용 프로그램에서는 클라이언트가 두 가지 방식으로 프로비전됩니다.

  • 서버 또는 다른 클라이언트 데이터베이스에서 검색된 범위 정보를 기반으로 SQL Server 또는 SQL Server Compact 클라이언트 데이터베이스 전체 초기화

    사용자 개체 및 동기화 개체는 SqlSyncDescriptionBuilderSqlCeSyncDescriptionBuilder 개체에서 제공하는 스키마 정보를 기반으로 클라이언트 데이터베이스에 만들어집니다. 첫 번째 동기화 세션의 일환으로 클라이언트 데이터베이스가 동기화할 수 있도록 준비되고 모든 행은 증분 삽입으로 클라이언트 데이터베이스에 다운로드됩니다.

  • 기존 클라이언트 데이터베이스를 사용한 SQL Server Compact 클라이언트 데이터베이스의 스냅숏 초기화

    스냅숏 초기화는 클라이언트 데이터베이스를 초기화하는 데 드는 시간을 줄이기 위해 디자인되었습니다. 전체 초기화를 사용하여 한 클라이언트 데이터베이스를 초기화한 후 이 처음 클라이언트 데이터베이스의 스냅숏을 사용하여 이후 데이터베이스를 초기화할 수 있습니다. 스냅숏은 테이블 스키마, 데이터(선택 사항) 및 변경 내용 추적 인프라를 포함하는 특별히 준비된 SQL Server Compact 데이터베이스입니다. 스냅숏이 필요한 각 클라이언트에 이 스냅숏을 복사합니다. 클라이언트에 대한 첫 번째 동기화 세션 동안 클라이언트 관련 메타데이터가 업데이트되고 스냅숏이 만들어진 이후 발생한 모든 변경 내용이 클라이언트 데이터베이스에 다운로드됩니다.

중요

스냅숏은 SQL Server Compact 데이터베이스에 작업이 없을 때만 생성해야 합니다. 스냅숏 생성 중에는 어떠한 형식의 작업도 동시에 실행할 수 없습니다.

다음 코드 예제에서는 먼저 서버에서 범위 정보를 검색하고 검색되는 기본 테이블 및 변경 내용 추적 스키마를 사용하여 SQL Server Compact 클라이언트 데이터베이스를 프로비전합니다. 이 코드에서는 그런 다음 SQL Server Compact 클라이언트 데이터베이스의 범위 정보를 기반으로 SQL Server 클라이언트 데이터베이스를 프로비전합니다.

// 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(clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply(clientSqlCe1Conn);

// 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(clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply(clientSqlConn);
' 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(clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply(clientSqlCe1Conn)


' 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(clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply(clientSqlConn)

다음 코드 예제에서는 SyncSampleClient1.sdf 데이터베이스에서 SyncSampleClient2.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 클래스의 생성자입니다. SqlSyncProviderSqlCeSyncProvider 모두 RelationalSyncProvider에서 파생되므로 생성자는 두 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 클라이언트 간 세션입니다. 처음 두 세션 중 행 7개(CustomerContact의 행 4개 모두와 필터링 조건을 충족하는 Customer의 행 3개)가 동기화됩니다. 스냅숏에 첫 번째 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 클래스가 필요합니다.

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
            // base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1),
            // and 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(scopeDesc);
            serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
            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(serverConn);

            // 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("SyncSamplesDb_SqlPeer1"));


            // 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(clientSqlCe1Desc);
            clientSqlCe1Config.ObjectPrefix = "Sync";
            clientSqlCe1Config.Apply(clientSqlCe1Conn);

            // 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(clientSqlDesc);
            clientSqlConfig.ObjectSchema = "Sync";
            clientSqlConfig.Apply(clientSqlConn);


            // 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 
            ' base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1), 
            ' and 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(scopeDesc)
            serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
            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(serverConn)

            ' 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("SyncSamplesDb_SqlPeer1"))


            ' 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(clientSqlCe1Desc)
            clientSqlCe1Config.ObjectPrefix = "Sync"
            clientSqlCe1Config.Apply(clientSqlCe1Conn)


            ' 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(clientSqlDesc)
            clientSqlConfig.ObjectSchema = "Sync"
            clientSqlConfig.Apply(clientSqlConn)


            ' 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

참고 항목

개념

공동 작업 동기화 개요
공동 작업 동기화에 대한 아키텍처 및 클래스