다음을 통해 공유


방법: 변경 내용의 순서 및 일괄 처리 크기 지정

이 항목에서는 Sync Framework를 사용하여 클라이언트 데이터베이스에 다운로드되는 변경 내용의 순서와 일괄 처리 크기를 지정하는 방법에 대해 설명합니다. 이 항목의 예제에서는 다음과 같은 Sync Framework 형식 및 이벤트를 중점적으로 설명합니다.

샘플 코드를 실행하는 방법에 대한 자세한 내용은 일반적인 클라이언트 및 서버 동기화 태스크 프로그래밍의 "방법 항목의 예제 응용 프로그램"을 참조하십시오.

동기화 순서 및 일괄 처리 이해

동기화되는 각 테이블의 변경 내용이 삽입, 업데이트 및 삭제의 순서로 서버 데이터베이스에서 선택됩니다. 변경 내용은 삭제, 삽입 및 업데이트의 순서로 클라이언트 데이터베이스에 적용됩니다. 여러 테이블이 동기화되는 경우 각 테이블이 처리되는 순서는 SyncTable 개체가 동기화 에이전트에 대한 테이블의 컬렉션에 추가된 순서에 따라 결정됩니다. 예를 들어 CustomerOrderHeader 테이블이 나열된 순서로 추가되는 경우 Customer 테이블 삽입이 먼저 선택된 다음 업데이트 및 삭제가 선택됩니다. 그런 다음 OrderHeader 테이블의 변경 내용이 선택됩니다. 모든 Customer 테이블 변경 내용이 단일 트랜잭션에서 클라이언트 데이터베이스에 적용된 다음(일괄 처리가 사용되지 않는 경우) OrderHeader 변경 내용이 두 번째 트랜잭션에서 적용됩니다. CustomerOrderHeader 테이블이 동일한 SyncGroup 개체에 할당된 경우 두 테이블의 삽입, 업데이트 및 삭제가 한 번에 선택됩니다. 모든 변경 내용이 단일 트랜잭션에서 클라이언트 데이터베이스에 적용됩니다(일괄 처리가 사용되지 않는 경우).

기본적으로 Sync Framework는 변경 내용을 일괄 처리로 나누지 않습니다. 변경 내용은 한 단위로 클라이언트 데이터베이스로 다운로드되고 클라이언트 데이터베이스에서 업로드됩니다. 많은 응용 프로그램의 경우 변경 내용을 더 작은 일괄 처리로 나누는 것이 적합합니다. 예를 들어, 동기화 세션이 중단되는 경우 모든 변경 내용을 다시 보내는 대신 마지막 일괄 처리에서 동기화가 다시 시작될 수 있습니다. 클라이언트에서 한 번에 더 작은 일괄 변경 내용을 관리할 수 있기 때문에 성능도 향상될 수 있습니다. 이러한 이점 때문에 응용 프로그램에서는 Sync Framework를 통해 일괄 변경 내용을 클라이언트로 다운로드할 수 있습니다(업로드 시에는 일괄 처리가 지원되지 않음).

일괄 처리를 사용하려면 BatchSize 속성의 값을 지정하고 변경 내용의 각 일괄 처리에 대한 앵커 값을 반환할 수 있는 SelectNewAnchorCommand 속성에 대한 명령을 만듭니다. 일괄 처리를 사용하지 않는 경우 응용 프로그램에서는 새 앵커 및 마지막 앵커 값을 사용하여 다운로드할 변경 내용의 전체 집합에 대한 상한과 하한을 정의합니다. 자세한 내용은 시작: 클라이언트 및 서버 동기화를 참조하십시오. 일괄 처리를 사용하는 경우 받은 최대 앵커 값이 변경 내용의 전체 집합에 대한 상한을 정의하고 새 앵커 값과 마지막 앵커 값이 각 일괄 변경 내용에 대한 상한과 하한을 정의합니다. SessionProgress 이벤트를 사용하여 전체적인 동기화 진행률을 간편하게 모니터링할 수 있으며 BatchProgress 속성을 통해 일괄 처리 수준에서 진행률 정보에 액세스할 수 있습니다.

