다음을 통해 공유


방법: 데이터베이스 동기화를 위한 데이터 필터링(SQL Server)

이 항목에서는 Sync Framework와 함께 사용하여 SQL Server, SQL Azure 및 SQL Server Compact 데이터베이스를 동기화할 수 있는 필터를 만드는 방법을 보여 줍니다. 이 항목의 예제에서는 다음과 같은 Sync Framework 클래스 및 멤버를 중점적으로 설명합니다.

SqlSyncScopeProvisioning

PopulateFromTemplate

FilterClause

FilterParameters

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

필터 이해

필터는 동기화 세션에서 원본 공급자가 열거하는 항목을 제어하는 데 사용됩니다. 항목이 동기화되고 대상 공급자에게 전송되면 원본 공급자는 항목이 필터를 통과할 때만 해당 항목을 포함시킵니다. 행의 필터 필드가 필터 조건을 충족하는 경우에만 행이 필터를 통과할 수 있도록 필터는 대개 하나 이상의 필드 값을 기반으로 합니다.

Sync Framework를 사용하여 정적 필터와 매개 변수 기반 필터라는 두 가지 유형의 필터를 만들 수 있습니다. 정적 필터는 동기화 범위의 일부로 정의되며 필터를 통과하기 위해 필터 필드에 포함되어야 하는 값을 정의합니다. 정적 필터는 원본 데이터베이스에서 범위에 대한 변경 내용을 열거하기 위해 사용하는 저장 프로시저에 인코딩됩니다. 정적 필터를 정의한 후에는 변경할 수 없습니다. 정적 필터에 대한 자세한 내용 및 정적 필터를 사용하는 방법에 대한 예를 보려면 방법: 데이터베이스 동기화 구성 및 실행(SQL Server)을 참조하십시오. 매개 변수 기반 필터는 필터 절에 의해 정의되며 동기화 범위에서 테이블 열에 매핑되는 매개 변수 집합입니다. 매개 변수 기반 필터는 두 단계로 정의됩니다. 첫 단계에서는 필터 절과 매개 변수를 정의하고 필터와 관련된 범위에 대한 설명을 작성합니다. 이 단계에서 필터와 범위는 템플릿 형식으로만 존재합니다. 두 번째 단계에서는 필터에 대한 매개 변수 값을 설정하고 템플릿을 통해 동기화 범위를 만듭니다. 이 단계에서 만들어지는 범위는 대상 공급자가 원본 데이터베이스와 동기화하기 위해 사용하는 범위입니다. 매개 변수 기반 필터링의 원본 데이터베이스는 SQL Server 또는 SQL Azure 데이터베이스일 수 있으며 대상 데이터베이스는 SQL Server, SQL Azure 또는 SQL Server Compact 데이터베이스일 수 있습니다.

일반적인 필터링 시나리오에서 데이터베이스 관리자 또는 응용 프로그램 개발자는 매개 변수 기반 필터를 정의하고 서버 데이터베이스의 필터링된 동기화를 준비합니다. 또한 Sync Framework 개체를 사용하여 사용자가 필터 매개 변수 값을 지정하고 동기화를 위해 클라이언트 데이터베이스를 구독할 수 있도록 하는 웹 기반 구독 도구와 같은 간단한 도구를 만들 수 있습니다. 데이터베이스 관리자는 구독 도구를 만들기 때문에 개별 사용자용 필터를 만들 필요가 없습니다. 그 대신, 사용자는 해당 도구를 사용하여 본인에게 적합한 매개 변수 값을 지정하고 동기화를 필요한 대로 구독할 수 있습니다.