예제

다음 코드 예제에서는 Sync Framework 샘플 데이터베이스의 Customer OrderHeader 테이블을 동기화하는 방법을 보여 줍니다. 이러한 테이블의 변경 내용은 일괄 처리로 다운로드되며 일괄 처리당 50개의 변경 내용이 포함됩니다. 초기 동기화에서는 10행을 다운로드합니다. 모든 행이 단일 일괄 처리로 다운로드되며 단일 트랜잭션에서 적용됩니다. 이후 동기화에서는 두 일괄 처리로 92행을 다운로드합니다. 각 일괄 처리에는 Customer 테이블과 OrderHeader 테이블의 변경 내용이 포함되어 있으며 각 일괄 처리는 단일 트랜잭션에서 적용됩니다.

API의 주요 요소

이 단원에서는 변경 내용의 순서 지정과 일괄 처리에 사용하는 API의 주요 부분을 보여 주는 코드 예제를 제공합니다. 다음 코드 예제는 SyncAgent에서 파생된 클래스에서 제공된 것입니다. 이 코드에서는 CustomerOrderHeader 테이블에 대한 SyncGroup 개체를 만듭니다.

//Create a SyncGroup so that changes to Customer
//and OrderHeader are made in one transaction.
SyncGroup customerOrderSyncGroup = new SyncGroup("CustomerOrder");

//Add each table: specify a synchronization direction of
//DownloadOnly.
SyncTable customerSyncTable = new SyncTable("Customer");
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerSyncTable.SyncDirection = SyncDirection.DownloadOnly;
customerSyncTable.SyncGroup = customerOrderSyncGroup;
this.Configuration.SyncTables.Add(customerSyncTable);

SyncTable orderHeaderSyncTable = new SyncTable("OrderHeader");
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly;
orderHeaderSyncTable.SyncGroup = customerOrderSyncGroup;
this.Configuration.SyncTables.Add(orderHeaderSyncTable);
'Create a SyncGroup so that changes to Customer
'and OrderHeader are made in one transaction.
Dim customerOrderSyncGroup As New SyncGroup("CustomerOrder")

'Add each table: specify a synchronization direction of
'DownloadOnly.
Dim customerSyncTable As New SyncTable("Customer")
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
customerSyncTable.SyncDirection = SyncDirection.DownloadOnly
customerSyncTable.SyncGroup = customerOrderSyncGroup
Me.Configuration.SyncTables.Add(customerSyncTable)

Dim orderHeaderSyncTable As New SyncTable("OrderHeader")
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly
orderHeaderSyncTable.SyncGroup = customerOrderSyncGroup
Me.Configuration.SyncTables.Add(orderHeaderSyncTable)

다음 코드 예제는 DbServerSyncProvider에서 파생된 클래스에서 제공된 것입니다. 이 코드에서는 일괄 처리를 사용하지 않는 응용 프로그램에 대한 앵커 명령을 만듭니다. 또한 동기화할 변경 내용의 전체 집합에 대한 새 앵커 값을 한 번에 반환합니다. 이 예제를 통해 일괄 처리를 사용하는 명령과 일괄 처리를 사용하지 않는 명령의 차이점을 쉽게 확인할 수 있습니다.

SqlCommand selectNewAnchorCommand = new SqlCommand();
string newAnchorVariable = "@" + SyncSession.SyncNewReceivedAnchor;
selectNewAnchorCommand.CommandText = "SELECT " + newAnchorVariable + " = min_active_rowversion() - 1";
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.Timestamp);
selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Connection = serverConn;
this.SelectNewAnchorCommand = selectNewAnchorCommand;
Dim selectNewAnchorCommand As New SqlCommand()
Dim newAnchorVariable As String = "@" + SyncSession.SyncNewReceivedAnchor
selectNewAnchorCommand.CommandText = "SELECT " + newAnchorVariable + " = min_active_rowversion() - 1"
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.Timestamp)
selectNewAnchorCommand.Parameters(newAnchorVariable).Direction = ParameterDirection.Output
selectNewAnchorCommand.Connection = serverConn
Me.SelectNewAnchorCommand = selectNewAnchorCommand

다음 코드 예제에서는 변경 내용이 일괄 처리로 전달되는 경우 사용할 수 있는 앵커 명령을 만듭니다. 이 예제에서는 변경 내용의 전체 집합에 대한 새 앵커 값을 한 번에 반환하는 대신 각 일괄 변경 내용에 대한 새 앵커 값을 반환합니다. 또한 BatchSize 속성을 사용하여 각 일괄 처리에 포함될 변경 내용의 수와 저장 프로시저와 동기화 런타임 간에 앵커 값을 전달하는 데 필요한 세션 변수를 지정합니다. 동기화 어댑터 명령을 수동으로 작성하는 경우 @sync_new_received_anchor@sync_last_received_anchor 세션 변수를 사용합니다. @sync_max_received_anchor 세션 변수는 새 앵커 명령에서만 사용됩니다.

SqlCommand selectNewAnchorCommand = new SqlCommand();
selectNewAnchorCommand.Connection = serverConn;
selectNewAnchorCommand.CommandText = "usp_GetNewBatchAnchor";
selectNewAnchorCommand.CommandType = CommandType.StoredProcedure;            
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp, 8);
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncMaxReceivedAnchor, SqlDbType.Timestamp, 8);
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp, 8);
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchSize, SqlDbType.Int, 4);
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchCount, SqlDbType.Int, 4);            

selectNewAnchorCommand.Parameters["@" + SyncSession.SyncMaxReceivedAnchor].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Parameters["@" + SyncSession.SyncNewReceivedAnchor].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Parameters["@" + SyncSession.SyncBatchCount].Direction = ParameterDirection.InputOutput;
this.SelectNewAnchorCommand = selectNewAnchorCommand;
this.BatchSize = 50;
Dim selectNewAnchorCommand As New SqlCommand()
selectNewAnchorCommand.Connection = serverConn
selectNewAnchorCommand.CommandText = "usp_GetNewBatchAnchor"
selectNewAnchorCommand.CommandType = CommandType.StoredProcedure
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp, 8)
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncMaxReceivedAnchor, SqlDbType.Timestamp, 8)
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp, 8)
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchSize, SqlDbType.Int, 4)
selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchCount, SqlDbType.Int, 4)

selectNewAnchorCommand.Parameters("@" + SyncSession.SyncMaxReceivedAnchor).Direction = ParameterDirection.Output
selectNewAnchorCommand.Parameters("@" + SyncSession.SyncNewReceivedAnchor).Direction = ParameterDirection.Output
selectNewAnchorCommand.Parameters("@" + SyncSession.SyncBatchCount).Direction = ParameterDirection.InputOutput
Me.SelectNewAnchorCommand = selectNewAnchorCommand
Me.BatchSize = 50

다음 코드 예제에서는 새 앵커 값, 최대 앵커 값, 삽입 및 업데이트에 대한 현재 일괄 처리 수를 출력하는 저장 프로시저를 만듭니다. 서버 동기화 공급자는 이 프로시저를 통해 서버 데이터베이스에서 일괄 변경 내용을 선택할 수 있습니다. 이 저장 프로시저의 논리는 하나의 예이지만 여기에 나와 있는 출력 값을 제공하는 논리라면 어떤 논리든 사용할 수 있습니다. 이 예제 코드의 한 가지 단점은 행이 동기화 간에 50회 넘게 변경되면 빈 일괄 처리가 발생할 수 있다는 것입니다. 이러한 경우를 처리하는 논리를 추가할 수 있습니다.

CREATE PROCEDURE usp_GetNewBatchAnchor (
    @sync_last_received_anchor timestamp, 
    @sync_batch_size bigint,
    @sync_max_received_anchor timestamp out,
    @sync_new_received_anchor timestamp out,            
    @sync_batch_count int output)            