필터링된 동기화를 설정하는 과정의 예는 다음과 같습니다.

  1. 사용자는 고객의 유형과 고객의 거주 도시에 대한 열을 포함하는 고객 정보 데이터베이스를 보유하고 있습니다.

  2. 사용자는 고객 유형과 도시라는 두 개의 필터 매개 변수를 기반으로 하는 필터 템플릿을 정의합니다. 사용자는 고객 유형과 도시 값이 매개 변수 값과 일치하는 경우에만 필터를 통과하는 행을 지정합니다.

  3. 특정 영업 사원이 자신의 클라이언트 데이터베이스와 사용자의 서버를 동기화하려고 합니다. 해당 영업 사원이 서울에 있는 소매 고객에 대한 매개 변수 값을 사용하여 필터링된 범위를 만들도록 요청하고 동기화합니다.

  4. 해당 영업 사원에게는 자신에게 필요한 고객 데이터만 전달되기 때문에 자신의 클라이언트 데이터베이스에서 사용되는 네트워크 트래픽과 메모리 양을 줄일 수 있습니다.

  5. 다른 영업 사원이 부산에 있는 도매 고객을 통과시키는 필터링된 범위를 요청하고 동기화합니다. 해당 영업 사원에게는 자신에게 필요한 고객 데이터만 전달됩니다.

매개 변수 기반 필터는 필드 값이 변경되어 이전에 필터를 통과했던 항목이 더 이상 필터를 통과하지 못하는 경우와 같이 항목이 필터 범위를 벗어나는 경우에만 적합합니다. 이러한 종류의 변경은 동기화 커뮤니티 내에서 올바르게 전파되지 않습니다. 예를 들어 필터는 영업 사원 이름 열을 기반으로 정의됩니다. 영업 사원 A가 자신의 이름으로 필터링되는 범위를 만들고 데이터를 자신의 클라이언트 데이터베이스와 동기화합니다. 그의 상사가 자신의 고객 중 한 명을 영업 사원 B에게 다시 할당합니다. 영업 사원 A가 데이터베이스와 다시 동기화하면 다시 할당된 고객이 제거되지 않은 채 영업 사원 A의 클라이언트 데이터베이스에 오래된 데이터와 함께 여전히 표시됩니다.

매개 변수 기반 필터 만들기

매개 변수 기반 필터는 두 단계로 만들어집니다. 먼저 필터 및 범위 템플릿이 정의됩니다. 그 다음 필터 매개 변수의 특정 값이 포함된 필터링된 범위가 만들어집니다. 이 두 단계로 구성된 절차에는 다음과 같은 이점이 있습니다.

  • 설정이 간편합니다. 필터 템플릿을 한 번만 정의합니다. 필터 템플릿을 만드는 작업이 데이터베이스 서버의 저장 프로시저에 대한 권한을 필요로 하는 유일한 작업입니다.

  • 구독이 간편합니다. 고객은 매개 변수 값을 지정하여 필터링된 범위를 본인에게 필요한 대로 만들고 구독할 수 있습니다. 이 단계에서는 데이터베이스 서버의 동기화 테이블에 행을 삽입할 수 있는 권한만 있으면 됩니다.

  • **유지 관리가 간편합니다.**여러 매개 변수가 결합되어 있고 필터링된 범위를 많이 만든 경우에도 매개 변수 기반 단일 프로시저를 사용하여 변경 사항을 열거하기 때문에 유지 관리가 간단합니다.

필터 템플릿 정의

매개 변수 기반 필터를 만드는 첫 단계는 나중에 필터링된 범위를 만드는 데 사용할 수 있는 필터 템플릿을 정의하는 것입니다. 필터 템플릿은 원본 데이터베이스에 저장되며 필터 템플릿을 위해서는 동기화 테이블 및 저장 프로시저를 만들어야 합니다. 따라서 원본 데이터베이스에 대한 적절한 권한이 있어야 합니다.

필터 템플릿은 동기화 범위에 대한 정의와 함께 정의됩니다. 범위에 있는 테이블에 대한 필터 템플릿은 다음과 같이 정의됩니다.

  • AddFilterColumn을 사용하여 동기화 범위에 있는 SqlSyncTableProvisioning 개체에 필터 열을 추가합니다. 이 경우 기본 테이블에 대한 변경 내용을 추적하는 추적 테이블에 필터 열이 추가됩니다.

  • SqlSyncTableProvisioning 개체의 FilterParameters 컬렉션에 SqlParameter 개체를 추가하여 하나 이상의 필터 매개 변수를 정의합니다. 이 경우 동기화 시 변경 내용을 열거하는 저장 프로시저의 인수 목록에 지정한 매개 변수가 추가됩니다.

  • SqlSyncTableProvisioning 개체의 FilterClause 속성을 설정하여 매개 변수 값과 열 값 사이의 관계를 정의하는 필터 절을 추가합니다. 필터 절은 WHERE 키워드가 없는 WHERE 절이며, [side] 별칭은 추적 테이블의 별칭입니다. 이러한 매개 변수는 FilterParameters 컬렉션에 지정되어 있는 매개 변수와 일치합니다. 이 단계에서는 필터 매개 변수와 열 사이의 관계만 정의합니다. 매개 변수의 실제 값은 나중에 필터 범위를 만들 때 지정합니다.