AS            
       -- Set a default batch size if a valid one is not passed in.
       IF  @sync_batch_size IS NULL OR @sync_batch_size <= 0
         SET @sync_batch_size = 1000    

       -- Before selecting the first batch of changes,
       -- set the maximum anchor value for this synchronization session.
       -- After the first time that this procedure is called, 
       -- Sync Framework passes a value for @sync_max_received_anchor
       -- to the procedure. Batches of changes are synchronized until this 
       -- value is reached.
       IF @sync_max_received_anchor IS NULL
         SELECT  @sync_max_received_anchor = MIN_ACTIVE_ROWVERSION() - 1
       
       -- If this is the first synchronization session for a database,
       -- get the lowest timestamp value from the tables. By default,
       -- Sync Framework uses a value of 0 for @sync_last_received_anchor
       -- on the first synchronization. If you do not set @sync_last_received_anchor,
       -- this can cause empty batches to be downloaded until the lowest
       -- timestamp value is reached.
       IF @sync_last_received_anchor IS NULL OR @sync_last_received_anchor = 0
       BEGIN
                
        SELECT @sync_last_received_anchor = MIN(TimestampCol) FROM (
          SELECT MIN(UpdateTimestamp) AS TimestampCol FROM Sales.Customer
          UNION
          SELECT MIN(InsertTimestamp) AS TimestampCol FROM Sales.Customer
          UNION
          SELECT MIN(UpdateTimestamp) AS TimestampCol FROM Sales.OrderHeader
          UNION
          SELECT MIN(InsertTimestamp) AS TimestampCol FROM Sales.OrderHeader
        ) MinTimestamp  
       
        SET @sync_new_received_anchor = @sync_last_received_anchor + @sync_batch_size

        -- Determine how many batches are required during the initial synchronization.
        IF @sync_batch_count <= 0
          SET @sync_batch_count = ((@sync_max_received_anchor / @sync_batch_size) - (@sync_last_received_anchor /  @sync_batch_size))

        END

       ELSE
       BEGIN

        SET @sync_new_received_anchor = @sync_last_received_anchor + @sync_batch_size

        -- Determine how many batches are required during subsequent synchronizations.
        IF @sync_batch_count <= 0
          SET @sync_batch_count = ((@sync_max_received_anchor / @sync_batch_size) - (@sync_new_received_anchor /  @sync_batch_size)) + 1  
       
       END

       -- Check whether this is the last batch.      
       IF @sync_new_received_anchor >= @sync_max_received_anchor
       BEGIN

         SET @sync_new_received_anchor = @sync_max_received_anchor        
         IF @sync_batch_count <= 0
           SET @sync_batch_count = 1

       END
GO

전체 코드 예제

다음의 전체 코드 예제에는 위에서 설명한 코드 예제와 동기화를 수행하는 데 필요한 추가 코드가 포함되어 있습니다. 이 예제를 사용하려면 데이터베이스 공급자용 유틸리티 클래스 방법 항목에 나와 있는 Utility 클래스가 필요합니다.

using System;
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.Server;
using Microsoft.Synchronization.Data.SqlServerCe;

namespace Microsoft.Samples.Synchronization
{
    class Program
    {
        static void Main(string[] args)
        {

            //The SampleStats class handles information from the 
            //SyncStatistics object that the Synchronize method returns and
            //from SyncAgent events.
            SampleStats sampleStats = new SampleStats();

            //Request a password for the client database, and delete
            //and re-create the database. The client synchronization
            //provider also enables you to create the client database 
            //if it does not exist.
            Utility.SetPassword_SqlCeClientSync();
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeClientSync, true);
            
            //Initial synchronization. Instantiate the SyncAgent
            //and call Synchronize.
            SampleSyncAgent sampleSyncAgent = new SampleSyncAgent();
            SyncStatistics syncStatistics = sampleSyncAgent.Synchronize();
            sampleStats.DisplayStats(syncStatistics, "initial");

            //Make changes on the server.
            Utility.MakeDataChangesOnServer("CustomerAndOrderHeader");