그 다음 SqlSyncScopeProvisioning 개체의 Apply 메서드를 사용하여 필터 및 범위 템플릿이 원본 데이터베이스에 적용되며 이 단계에서 적합한 동기화 테이블과 저장 프로시저가 만들어집니다.

필터 절에서 별칭 [base] 및 [side]는 Sync Framework에 의해 정의됩니다. [base]는 테이블의 기본 이름을 나타내며 [side]는 변경 내용 추적 테이블을 나타냅니다. 예를 들어 Customer 테이블은 CustomerType 열을 기준으로 필터링됩니다. 기본적으로 [base]는 [Customer]의 별칭이고 [side]는 [Customer_tracking]의 별칭입니다. CustomerType 열이 기본 테이블과 추적 테이블에 모두 있으므로 이에 대한 참조는 필터 절에서 정규화되어야 합니다 그렇지 않으면 열이 모호하고 오류가 발생합니다. [base] 및 [side] 별칭 대신 [Customer_tracking].[CustomerType] = @customertype 등의 실제 테이블 이름을 사용할 수도 있습니다.

다음 예제에서는 필터 템플릿을 만들어 원본 데이터베이스에 적용합니다.

// Create a scope named "customertype_template", and add two tables to the scope.
// GetDescriptionForTable gets the schema of each table, so that tracking 
// tables and triggers can be created for that table.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customertype_template");

// Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter.";

// Definition for tables.
DbSyncTableDescription customerDescription =
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
DbSyncTableDescription customerContactDescription =
    SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn);
scopeDesc.Tables.Add(customerContactDescription);

// Create a provisioning object for "customertype_template" that can be used to create a template
// from which filtered synchronization scopes can be created. We specify that
// all synchronization-related objects should be created in a 
// database schema named "Sync". If you specify a schema, it must already exist
// in the database.
SqlSyncScopeProvisioning serverTemplate = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
serverTemplate.ObjectSchema = "Sync";

// Specify the column in the Customer table to use for filtering data, 
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
// The CustomerType column that defines the filter is set up as a parameter in this template.
// An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverTemplate.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = @customertype";
SqlParameter param = new SqlParameter("@customertype", SqlDbType.NVarChar, 100);
serverTemplate.Tables["Sales.Customer"].FilterParameters.Add(param);

// Create the "customertype_template" template in the database.
// This action creates tables and stored procedures in the database, so appropriate database permissions are needed.
serverTemplate.Apply();
' Create a scope named "customertype_template", and add two tables to the scope.
' GetDescriptionForTable gets the schema of each table, so that tracking 
' tables and triggers can be created for that table.
Dim scopeDesc As New DbSyncScopeDescription("customertype_template")

' Set a friendly description of the template.
scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter."

' Definition for tables.
Dim customerDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
Dim customerContactDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn)
scopeDesc.Tables.Add(customerContactDescription)

' Create a provisioning object for "customertype_template" that can be used to create a template
' from which filtered synchronization scopes can be created. We specify that
' all synchronization-related objects should be created in a 
' database schema named "Sync". If you specify a schema, it must already exist
' in the database.
Dim serverTemplate As New SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template)
serverTemplate.ObjectSchema = "Sync"

' Specify the column in the Customer table to use for filtering data, 
' and the filtering clause to use against the tracking table.
' "[side]" is an alias for the tracking table.
' The CustomerType column that defines the filter is set up as a parameter in this template.
' An actual customer type will be specified when the synchronization scope is created.
serverTemplate.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverTemplate.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = @customertype"
Dim param As New SqlParameter("@customertype", SqlDbType.NVarChar, 100)
serverTemplate.Tables("Sales.Customer").FilterParameters.Add(param)