            //Subsequent synchronization.
            syncStatistics = sampleSyncAgent.Synchronize();
            sampleStats.DisplayStats(syncStatistics, "subsequent");

            //Return server data back to its original state.
            Utility.CleanUpServer();

            //Exit.
            Console.Write("\nPress Enter to close the window.");
            Console.ReadLine();
        }
    }

    //Create a class that is derived from 
    //Microsoft.Synchronization.SyncAgent.
    public class SampleSyncAgent : SyncAgent
    {
        public SampleSyncAgent()
        {
            //Instantiate a client synchronization provider and specify it
            //as the local provider for this synchronization agent.
            this.LocalProvider = new SampleClientSyncProvider();

            //Instantiate a server synchronization provider and specify it
            //as the remote provider for this synchronization agent.
            this.RemoteProvider = new SampleServerSyncProvider();

            //Create a SyncGroup so that changes to Customer
            //and OrderHeader are made in one transaction.
            SyncGroup customerOrderSyncGroup = new SyncGroup("CustomerOrder");

            //Add each table: specify a synchronization direction of
            //DownloadOnly.
            SyncTable customerSyncTable = new SyncTable("Customer");
            customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            customerSyncTable.SyncDirection = SyncDirection.DownloadOnly;
            customerSyncTable.SyncGroup = customerOrderSyncGroup;
            this.Configuration.SyncTables.Add(customerSyncTable);

            SyncTable orderHeaderSyncTable = new SyncTable("OrderHeader");
            orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly;
            orderHeaderSyncTable.SyncGroup = customerOrderSyncGroup;
            this.Configuration.SyncTables.Add(orderHeaderSyncTable);
        }
    }

    //Create a class that is derived from 
    //Microsoft.Synchronization.Server.DbServerSyncProvider.
    public class SampleServerSyncProvider : DbServerSyncProvider
    {
        public SampleServerSyncProvider()
        {
            //Create a connection to the sample server database.
            Utility util = new Utility();
            SqlConnection serverConn = new SqlConnection(Utility.ConnStr_DbServerSync);
            this.Connection = serverConn;

            //Create a command to retrieve a new anchor value from
            //the server. In this case, we call a stored procedure
            //that returns an anchor that can be used with batches
            //of changes.
            SqlCommand selectNewAnchorCommand = new SqlCommand();
            selectNewAnchorCommand.Connection = serverConn;
            selectNewAnchorCommand.CommandText = "usp_GetNewBatchAnchor";
            selectNewAnchorCommand.CommandType = CommandType.StoredProcedure;            
            selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp, 8);
            selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncMaxReceivedAnchor, SqlDbType.Timestamp, 8);
            selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp, 8);
            selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchSize, SqlDbType.Int, 4);
            selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchCount, SqlDbType.Int, 4);            

            selectNewAnchorCommand.Parameters["@" + SyncSession.SyncMaxReceivedAnchor].Direction = ParameterDirection.Output;
            selectNewAnchorCommand.Parameters["@" + SyncSession.SyncNewReceivedAnchor].Direction = ParameterDirection.Output;
            selectNewAnchorCommand.Parameters["@" + SyncSession.SyncBatchCount].Direction = ParameterDirection.InputOutput;
            this.SelectNewAnchorCommand = selectNewAnchorCommand;
            this.BatchSize = 50;
            
            //Create SyncAdapters for each table by using the SqlSyncAdapterBuilder:
            //  * Specify the base table and tombstone table names.
            //  * Specify the columns that are used to track when
            //    and where changes are made.
            //  * Specify download only synchronization.
            //  * Call ToSyncAdapter to create the SyncAdapter.
            //  * Specify a name for the SyncAdapter that matches the
            //    the name specified for the corresponding SyncTable.
            //    Do not include the schema names (Sales in this case).

            //Customer table
            SqlSyncAdapterBuilder customerBuilder = new SqlSyncAdapterBuilder(serverConn);

            customerBuilder.TableName = "Sales.Customer";
            customerBuilder.TombstoneTableName = customerBuilder.TableName + "_Tombstone";
            customerBuilder.SyncDirection = SyncDirection.DownloadOnly;
            customerBuilder.CreationTrackingColumn = "InsertTimestamp";
            customerBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            customerBuilder.DeletionTrackingColumn = "DeleteTimestamp";
            
            SyncAdapter customerSyncAdapter = customerBuilder.ToSyncAdapter();
            customerSyncAdapter.TableName = "Customer";
            this.SyncAdapters.Add(customerSyncAdapter);


            //OrderHeader table.
            SqlSyncAdapterBuilder orderHeaderBuilder = new SqlSyncAdapterBuilder(serverConn);

            orderHeaderBuilder.TableName = "Sales.OrderHeader";
            orderHeaderBuilder.TombstoneTableName = orderHeaderBuilder.TableName + "_Tombstone";
            orderHeaderBuilder.SyncDirection = SyncDirection.DownloadOnly;
            orderHeaderBuilder.CreationTrackingColumn = "InsertTimestamp";
            orderHeaderBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            orderHeaderBuilder.DeletionTrackingColumn = "DeleteTimestamp";

            SyncAdapter orderHeaderSyncAdapter = orderHeaderBuilder.ToSyncAdapter();
            orderHeaderSyncAdapter.TableName = "OrderHeader";
            this.SyncAdapters.Add(orderHeaderSyncAdapter);

            //Handle the ChangesSelected event, and display
            //information to the console.
            this.ChangesSelected += new EventHandler<ChangesSelectedEventArgs>(SampleServerSyncProvider_ChangesSelected);
        }

        public void SampleServerSyncProvider_ChangesSelected(object sender, ChangesSelectedEventArgs e)
        {
            Console.WriteLine("Total number of batches: " + e.Context.BatchCount);
            Console.WriteLine("Changes applied for group " + e.GroupMetadata.GroupName);
            Console.WriteLine("Inserts applied for group: " + e.Context.GroupProgress.TotalInserts.ToString());
            Console.WriteLine("Updates applied for group: " + e.Context.GroupProgress.TotalUpdates.ToString());
            Console.WriteLine("Deletes applied for group: " + e.Context.GroupProgress.TotalDeletes.ToString());
        }
    }

    //Create a class that is derived from 
    //Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
    public class SampleClientSyncProvider : SqlCeClientSyncProvider
    {

        public SampleClientSyncProvider()
        {
            //Specify a connection string for the sample client database.
            Utility util = new Utility();
            this.ConnectionString = Utility.ConnStr_SqlCeClientSync;         
        }
    }

    //Handle the statistics that are returned by the SyncAgent.
    public class SampleStats
    {
        public void DisplayStats(SyncStatistics syncStatistics, string syncType)
        {
            Console.WriteLine(String.Empty);
            if (syncType == "initial")
            {
                Console.WriteLine("****** Initial Synchronization Stats ******");
            }
            else if (syncType == "subsequent")
            {
                Console.WriteLine("***** Subsequent Synchronization Stats ****");
            }

            Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
            Console.WriteLine("Total Changes Downloaded: " + syncStatistics.TotalChangesDownloaded);
            Console.WriteLine("Complete Time: " + syncStatistics.SyncCompleteTime);
            Console.WriteLine(String.Empty);
        }
    }         
}
Imports System
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.Server
Imports Microsoft.Synchronization.Data.SqlServerCe

Class Program

    Shared Sub Main(ByVal args() As String)

        'The SampleStats class handles information from the 
        'SyncStatistics object that the Synchronize method returns and
        'from SyncAgent events.
        Dim sampleStats As New SampleStats()

        'Request a password for the client database, and delete
        'and re-create the database. The client synchronization
        'provider also enables you to create the client database 
        'if it does not exist.
        Utility.SetPassword_SqlCeClientSync()
        Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeClientSync, True)

        'Initial synchronization. Instantiate the SyncAgent
        'and call Synchronize.
        Dim sampleSyncAgent As New SampleSyncAgent()
        Dim syncStatistics As SyncStatistics = sampleSyncAgent.Synchronize()
        sampleStats.DisplayStats(syncStatistics, "initial")

        'Make changes on the server.
        Utility.MakeDataChangesOnServer("CustomerAndOrderHeader")

        'Subsequent synchronization.
        syncStatistics = sampleSyncAgent.Synchronize()
        sampleStats.DisplayStats(syncStatistics, "subsequent")

        'Return server data back to its original state.
        Utility.CleanUpServer()

        'Exit.
        Console.Write(vbLf + "Press Enter to close the window.")
        Console.ReadLine()

    End Sub 'Main
End Class 'Program

'Create a class that is derived from 
'Microsoft.Synchronization.SyncAgent.