' Create the "customertype_template" template in the database.
' This action creates tables and stored procedures in the database, so appropriate permissions are needed.
serverTemplate.Apply()

필터링된 범위 만들기

필터를 사용하여 서버와 동기화하기 전에 고객은 먼저 필터 매개 변수에 대한 특정 값을 정의해야 합니다. 이를 위해서 고객은 먼저 서버의 필터 템플릿을 통해 SqlSyncScopeProvisioning 개체를 채우고 PopulateFromTemplate을 호출하여 필터링된 범위의 이름을 지정합니다. 그런 다음 고객은 SqlSyncTableProvisioning 개체에 있는 FilterParameters 컬렉션 멤버의 값 속성을 설정하여 필터 매개 변수 값을 정의합니다. 마지막으로 고객은 SqlSyncScopeProvisioning 개체의 Apply 메서드를 호출하여 서버에 필터링된 범위를 적용합니다. 서버 데이터베이스에 필터링된 범위를 적용하면 동기화 테이블에 행이 추가되며 이를 위해 해당 테이블에 행을 삽입할 수 있는 권한만 있으면 됩니다.

서버 데이터베이스에 필터링된 범위를 지정한 후에는 GetDescriptionForScope를 호출하여 명명된 범위에 대한 범위 설명을 가져오고, 범위 설명을 SqlSyncScopeProvisioning 개체에 로드하고, Apply를 호출하여 범위 설명을 클라이언트 데이터베이스에 적용하여 클라이언트 데이터베이스를 프로비전합니다.

필터 매개 변수 값을 정의하고 새롭게 지정된 필터를 서버 데이터베이스에 적용하고 클라이언트 데이터베이스를 프로비전하기 위한 코드는 사용자로부터 필터 매개 변수 값을 수집하고 필터링된 동기화에 대한 사용자의 클라이언트 데이터베이스를 구독하는 별도의 도구에서 쉽게 캡슐화할 수 있습니다.

다음 예제에서는 필터에 대한 매개 변수 값을 정의하고 해당 값을 서버 데이터베이스에 적용하고 필터링된 범위를 클라이언트 데이터베이스에 프로비전하여 동기화를 준비합니다.

// Create a synchronization scope for retail customers.
// This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
// the permissions needed on the server.
SqlSyncScopeProvisioning serverProvRetail = new SqlSyncScopeProvisioning(serverConn);
serverProvRetail.ObjectSchema = "Sync";
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template");
serverProvRetail.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Retail";
serverProvRetail.UserDescription = "Customer data includes only retail customers.";
serverProvRetail.Apply();

// Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
// information that is retrieved from the server.
DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", null, "Sync", serverConn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply();
' Create a synchronization scope for retail customers.
' This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
' the permissions needed on the server.
Dim serverProvRetail As New SqlSyncScopeProvisioning(serverConn)
serverProvRetail.ObjectSchema = "Sync"
serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template")
serverProvRetail.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Retail"
serverProvRetail.UserDescription = "Customer data includes only retail customers."
serverProvRetail.Apply()

' Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
' information that is retrieved from the server.
Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", Nothing, "Sync", serverConn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply()

필터링된 범위를 사용하여 클라이언트 동기화

필터링된 범위를 정의하고 클라이언트 데이터베이스를 프로비전한 후에는 필터링된 범위의 SqlSyncProvider 개체를 클라이언트 및 서버 데이터베이스에서 만들고 공급자를 SyncOrchestrator 개체와 연결하고 Synchronize 메서드를 호출하여 클라이언트를 동기화할 수 있습니다.

다음 예제에서는 두 데이터베이스에 대한 필터링된 동기화를 수행합니다.

syncOrchestrator = new SampleSyncOrchestrator(
    new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync"),
    new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
    );
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()