Public Class SampleSyncAgent
    Inherits SyncAgent

    Public Sub New()
        'Instantiate a client synchronization provider and specify it
        'as the local provider for this synchronization agent.
        Me.LocalProvider = New SampleClientSyncProvider()

        'Instantiate a server synchronization provider and specify it
        'as the remote provider for this synchronization agent.
        Me.RemoteProvider = New SampleServerSyncProvider()

        'Create a SyncGroup so that changes to Customer
        'and OrderHeader are made in one transaction.
        Dim customerOrderSyncGroup As New SyncGroup("CustomerOrder")

        'Add each table: specify a synchronization direction of
        'DownloadOnly.
        Dim customerSyncTable As New SyncTable("Customer")
        customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
        customerSyncTable.SyncDirection = SyncDirection.DownloadOnly
        customerSyncTable.SyncGroup = customerOrderSyncGroup
        Me.Configuration.SyncTables.Add(customerSyncTable)

        Dim orderHeaderSyncTable As New SyncTable("OrderHeader")
        orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
        orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly
        orderHeaderSyncTable.SyncGroup = customerOrderSyncGroup
        Me.Configuration.SyncTables.Add(orderHeaderSyncTable)

End Class 'SampleSyncAgent

'Create a class that is derived from 
'Microsoft.Synchronization.Server.DbServerSyncProvider.

Public Class SampleServerSyncProvider
    Inherits DbServerSyncProvider

    Public Sub New()
        'Create a connection to the sample server database.
        Dim util As New Utility()
        Dim serverConn As New SqlConnection(Utility.ConnStr_DbServerSync)
        Me.Connection = serverConn

        'Create a command to retrieve a new anchor value from
        'the server. In this case, we call a stored procedure
        'that returns an anchor that can be used with batches
        'of changes.
        Dim selectNewAnchorCommand As New SqlCommand()
        selectNewAnchorCommand.Connection = serverConn
        selectNewAnchorCommand.CommandText = "usp_GetNewBatchAnchor"
        selectNewAnchorCommand.CommandType = CommandType.StoredProcedure
        selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp, 8)
        selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncMaxReceivedAnchor, SqlDbType.Timestamp, 8)
        selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp, 8)
        selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchSize, SqlDbType.Int, 4)
        selectNewAnchorCommand.Parameters.Add("@" + SyncSession.SyncBatchCount, SqlDbType.Int, 4)

        selectNewAnchorCommand.Parameters("@" + SyncSession.SyncMaxReceivedAnchor).Direction = ParameterDirection.Output
        selectNewAnchorCommand.Parameters("@" + SyncSession.SyncNewReceivedAnchor).Direction = ParameterDirection.Output
        selectNewAnchorCommand.Parameters("@" + SyncSession.SyncBatchCount).Direction = ParameterDirection.InputOutput
        Me.SelectNewAnchorCommand = selectNewAnchorCommand
        Me.BatchSize = 50
        'Create SyncAdapters for each table by using the SqlSyncAdapterBuilder:
        '  * Specify the base table and tombstone table names.
        '  * Specify the columns that are used to track when
        '    and where changes are made.
        '  * Specify download only synchronization.
        '  * Call ToSyncAdapter to create the SyncAdapter.
        '  * Specify a name for the SyncAdapter that matches the
        '    the name specified for the corresponding SyncTable.
        '    Do not include the schema names (Sales in this case).
        'Customer table
        Dim customerBuilder As New SqlSyncAdapterBuilder(serverConn)

        customerBuilder.TableName = "Sales.Customer"
        customerBuilder.TombstoneTableName = customerBuilder.TableName + "_Tombstone"
        customerBuilder.SyncDirection = SyncDirection.DownloadOnly
        customerBuilder.CreationTrackingColumn = "InsertTimestamp"
        customerBuilder.UpdateTrackingColumn = "UpdateTimestamp"
        customerBuilder.DeletionTrackingColumn = "DeleteTimestamp"

        Dim customerSyncAdapter As SyncAdapter = customerBuilder.ToSyncAdapter()
        customerSyncAdapter.TableName = "Customer"
        Me.SyncAdapters.Add(customerSyncAdapter)


        'OrderHeader table.
        Dim orderHeaderBuilder As New SqlSyncAdapterBuilder(serverConn)

        orderHeaderBuilder.TableName = "Sales.OrderHeader"
        orderHeaderBuilder.TombstoneTableName = orderHeaderBuilder.TableName + "_Tombstone"
        orderHeaderBuilder.SyncDirection = SyncDirection.DownloadOnly
        orderHeaderBuilder.CreationTrackingColumn = "InsertTimestamp"
        orderHeaderBuilder.UpdateTrackingColumn = "UpdateTimestamp"
        orderHeaderBuilder.DeletionTrackingColumn = "DeleteTimestamp"

        Dim orderHeaderSyncAdapter As SyncAdapter = orderHeaderBuilder.ToSyncAdapter()
        orderHeaderSyncAdapter.TableName = "OrderHeader"
        Me.SyncAdapters.Add(orderHeaderSyncAdapter)

        'Handle the ChangesSelected event, and display
        'information to the console.
        AddHandler Me.ChangesSelected, AddressOf SampleServerSyncProvider_ChangesSelected

    End Sub 'New


    Public Sub SampleServerSyncProvider_ChangesSelected(ByVal sender As Object, ByVal e As ChangesSelectedEventArgs)
        Console.WriteLine("Total number of batches: " & e.Context.BatchCount)
        Console.WriteLine("Changes applied for group " & e.GroupMetadata.GroupName)
        Console.WriteLine("Inserts applied for group: " & e.Context.GroupProgress.TotalInserts.ToString())
        Console.WriteLine("Updates applied for group: " & e.Context.GroupProgress.TotalUpdates.ToString())
        Console.WriteLine("Deletes applied for group: " & e.Context.GroupProgress.TotalDeletes.ToString())

    End Sub 'SampleServerSyncProvider_ChangesSelected
End Class 'SampleServerSyncProvider

'Create a class that is derived from 
'Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
Public Class SampleClientSyncProvider
    Inherits SqlCeClientSyncProvider


    Public Sub New()
        'Specify a connection string for the sample client database.
        Dim util As New Utility()
        Me.ConnectionString = Utility.ConnStr_SqlCeClientSync

    End Sub 'New
End Class 'SampleClientSyncProvider

'Handle the statistics that are returned by the SyncAgent.
Public Class SampleStats

    Public Sub DisplayStats(ByVal syncStatistics As SyncStatistics, ByVal syncType As String)
        Console.WriteLine(String.Empty)
        If syncType = "initial" Then
            Console.WriteLine("****** Initial Synchronization Stats ******")
        ElseIf syncType = "subsequent" Then
            Console.WriteLine("***** Subsequent Synchronization Stats ****")
        End If

        Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
        Console.WriteLine("Total Changes Downloaded: " & syncStatistics.TotalChangesDownloaded)
        Console.WriteLine("Complete Time: " & syncStatistics.SyncCompleteTime)
        Console.WriteLine(String.Empty)

    End Sub 'DisplayStats
End Class 'SampleStats

참고 항목

개념

일반적인 클라이언트 및 서버 동기화 태스크 프로그래밍