예제

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

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization.Data.SqlServerCe;

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

            // Create the connections over which provisioning and synchronization
            // are performed. The Utility class handles all functionality that is not
            // directly related to synchronization, such as holding connection 
            // string information and making changes to the server database.
            SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
            SqlConnection clientSqlConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);
            SqlCeConnection clientSqlCe1Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync1);

            // Create a scope named "customertype_template", and add two tables to the scope.
            // GetDescriptionForTable gets the schema of each table, so that tracking 
            // tables and triggers can be created for that table.
            DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customertype_template");

            // Set a friendly description of the template.
            scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter.";

            // Definition for tables.
            DbSyncTableDescription customerDescription =
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
            scopeDesc.Tables.Add(customerDescription);
            DbSyncTableDescription customerContactDescription =
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn);
            scopeDesc.Tables.Add(customerContactDescription);

            // Create a provisioning object for "customertype_template" that can be used to create a template
            // from which filtered synchronization scopes can be created. We specify that
            // all synchronization-related objects should be created in a 
            // database schema named "Sync". If you specify a schema, it must already exist
            // in the database.
            SqlSyncScopeProvisioning serverTemplate = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
            serverTemplate.ObjectSchema = "Sync";

            // Specify the column in the Customer table to use for filtering data, 
            // and the filtering clause to use against the tracking table.
            // "[side]" is an alias for the tracking table.
            // The CustomerType column that defines the filter is set up as a parameter in this template.
            // An actual customer type will be specified when the synchronization scope is created.
            serverTemplate.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
            serverTemplate.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = @customertype";
            SqlParameter param = new SqlParameter("@customertype", SqlDbType.NVarChar, 100);
            serverTemplate.Tables["Sales.Customer"].FilterParameters.Add(param);

            // Create the "customertype_template" template in the database.
            // This action creates tables and stored procedures in the database, so appropriate database permissions are needed.
            serverTemplate.Apply();

            // Create a synchronization scope for retail customers.
            // This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
            // the permissions needed on the server.
            SqlSyncScopeProvisioning serverProvRetail = new SqlSyncScopeProvisioning(serverConn);
            serverProvRetail.ObjectSchema = "Sync";
            serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template");
            serverProvRetail.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Retail";
            serverProvRetail.UserDescription = "Customer data includes only retail customers.";
            serverProvRetail.Apply();

            // Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
            // information that is retrieved from the server.
            DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", null, "Sync", serverConn);
            SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
            clientSqlConfig.ObjectSchema = "Sync";
            clientSqlConfig.Apply();

            // Create a SQL Server Compact database and provision it based on a new scope that is created from template
            // information coupled with a new customer type to use for the filter.
            // Compact databases do not support separate schemas, so we prefix the name of all 
            // synchronization-related objects with "Sync" so that they are easy to
            // identify.
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);

            // First create the scope in the server database based on the filter template.
            // Again, note that this step requires only permission to insert rows in the synchronization tables.
            SqlSyncScopeProvisioning serverProvWholesale = new SqlSyncScopeProvisioning(serverConn);
            serverProvWholesale.ObjectSchema = "Sync";
            serverProvWholesale.PopulateFromTemplate("WholesaleCustomers", "customertype_template");
            serverProvWholesale.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Wholesale";
            serverProvWholesale.UserDescription = "Customer data includes only wholesale customers.";
            serverProvWholesale.Apply();

            // Now provision the SQL Server Compact database with the new filtered scope.
            DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("WholesaleCustomers", null, "Sync", serverConn);
            SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc);
            clientSqlCe1Config.ObjectPrefix = "Sync";
            clientSqlCe1Config.Apply();

            // Initial synchronization sessions. 7 rows are synchronized:
            // all rows (4) from CustomerContact, and the 3 rows from Customer 
            // that satisfy the filtering criteria.
            SampleSyncOrchestrator syncOrchestrator;
            SyncOperationStatistics syncStats;

            // Data is downloaded from the server to the SQL Server client.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync"),
                new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");

            // Data is downloaded from the server to the 
            // first SQL Server Compact client.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"),
                new SqlSyncProvider("WholesaleCustomers", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");

            // Make changes on the server: 
            //   1 insert affects RetailCustomers scope
            //   1 update affects WholesaleCustomers scope
            //   1 delete affects RetailCustomers scope
            Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");

            // Synchronize again. Three changes were made on the server.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"),
                new SqlSyncProvider("WholesaleCustomers", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync"),
                new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync")
            );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            serverConn.Close();
            serverConn.Dispose();
            clientSqlConn.Close();
            clientSqlConn.Dispose();
            clientSqlCe1Conn.Close();
            clientSqlCe1Conn.Dispose();

            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server);
            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client);

            Console.Write("\nPress any key to exit.");
            Console.Read();

        }

    }

    public class SampleSyncOrchestrator : SyncOrchestrator
    {
        public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
        {

            this.LocalProvider = localProvider;
            this.RemoteProvider = remoteProvider;
            this.Direction = SyncDirectionOrder.UploadAndDownload;
        }

        public void DisplayStats(SyncOperationStatistics syncStatistics, string syncType)
        {
            Console.WriteLine(String.Empty);
            if (syncType == "initial")
            {
                Console.WriteLine("****** Initial Synchronization ******");
            }
            else if (syncType == "subsequent")
            {
                Console.WriteLine("***** Subsequent Synchronization ****");
            }

            Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
            Console.WriteLine("Total Changes Uploaded: " + syncStatistics.UploadChangesTotal);
            Console.WriteLine("Total Changes Downloaded: " + syncStatistics.DownloadChangesTotal);
            Console.WriteLine("Complete Time: " + syncStatistics.SyncEndTime);
            Console.WriteLine(String.Empty);
        }
    }
}
Imports System
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlServerCe
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Data
Imports Microsoft.Synchronization.Data.SqlServer
Imports Microsoft.Synchronization.Data.SqlServerCe

Namespace Microsoft.Samples.Synchronization
    Class Program
        Shared Sub Main(ByVal args As String())

            ' Create the connections over which provisioning and synchronization
            ' are performed. The Utility class handles all functionality that is not
            ' directly related to synchronization, such as holding connection 
            ' string information and making changes to the server database.
            Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
            Dim clientSqlConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)
            Dim clientSqlCe1Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync1)

            ' Create a scope named "customertype_template", and add two tables to the scope.
            ' GetDescriptionForTable gets the schema of each table, so that tracking 
            ' tables and triggers can be created for that table.
            Dim scopeDesc As New DbSyncScopeDescription("customertype_template")

            ' Set a friendly description of the template.
            scopeDesc.UserDescription = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter."

            ' Definition for tables.
            Dim customerDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
            scopeDesc.Tables.Add(customerDescription)
            Dim customerContactDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn)
            scopeDesc.Tables.Add(customerContactDescription)

            ' Create a provisioning object for "customertype_template" that can be used to create a template
            ' from which filtered synchronization scopes can be created. We specify that
            ' all synchronization-related objects should be created in a 
            ' database schema named "Sync". If you specify a schema, it must already exist
            ' in the database.
            Dim serverTemplate As New SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template)
            serverTemplate.ObjectSchema = "Sync"

            ' Specify the column in the Customer table to use for filtering data, 
            ' and the filtering clause to use against the tracking table.
            ' "[side]" is an alias for the tracking table.
            ' The CustomerType column that defines the filter is set up as a parameter in this template.
            ' An actual customer type will be specified when the synchronization scope is created.
            serverTemplate.Tables("Sales.Customer").AddFilterColumn("CustomerType")
            serverTemplate.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = @customertype"
            Dim param As New SqlParameter("@customertype", SqlDbType.NVarChar, 100)
            serverTemplate.Tables("Sales.Customer").FilterParameters.Add(param)

            ' Create the "customertype_template" template in the database.
            ' This action creates tables and stored procedures in the database, so appropriate permissions are needed.
            serverTemplate.Apply()

            ' Write the template creation script to a file. You can modify 
            ' this script if necessary and run it against the server
            ' to customize behavior.
            File.WriteAllText("SampleTemplateScript.txt", serverTemplate.Script())

            ' Create a synchronization scope for retail customers.
            ' This action adds rows to synchronization tables but does not create new tables or stored procedures, reducing
            ' the permissions needed on the server.
            Dim serverProvRetail As New SqlSyncScopeProvisioning(serverConn)
            serverProvRetail.ObjectSchema = "Sync"
            serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template")
            serverProvRetail.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Retail"
            serverProvRetail.UserDescription = "Customer data includes only retail customers."
            serverProvRetail.Apply()

            ' Provision the existing database SyncSamplesDb_SqlPeer2 based on filtered scope
            ' information that is retrieved from the server.
            Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", Nothing, "Sync", serverConn)
            Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc)
            clientSqlConfig.ObjectSchema = "Sync"
            clientSqlConfig.Apply()

            ' Create a SQL Server Compact database and provision it based on a new scope that is created from template
            ' information coupled with a new customer type to use for the filter.
            ' Compact databases do not support separate schemas, so we prefix the name of all 
            ' synchronization-related objects with "Sync" so that they are easy to
            ' identify.
            Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)

            ' First create the scope in the server database based on the filter template.
            ' Again, note that this step requires only permission to insert rows to the synchronization tables.
            Dim serverProvWholesale As New SqlSyncScopeProvisioning(serverConn)
            serverProvWholesale.ObjectSchema = "Sync"
            serverProvWholesale.PopulateFromTemplate("WholesaleCustomers", "customertype_template")
            serverProvWholesale.Tables("Sales.Customer").FilterParameters("@customertype").Value = "Wholesale"
            serverProvWholesale.UserDescription = "Customer data includes only wholesale customers."
            serverProvWholesale.Apply()

            ' Now provision the SQL Server Compact database with the new filtered scope.
            Dim clientSqlCe1Desc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("WholesaleCustomers", Nothing, "Sync", serverConn)
            Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Conn, clientSqlCe1Desc)
            clientSqlCe1Config.ObjectPrefix = "Sync"
            clientSqlCe1Config.Apply()


            ' Initial synchronization sessions. 7 rows are synchronized:
            ' all rows (4) from CustomerContact, and the 3 rows from Customer 
            ' that satisfy the filtering criteria.
            Dim syncOrchestrator As SampleSyncOrchestrator
            Dim syncStats As SyncOperationStatistics

            ' Data is downloaded from the server to the SQL Server client.
            syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")

            ' Data is downloaded from the server to the 
            ' first SQL Server Compact client.
            syncOrchestrator = New SampleSyncOrchestrator(New SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"), New SqlSyncProvider("WholesaleCustomers", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")

            ' Make changes on the server: 
            '   1 insert affects RetailCustomers scope
            '   1 update affects WholesaleCustomers scope
            '   1 delete affects RetailCustomers scope
            Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")

            ' Synchronize again. Three changes were made on the server.
            syncOrchestrator = New SampleSyncOrchestrator(New SqlCeSyncProvider("WholesaleCustomers", clientSqlCe1Conn, "Sync"), New SqlSyncProvider("WholesaleCustomers", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")

            syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("RetailCustomers", clientSqlConn, Nothing, "Sync"), New SqlSyncProvider("RetailCustomers", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")

            serverConn.Close()
            serverConn.Dispose()
            clientSqlConn.Close()
            clientSqlConn.Dispose()
            clientSqlCe1Conn.Close()
            clientSqlCe1Conn.Dispose()

            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server)
            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client)

            Console.Write(vbLf & "Press any key to exit.")
            Console.Read()

        End Sub

    End Class

    Public Class SampleSyncOrchestrator
        Inherits SyncOrchestrator
        Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)

            Me.LocalProvider = localProvider
            Me.RemoteProvider = remoteProvider
            Me.Direction = SyncDirectionOrder.UploadAndDownload
        End Sub

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

            Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
            Console.WriteLine("Total Changes Uploaded: " & syncStatistics.UploadChangesTotal)
            Console.WriteLine("Total Changes Downloaded: " & syncStatistics.DownloadChangesTotal)
            Console.WriteLine("Complete Time: " & syncStatistics.SyncEndTime)
            Console.WriteLine([String].Empty)
        End Sub
    End Class
End Namespace

참고 항목

개념

SQL Server와 SQL Server Compact 동기화