방법: 행 및 열 필터링
이 항목에서는 동기화되는 테이블의 행 및 열을 필터링하는 방법에 대해 설명합니다. 이 항목의 예제에서는 다음과 같은 Sync Framework 형식을 중점적으로 설명합니다.
샘플 코드를 실행하는 방법에 대한 자세한 내용은 일반적인 클라이언트 및 서버 동기화 태스크 프로그래밍의 "방법 항목의 예제 응용 프로그램"을 참조하십시오.
필터링 이해
일반적으로 클라이언트 데이터베이스에는 서버에서 사용할 수 있는 데이터의 하위 집합만 있으면 됩니다. Sync Framework를 사용하면 클라이언트에 필요한 행 및 열을 지정할 수 있으며, 동기화 명령을 수동으로 생성할 것인지, 아니면 SqlSyncAdapterBuilder를 사용하여 생성할 것인지를 지정할 수 있습니다. 데이터를 필터링하면 다음이 가능해집니다.
네트워크를 통해 보내는 데이터 양 감소
클라이언트에 필요한 저장 공간 감소
개별 클라이언트 요구 사항을 기반으로 하여 사용자 지정 데이터 파티션 제공
클라이언트가 데이터를 업데이트하는 경우 각 클라이언트에 대해 서로 다른 데이터 파티션을 보낼 수 있으므로 충돌 방지 또는 감소. 즉, 두 클라이언트가 같은 데이터 값을 업데이트하지 않습니다.
필터는 단일 테이블을 기준으로 할 수도 있고 JOIN 절이나 여러 SELECT 문을 사용하여 여러 테이블을 참조할 수도 있습니다. 필터의 기준으로 여러 테이블을 사용하는 경우 자신이 담당하는 고객에 대한 데이터만 필요로 하며 모든 고객의 주문을 필요로 하는 영업 사원의 경우 등에 사용자가 데이터 파티션을 다운로드할 수 있습니다. 여러 테이블에 필터를 적용하면 데이터를 융통성 있게 확인할 수 있습니다. 그러나 필터는 최대한 간단하게 유지해야 하며 테이블 수가 늘어나면 성능을 테스트해야 합니다. 또한 필터의 기준이 되는 열을 인덱싱해야 합니다.
중요
보안을 위해 필터링을 사용하지 않습니다. 서버에서 클라이언트 또는 사용자 ID를 기반으로 하는 데이터를 필터링하는 기능은 보안 기능이 아닙니다. 즉, 이러한 방법을 사용해도 클라이언트가 다른 클라이언트에 속하는 데이터를 읽지 못하도록 차단할 수는 없습니다. 이러한 형식의 필터링은 데이터를 분할하고 클라이언트 데이터베이스로 가져오는 데이터 양을 줄이는 데만 유용합니다.
Sync Framework에서는 자동 파티션 관리 기능이 제공되지 않습니다. 그러므로 다음과 같은 결과가 발생합니다.
행을 업데이트하고 필터링에 사용한 열의 값을 변경하면 해당 파티션에 업데이트한 행을 포함하는 클라이언트에서 행이 자동으로 삭제되지 않습니다. 우편 번호를 기준으로 하여 고객 데이터를 영업 사원에게 다운로드하는 응용 프로그램을 예로 들어 보겠습니다. 고객이 우편 번호가 다른 지역으로 사무실을 이전해도 해당 고객의 데이터는 원래 해당 데이터를 소유하고 있었던 영업 사원으로부터 제거되지 않습니다. 이 기능이 필요한 경우 Sync Framework에서 해당 업데이트를 삭제 항목으로 다운로드하도록 하는 시스템을 개발하면 됩니다.
응용 프로그램이 클라이언트 파티션 외부에 있는 클라이언트에서 데이터를 삽입하지 못하도록 하는 메커니즘이 없습니다. 이 경우에는 파티션 외부 삽입 및 업데이트를 허용하지 않도록 클라이언트에 제약 조건을 추가할 수 있습니다.
키가 아닌 열에 따른 필터
SQL Server 변경 내용 추적 및 일부 사용자 지정 추적 시스템에서는 삭제된 행의 기본 키만 유지됩니다. 필터에 기본 키만 사용되는 경우 SelectIncrementalDeletesCommand 속성에 지정된 쿼리로 올바른 하위 행 집합을 식별하여 클라이언트에 다운로드할 수 있습니다. 필터에 기본 키가 아닌 열이 사용된 경우 삭제된 행에 더 이상 없는 열이 참조되므로 쿼리가 실패합니다. 이 문제를 해결하려면 다음 방법 중 하나를 사용합니다.
모든 필터링 열을 기본 키에 포함합니다. 키의 선택도에 영향을 주지 않도록 추가 열을 키 끝에 배치합니다.
삽입 및 업데이트만 필터링합니다. 불필요한 삭제는 클라이언트에 다운로드되어도 무시됩니다.
서버에서 논리적 삭제를 수행합니다. 행을 삭제하는 대신 ON DELETE 트리거를 사용하여 행이 삭제 또는 보관된 것으로 표시하는 열을 업데이트합니다. 그런 다음 변경 내용을 클라이언트에 업데이트로 보냅니다.
데이터를 필터링하는 데 사용할 수 있는 추가 값을 사용하여 SQL Server 변경 내용 추적의 컨텍스트 열(SYS_CHANGE_CONTEXT)을 오버로드합니다. 이 옵션은 성능 면에서는 가장 뛰어나지만 이 열을 구문 분석해야 하므로 가장 복잡한 방법일 수 있습니다.
예제
이 항목의 예제 코드에서는 Sync Framework 샘플 데이터베이스에서 Customer
, OrderHeader
및 OrderDetail
테이블의 데이터를 필터링하는 방법을 보여 줍니다. Customer
테이블은 SalesPerson
열에 대해 Brenda Diaz
값이 포함된 행만 다운로드되도록 필터링됩니다. 그런 후에 필터는 다른 두 테이블로 확장됩니다. 예제에서는 SqlSyncAdapterBuilder
를 사용하여 데이터를 필터링하는 방법과 동기화 명령을 수동으로 만들어 데이터를 필터링하는 방법을 보여 줍니다. 동기화 명령의 개요는 방법: 스냅숏, 다운로드, 업로드 및 양방향 동기화 지정을 참조하십시오.
SqlSyncAdapterBuilder 사용
이 단원에서는 SqlSyncAdapterBuilder를 사용하여 명령을 만드는 경우 필터링에 사용되는 API에 대해 설명합니다. 또한 API의 주요 부분을 보여 주는 코드 예제와 전체 코드 예제가 제공됩니다.
API의 주요 요소
다음 코드 예제에서는 세 테이블에서 모두 필터 절에 사용되는 필터 매개 변수를 만듭니다.
SqlParameter filterParameter = new SqlParameter("@SalesPerson", SqlDbType.NVarChar);
Dim filterParameter As New SqlParameter("@SalesPerson", SqlDbType.NVarChar)
다음 코드 예제에서는 Customer
테이블에 대해 다운로드할 열을 지정합니다.
string[] customerDataColumns = new string[3];
customerDataColumns[0] = "CustomerId";
customerDataColumns[1] = "CustomerName";
customerDataColumns[2] = "CustomerType";
customerBuilder.DataColumns.AddRange(customerDataColumns);
customerBuilder.TombstoneDataColumns.AddRange(customerDataColumns);
Dim customerDataColumns(2) As String
customerDataColumns(0) = "CustomerId"
customerDataColumns(1) = "CustomerName"
customerDataColumns(2) = "CustomerType"
customerBuilder.DataColumns.AddRange(customerDataColumns)
customerBuilder.TombstoneDataColumns.AddRange(customerDataColumns)
다음 코드 예제에서는 Customer
테이블에 대해 다운로드할 행을 지정합니다. SalesPerson
에 대한 값을 하드코드할 수 있습니다. 그러나 일반적으로는 예제에 나와 있는 것처럼 변경할 수 있는 값이 포함된 매개 변수를 사용합니다. 여기서는 첫 번째 코드 예제의 매개 변수가 사용됩니다.
string customerFilterClause = "SalesPerson=@SalesPerson";
customerBuilder.FilterClause = customerFilterClause;
customerBuilder.FilterParameters.Add(filterParameter);
customerBuilder.TombstoneFilterClause = customerFilterClause;
customerBuilder.TombstoneFilterParameters.Add(filterParameter);
Dim customerFilterClause As String = "SalesPerson=@SalesPerson"
With customerBuilder
.FilterClause = customerFilterClause
.FilterParameters.Add(filterParameter)
.TombstoneFilterClause = customerFilterClause
.TombstoneFilterParameters.Add(filterParameter)
End With
다음 코드 예제에서는 Customer
테이블에서 OrderHeader
테이블로 필터를 확장합니다. 이 경우 필터 절에는 SELECT
문이 사용됩니다. 수동 명령의 경우에는 명령 지정 방법을 보다 자세하게 제어할 수 있는 JOIN
절이 사용됩니다.
string orderHeaderFilterClause =
"CustomerId IN (SELECT CustomerId FROM Sales.Customer " +
"WHERE SalesPerson=@SalesPerson)";
orderHeaderBuilder.FilterClause = orderHeaderFilterClause;
orderHeaderBuilder.FilterParameters.Add(filterParameter);
orderHeaderBuilder.TombstoneFilterClause = orderHeaderFilterClause;
orderHeaderBuilder.TombstoneFilterParameters.Add(filterParameter);
Dim orderHeaderFilterClause As String = _
"CustomerId IN (SELECT CustomerId FROM Sales.Customer " _
& "WHERE SalesPerson=@SalesPerson)"
With orderHeaderBuilder
.FilterClause = orderHeaderFilterClause
.FilterParameters.Add(filterParameter)
.TombstoneFilterClause = orderHeaderFilterClause
.TombstoneFilterParameters.Add(filterParameter)
End With
다음 코드 예제에서는 SyncAgent
에서 파생되는 클래스에서 @SalesPerson
매개 변수에 대한 값을 지정합니다. 응용 프로그램에서 이 값은 로그인 ID 또는 기타 사용자 입력에서 생성될 수 있습니다.
this.Configuration.SyncParameters.Add(
new SyncParameter("@SalesPerson", "Brenda Diaz"));
Me.Configuration.SyncParameters.Add(New SyncParameter("@SalesPerson", "Brenda Diaz"))
전체 코드 예제
다음의 전체 코드 예제에는 위에서 설명한 코드 예제와 동기화를 수행하는 데 필요한 추가 코드가 포함되어 있습니다. 이 예제를 사용하려면 데이터베이스 공급자용 유틸리티 클래스 방법 항목에 나와 있는 Utility
클래스가 필요합니다. 샘플을 실행할 때는 SyncStatistics가 반환하는 정보를 주의 깊게 확인하십시오. 행 하위 집합은 초기 동기화와 후속 동기화에서 모두 다운로드됩니다.
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.
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("Customer");
//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 two SyncGroups so that changes to OrderHeader
//and OrderDetail are made in one transaction. Depending on
//application requirements, you might include Customer
//in the same group.
SyncGroup customerSyncGroup = new SyncGroup("Customer");
SyncGroup orderSyncGroup = new SyncGroup("Order");
//Add each table: specify a synchronization direction of
//DownloadOnly.
SyncTable customerSyncTable = new SyncTable("Customer");
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerSyncTable.SyncDirection = SyncDirection.DownloadOnly;
customerSyncTable.SyncGroup = customerSyncGroup;
this.Configuration.SyncTables.Add(customerSyncTable);
SyncTable orderHeaderSyncTable = new SyncTable("OrderHeader");
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly;
orderHeaderSyncTable.SyncGroup = orderSyncGroup;
this.Configuration.SyncTables.Add(orderHeaderSyncTable);
SyncTable orderDetailSyncTable = new SyncTable("OrderDetail");
orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
orderDetailSyncTable.SyncDirection = SyncDirection.DownloadOnly;
orderDetailSyncTable.SyncGroup = orderSyncGroup;
this.Configuration.SyncTables.Add(orderDetailSyncTable);
//Specify a value for the @SalesPerson parameter that is added
//in the server synchronization provider. This value would
//typically be provided by a user in the application, but we
//have hardcoded it here for convenience.
this.Configuration.SyncParameters.Add(
new SyncParameter("@SalesPerson", "Brenda Diaz"));
}
}
//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 use a timestamp value
//that is retrieved and stored in the client database.
//During each synchronization, the new anchor value and
//the last anchor value from the previous synchronization
//are used: the set of changes between these upper and
//lower bounds is synchronized.
//
//SyncSession.SyncNewReceivedAnchor is a string constant;
//you could also use @sync_new_received_anchor directly in
//your queries.
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;
//Create a filter parameter that will be used in the filter clause for
//all three tables.
SqlParameter filterParameter = new SqlParameter("@SalesPerson", SqlDbType.NVarChar);
//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
// changes are made.
// * Specify download-only synchronization.
// * Specify if you want only certain columns at the client.
// * Specify filter clauses for the base tables and tombstone
// tables.
// * Call ToSyncAdapter to create the SyncAdapter.
// * Specify a name for the SyncAdapter that matches the
// the name that is 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";
//Specify the columns that you want at the client. If you
//want all columns, this code is not required. In this
//case, we filter out SalesPerson.
string[] customerDataColumns = new string[3];
customerDataColumns[0] = "CustomerId";
customerDataColumns[1] = "CustomerName";
customerDataColumns[2] = "CustomerType";
customerBuilder.DataColumns.AddRange(customerDataColumns);
customerBuilder.TombstoneDataColumns.AddRange(customerDataColumns);
//Specify a filter clause, which is an SQL WHERE clause
//without the WHERE keyword. Use the parameter that is
//created above. The value for the parameter is specified
//in the SyncAgent Configuration object.
string customerFilterClause = "SalesPerson=@SalesPerson";
customerBuilder.FilterClause = customerFilterClause;
customerBuilder.FilterParameters.Add(filterParameter);
customerBuilder.TombstoneFilterClause = customerFilterClause;
customerBuilder.TombstoneFilterParameters.Add(filterParameter);
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";
//Filter properties: extend the filter to the OrderHeader table.
string orderHeaderFilterClause =
"CustomerId IN (SELECT CustomerId FROM Sales.Customer " +
"WHERE SalesPerson=@SalesPerson)";
orderHeaderBuilder.FilterClause = orderHeaderFilterClause;
orderHeaderBuilder.FilterParameters.Add(filterParameter);
orderHeaderBuilder.TombstoneFilterClause = orderHeaderFilterClause;
orderHeaderBuilder.TombstoneFilterParameters.Add(filterParameter);
SyncAdapter orderHeaderSyncAdapter = orderHeaderBuilder.ToSyncAdapter();
orderHeaderSyncAdapter.TableName = "OrderHeader";
this.SyncAdapters.Add(orderHeaderSyncAdapter);
//OrderDetail table.
SqlSyncAdapterBuilder orderDetailBuilder = new SqlSyncAdapterBuilder(serverConn);
orderDetailBuilder.TableName = "Sales.OrderDetail";
orderDetailBuilder.TombstoneTableName = orderDetailBuilder.TableName + "_Tombstone";
orderDetailBuilder.SyncDirection = SyncDirection.DownloadOnly;
orderDetailBuilder.CreationTrackingColumn = "InsertTimestamp";
orderDetailBuilder.UpdateTrackingColumn = "UpdateTimestamp";
orderDetailBuilder.DeletionTrackingColumn = "DeleteTimestamp";
//Filter properties: extend the filter to the OrderDetail table.
string orderDetailFilterClause =
"OrderId IN (SELECT OrderId FROM Sales.OrderHeader " +
"WHERE CustomerId IN " +
"(SELECT CustomerId FROM Sales.Customer " +
"WHERE SalesPerson=@SalesPerson))";
orderDetailBuilder.FilterClause = orderDetailFilterClause;
orderDetailBuilder.FilterParameters.Add(filterParameter);
orderDetailBuilder.TombstoneFilterClause = orderDetailFilterClause;
orderDetailBuilder.TombstoneFilterParameters.Add(filterParameter);
SyncAdapter orderDetailSyncAdapter = orderDetailBuilder.ToSyncAdapter();
orderDetailSyncAdapter.TableName = "OrderDetail";
this.SyncAdapters.Add(orderDetailSyncAdapter);
}
}
//Create a class that is derived from
//Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
//You can just instantiate the provider directly and associate it
//with the SyncAgent, but you could use this class to handle client
//provider events and other client-side processing.
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 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 ******");
}
else if (syncType == "subsequent")
{
Console.WriteLine("***** Subsequent Synchronization ****");
}
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.
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("Customer")
'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 two SyncGroups so that changes to OrderHeader
'and OrderDetail are made in one transaction. Depending on
'application requirements, you might include Customer
'in the same group.
Dim customerSyncGroup As New SyncGroup("Customer")
Dim orderSyncGroup As New SyncGroup("Order")
'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 = customerSyncGroup
Me.Configuration.SyncTables.Add(customerSyncTable)
Dim orderHeaderSyncTable As New SyncTable("OrderHeader")
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly
orderHeaderSyncTable.SyncGroup = orderSyncGroup
Me.Configuration.SyncTables.Add(orderHeaderSyncTable)
Dim orderDetailSyncTable As New SyncTable("OrderDetail")
orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
orderDetailSyncTable.SyncDirection = SyncDirection.DownloadOnly
orderDetailSyncTable.SyncGroup = orderSyncGroup
Me.Configuration.SyncTables.Add(orderDetailSyncTable)
'Specify a value for the @SalesPerson parameter that is added
'in the server synchronization provider. This value would
'typically be provided by a user in the application, but we
'have hardcoded it here for convenience.
Me.Configuration.SyncParameters.Add(New SyncParameter("@SalesPerson", "Brenda Diaz"))
End Sub 'New
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 use a timestamp value
'that is retrieved and stored in the client database.
'During each synchronization, the new anchor value and
'the last anchor value from the previous synchronization
'are used: the set of changes between these upper and
'lower bounds is synchronized.
'
'SyncSession.SyncNewReceivedAnchor is a string constant;
'you could also use @sync_new_received_anchor directly in
'your queries.
Dim selectNewAnchorCommand As New SqlCommand()
Dim newAnchorVariable As String = "@" + SyncSession.SyncNewReceivedAnchor
With selectNewAnchorCommand
.CommandText = "SELECT " + newAnchorVariable + " = min_active_rowversion() - 1"
.Parameters.Add(newAnchorVariable, SqlDbType.Timestamp)
.Parameters(newAnchorVariable).Direction = ParameterDirection.Output
.Connection = serverConn
End With
Me.SelectNewAnchorCommand = selectNewAnchorCommand
'Create a filter parameter that will be used in the filter clause for
'all three tables.
Dim filterParameter As New SqlParameter("@SalesPerson", SqlDbType.NVarChar)
'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
' changes are made.
' * Specify download-only synchronization.
' * Specify if you want only certain columns at the client.
' * Specify filter clauses for the base tables and tombstone
' tables.
' * Call ToSyncAdapter to create the SyncAdapter.
' * Specify a name for the SyncAdapter that matches the
' the name that is specified for the corresponding SyncTable.
' Do not include the schema names (Sales in this case).
'Customer table.
Dim customerBuilder As New SqlSyncAdapterBuilder(serverConn)
With customerBuilder
.TableName = "Sales.Customer"
.TombstoneTableName = customerBuilder.TableName + "_Tombstone"
.SyncDirection = SyncDirection.DownloadOnly
.CreationTrackingColumn = "InsertTimestamp"
.UpdateTrackingColumn = "UpdateTimestamp"
.DeletionTrackingColumn = "DeleteTimestamp"
End With
'Specify the columns that you want at the client. If you
'want all columns, this code is not required. In this
'case, we filter out SalesPerson.
Dim customerDataColumns(2) As String
customerDataColumns(0) = "CustomerId"
customerDataColumns(1) = "CustomerName"
customerDataColumns(2) = "CustomerType"
customerBuilder.DataColumns.AddRange(customerDataColumns)
customerBuilder.TombstoneDataColumns.AddRange(customerDataColumns)
'Specify a filter clause, which is an SQL WHERE clause
'without the WHERE keyword. Use the parameter that is
'created above. The value for the parameter is specified
'in the SyncAgent Configuration object.
Dim customerFilterClause As String = "SalesPerson=@SalesPerson"
With customerBuilder
.FilterClause = customerFilterClause
.FilterParameters.Add(filterParameter)
.TombstoneFilterClause = customerFilterClause
.TombstoneFilterParameters.Add(filterParameter)
End With
Dim customerSyncAdapter As SyncAdapter = customerBuilder.ToSyncAdapter()
customerSyncAdapter.TableName = "Customer"
Me.SyncAdapters.Add(customerSyncAdapter)
'OrderHeader table.
Dim orderHeaderBuilder As New SqlSyncAdapterBuilder(serverConn)
With orderHeaderBuilder
.TableName = "Sales.OrderHeader"
.TombstoneTableName = orderHeaderBuilder.TableName + "_Tombstone"
.SyncDirection = SyncDirection.DownloadOnly
.CreationTrackingColumn = "InsertTimestamp"
.UpdateTrackingColumn = "UpdateTimestamp"
.DeletionTrackingColumn = "DeleteTimestamp"
End With
'Filter properties: extend the filter to the OrderHeader table.
Dim orderHeaderFilterClause As String = _
"CustomerId IN (SELECT CustomerId FROM Sales.Customer " _
& "WHERE SalesPerson=@SalesPerson)"
With orderHeaderBuilder
.FilterClause = orderHeaderFilterClause
.FilterParameters.Add(filterParameter)
.TombstoneFilterClause = orderHeaderFilterClause
.TombstoneFilterParameters.Add(filterParameter)
End With
Dim orderHeaderSyncAdapter As SyncAdapter = orderHeaderBuilder.ToSyncAdapter()
orderHeaderSyncAdapter.TableName = "OrderHeader"
Me.SyncAdapters.Add(orderHeaderSyncAdapter)
'OrderDetail table.
Dim orderDetailBuilder As New SqlSyncAdapterBuilder(serverConn)
With orderDetailBuilder
.TableName = "Sales.OrderDetail"
.TombstoneTableName = orderDetailBuilder.TableName + "_Tombstone"
.SyncDirection = SyncDirection.DownloadOnly
.CreationTrackingColumn = "InsertTimestamp"
.UpdateTrackingColumn = "UpdateTimestamp"
.DeletionTrackingColumn = "DeleteTimestamp"
'Filter properties: extend the filter to the OrderDetail table.
Dim orderDetailFilterClause As String = _
"OrderId IN (SELECT OrderId FROM Sales.OrderHeader " _
& "WHERE CustomerId IN " _
& "(SELECT CustomerId FROM Sales.Customer " _
& "WHERE SalesPerson=@SalesPerson))"
.FilterClause = orderDetailFilterClause
.FilterParameters.Add(filterParameter)
.TombstoneFilterClause = orderDetailFilterClause
.TombstoneFilterParameters.Add(filterParameter)
End With
Dim orderDetailSyncAdapter As SyncAdapter = orderDetailBuilder.ToSyncAdapter()
orderDetailSyncAdapter.TableName = "OrderDetail"
Me.SyncAdapters.Add(orderDetailSyncAdapter)
End Sub 'New
End Class 'SampleServerSyncProvider
'Create a class that is derived from
'Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
'You can just instantiate the provider directly and associate it
'with the SyncAgent, but you could use this class to handle client
'provider events and other client-side processing.
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 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 ******")
ElseIf syncType = "subsequent" Then
Console.WriteLine("***** Subsequent Synchronization ****")
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
수동 명령 사용
이 단원에서는 명령을 수동으로 만드는 경우 필터링에 사용되는 API에 대해 설명합니다. 또한 API의 주요 부분을 보여 주는 코드 예제와 전체 코드 예제가 제공됩니다.
API의 주요 요소
다음 코드 예제에서는 Customer
테이블에 대해 다운로드할 삽입된 열 및 행을 지정합니다. SalesPerson
에 대한 값을 하드코드할 수 있습니다. 그러나 일반적으로는 예제에 나와 있는 것처럼 변경할 수 있는 값이 포함된 매개 변수를 사용합니다. 이 예제에서는 필터 매개 변수를 증분 삽입 다운로드에 필요한 다른 매개 변수와 함께 전달합니다.
SqlCommand customerIncrInserts = new SqlCommand();
customerIncrInserts.CommandText =
"SELECT CustomerId, CustomerName, CustomerType " +
"FROM Sales.Customer " +
"WHERE SalesPerson = @SalesPerson " +
"AND (InsertTimestamp > @sync_last_received_anchor " +
"AND InsertTimestamp <= @sync_new_received_anchor " +
"AND InsertId <> @sync_client_id)";
customerIncrInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerIncrInserts.Connection = serverConn;
customerSyncAdapter.SelectIncrementalInsertsCommand = customerIncrInserts;
Dim customerIncrInserts As New SqlCommand()
With customerIncrInserts
.CommandText = _
"SELECT CustomerId, CustomerName, CustomerType " _
& "FROM Sales.Customer " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (InsertTimestamp > @sync_last_received_anchor " _
& "AND InsertTimestamp <= @sync_new_received_anchor " _
& "AND InsertId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
customerSyncAdapter.SelectIncrementalInsertsCommand = customerIncrInserts
다음 코드 예제에서는 Customer
테이블에서 OrderHeader
테이블로 필터를 확장합니다. 이 경우 JOIN
절을 사용하여 두 테이블 간의 관계를 정의합니다.
SqlCommand orderHeaderIncrInserts = new SqlCommand();
orderHeaderIncrInserts.CommandText =
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " +
"FROM Sales.OrderHeader oh " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE c.SalesPerson = @SalesPerson " +
"AND (oh.InsertTimestamp > @sync_last_received_anchor " +
"AND oh.InsertTimestamp <= @sync_new_received_anchor " +
"AND oh.InsertId <> @sync_client_id)";
orderHeaderIncrInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderHeaderIncrInserts.Connection = serverConn;
orderHeaderSyncAdapter.SelectIncrementalInsertsCommand = orderHeaderIncrInserts;
Dim orderHeaderIncrInserts As New SqlCommand()
With orderHeaderIncrInserts
.CommandText = _
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " _
& "FROM Sales.OrderHeader oh " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE c.SalesPerson = @SalesPerson " _
& "AND (oh.InsertTimestamp > @sync_last_received_anchor " _
& "AND oh.InsertTimestamp <= @sync_new_received_anchor " _
& "AND oh.InsertId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderHeaderSyncAdapter.SelectIncrementalInsertsCommand = orderHeaderIncrInserts
다음 코드 예제에서는 SyncAgent
에서 파생되는 클래스에서 @SalesPerson
매개 변수에 대한 값을 지정합니다. 응용 프로그램에서 이 값은 로그인 ID 또는 기타 사용자 입력에서 생성될 수 있습니다.
this.Configuration.SyncParameters.Add(
new SyncParameter("@SalesPerson", "Brenda Diaz"));
this.Configuration.SyncParameters.Add(
new SyncParameter("@SalesPerson", "Brenda Diaz"));
전체 코드 예제
다음의 전체 코드 예제에는 위에서 설명한 코드 예제와 동기화를 수행하는 데 필요한 추가 코드가 포함되어 있습니다. 이 예제를 사용하려면 데이터베이스 공급자용 유틸리티 클래스 방법 항목에 나와 있는 Utility
클래스가 필요합니다. 샘플을 실행할 때는 SyncStatistics
가 반환하는 정보를 주의 깊게 확인하십시오. 행 하위 집합은 초기 동기화와 후속 동기화에서 모두 다운로드됩니다.
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.
SampleStats sampleStats = new SampleStats();
//Request a password for the client database, and delete
//and recreate 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("Customer");
//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 two SyncGroups, so that changes to OrderHeader
//and OrderDetail are made in one transaction. Depending on
//application requirements, you might include Customer
//in the same group.
SyncGroup customerSyncGroup = new SyncGroup("Customer");
SyncGroup orderSyncGroup = new SyncGroup("Order");
//Add each table: specify a synchronization direction of
//DownloadOnly.
SyncTable customerSyncTable = new SyncTable("Customer");
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerSyncTable.SyncDirection = SyncDirection.DownloadOnly;
customerSyncTable.SyncGroup = customerSyncGroup;
this.Configuration.SyncTables.Add(customerSyncTable);
SyncTable orderHeaderSyncTable = new SyncTable("OrderHeader");
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly;
orderHeaderSyncTable.SyncGroup = orderSyncGroup;
this.Configuration.SyncTables.Add(orderHeaderSyncTable);
SyncTable orderDetailSyncTable = new SyncTable("OrderDetail");
orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
orderDetailSyncTable.SyncDirection = SyncDirection.DownloadOnly;
orderDetailSyncTable.SyncGroup = orderSyncGroup;
this.Configuration.SyncTables.Add(orderDetailSyncTable);
//Specify a value for the @SalesPerson parameter that is added
//in the server synchronization provider. This value would
//typically be provided by a user in the application, but we
//have hardcoded it here for convenience.
this.Configuration.SyncParameters.Add(
new SyncParameter("@SalesPerson", "Brenda Diaz"));
}
}
//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 use a timestamp value
//that is retrieved and stored in the client database.
//During each synchronization, the new anchor value and
//the last anchor value from the previous synchronization
//are used: the set of changes between these upper and
//lower bounds is synchronized.
//
//SyncSession.SyncNewReceivedAnchor is a string constant;
//you could also use @sync_new_received_anchor directly in
//your queries.
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;
//Create a SyncAdapter for each table, and then define
//the commands to synchronize changes:
//* SelectIncrementalInsertsCommand, SelectIncrementalUpdatesCommand,
// and SelectIncrementalDeletesCommand are used to select changes
// from the server that the client provider then applies to the client.
//* Specify if you want only certain columns at the client by
// using the SELECT statement in the command.
//* Filter rows by using the WHERE clause in the command.
// In this case, we filter out SalesPerson.
//
//Customer table
//
//Create the SyncAdapter
SyncAdapter customerSyncAdapter = new SyncAdapter("Customer");
//Select inserts from the server
SqlCommand customerIncrInserts = new SqlCommand();
customerIncrInserts.CommandText =
"SELECT CustomerId, CustomerName, CustomerType " +
"FROM Sales.Customer " +
"WHERE SalesPerson = @SalesPerson " +
"AND (InsertTimestamp > @sync_last_received_anchor " +
"AND InsertTimestamp <= @sync_new_received_anchor " +
"AND InsertId <> @sync_client_id)";
customerIncrInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
customerIncrInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerIncrInserts.Connection = serverConn;
customerSyncAdapter.SelectIncrementalInsertsCommand = customerIncrInserts;
//Select updates from the server
SqlCommand customerIncrUpdates = new SqlCommand();
customerIncrUpdates.CommandText =
"SELECT CustomerId, CustomerName, CustomerType " +
"FROM Sales.Customer " +
"WHERE SalesPerson = @SalesPerson " +
"AND (UpdateTimestamp > @sync_last_received_anchor " +
"AND UpdateTimestamp <= @sync_new_received_anchor " +
"AND UpdateId <> @sync_client_id " +
"AND NOT (InsertTimestamp > @sync_last_received_anchor " +
"AND InsertId <> @sync_client_id))";
customerIncrUpdates.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerIncrUpdates.Connection = serverConn;
customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates;
//Select deletes from the server
SqlCommand customerIncrDeletes = new SqlCommand();
customerIncrDeletes.CommandText =
"SELECT CustomerId, CustomerName, CustomerType " +
"FROM Sales.Customer_Tombstone " +
"WHERE SalesPerson = @SalesPerson " +
"AND (@sync_initialized = 1 " +
"AND DeleteTimestamp > @sync_last_received_anchor " +
"AND DeleteTimestamp <= @sync_new_received_anchor " +
"AND DeleteId <> @sync_client_id)";
customerIncrDeletes.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
customerIncrDeletes.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit);
customerIncrDeletes.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerIncrDeletes.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
customerIncrDeletes.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerIncrDeletes.Connection = serverConn;
customerSyncAdapter.SelectIncrementalDeletesCommand = customerIncrDeletes;
//Add the SyncAdapter to the server synchronization provider
this.SyncAdapters.Add(customerSyncAdapter);
//
//OrderHeader table
//
//Create the SyncAdapter
SyncAdapter orderHeaderSyncAdapter = new SyncAdapter("OrderHeader");
//Select inserts from the server
SqlCommand orderHeaderIncrInserts = new SqlCommand();
orderHeaderIncrInserts.CommandText =
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " +
"FROM Sales.OrderHeader oh " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE c.SalesPerson = @SalesPerson " +
"AND (oh.InsertTimestamp > @sync_last_received_anchor " +
"AND oh.InsertTimestamp <= @sync_new_received_anchor " +
"AND oh.InsertId <> @sync_client_id)";
orderHeaderIncrInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderHeaderIncrInserts.Connection = serverConn;
orderHeaderSyncAdapter.SelectIncrementalInsertsCommand = orderHeaderIncrInserts;
//Select updates from the server
SqlCommand orderHeaderIncrUpdates = new SqlCommand();
orderHeaderIncrUpdates.CommandText =
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " +
"FROM Sales.OrderHeader oh " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE c.SalesPerson = @SalesPerson " +
"AND (oh.UpdateTimestamp > @sync_last_received_anchor " +
"AND oh.UpdateTimestamp <= @sync_new_received_anchor " +
"AND oh.UpdateId <> @sync_client_id " +
"AND NOT (oh.InsertTimestamp > @sync_last_received_anchor " +
"AND oh.InsertId <> @sync_client_id))";
orderHeaderIncrUpdates.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderHeaderIncrUpdates.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrUpdates.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderHeaderIncrUpdates.Connection = serverConn;
orderHeaderSyncAdapter.SelectIncrementalUpdatesCommand = orderHeaderIncrUpdates;
//Select deletes from the server
SqlCommand orderHeaderIncrDeletes = new SqlCommand();
orderHeaderIncrDeletes.CommandText =
"SELECT oht.OrderId, oht.CustomerId, oht.OrderDate, oht.OrderStatus " +
"FROM Sales.OrderHeader_Tombstone oht " +
"JOIN Sales.Customer c ON oht.CustomerId = c.CustomerId " +
"WHERE c.SalesPerson = @SalesPerson " +
"AND (@sync_initialized = 1 " +
"AND oht.DeleteTimestamp > @sync_last_received_anchor " +
"AND oht.DeleteTimestamp <= @sync_new_received_anchor " +
"AND oht.DeleteId <> @sync_client_id)";
orderHeaderIncrDeletes.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderHeaderIncrDeletes.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit);
orderHeaderIncrDeletes.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrDeletes.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderHeaderIncrDeletes.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderHeaderIncrDeletes.Connection = serverConn;
orderHeaderSyncAdapter.SelectIncrementalDeletesCommand = orderHeaderIncrDeletes;
//Add the SyncAdapter to the server synchronization provider
this.SyncAdapters.Add(orderHeaderSyncAdapter);
//
//OrderDetail table
//
//Create the SyncAdapter
SyncAdapter orderDetailSyncAdapter = new SyncAdapter("OrderDetail");
//Select inserts from the server
SqlCommand orderDetailIncrInserts = new SqlCommand();
orderDetailIncrInserts.CommandText =
"SELECT od.OrderDetailId, od.OrderId, od.Product, od.Quantity " +
"FROM Sales.OrderDetail od " +
"JOIN Sales.OrderHeader oh ON od.OrderId = oh.OrderId " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE SalesPerson = @SalesPerson " +
"AND (od.InsertTimestamp > @sync_last_received_anchor " +
"AND od.InsertTimestamp <= @sync_new_received_anchor " +
"AND od.InsertId <> @sync_client_id)";
orderDetailIncrInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderDetailIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderDetailIncrInserts.Connection = serverConn;
orderDetailSyncAdapter.SelectIncrementalInsertsCommand = orderDetailIncrInserts;
//Select updates from the server
SqlCommand orderDetailIncrUpdates = new SqlCommand();
orderDetailIncrUpdates.CommandText =
"SELECT od.OrderDetailId, od.OrderId, od.Product, od.Quantity " +
"FROM Sales.OrderDetail od " +
"JOIN Sales.OrderHeader oh ON od.OrderId = oh.OrderId " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE SalesPerson = @SalesPerson " +
"AND (od.UpdateTimestamp > @sync_last_received_anchor " +
"AND od.UpdateTimestamp <= @sync_new_received_anchor " +
"AND od.UpdateId <> @sync_client_id " +
"AND NOT (od.InsertTimestamp > @sync_last_received_anchor " +
"AND od.InsertId <> @sync_client_id))";
orderDetailIncrUpdates.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderDetailIncrUpdates.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrUpdates.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderDetailIncrUpdates.Connection = serverConn;
orderDetailSyncAdapter.SelectIncrementalUpdatesCommand = orderDetailIncrUpdates;
//Select deletes from the server
SqlCommand orderDetailIncrDeletes = new SqlCommand();
orderDetailIncrDeletes.CommandText =
"SELECT odt.OrderDetailId, odt.OrderId, odt.Product, odt.Quantity " +
"FROM Sales.OrderDetail_Tombstone odt " +
"JOIN Sales.OrderHeader oh ON odt.OrderId = oh.OrderId " +
"JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " +
"WHERE SalesPerson = @SalesPerson " +
"AND (@sync_initialized = 1 " +
"AND odt.DeleteTimestamp > @sync_last_received_anchor " +
"AND odt.DeleteTimestamp <= @sync_new_received_anchor " +
"AND odt.DeleteId <> @sync_client_id)";
orderDetailIncrDeletes.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
orderDetailIncrDeletes.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit);
orderDetailIncrDeletes.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrDeletes.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
orderDetailIncrDeletes.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
orderDetailIncrDeletes.Connection = serverConn;
orderDetailSyncAdapter.SelectIncrementalDeletesCommand = orderDetailIncrDeletes;
//Add the SyncAdapter to the server synchronization provider
this.SyncAdapters.Add(orderDetailSyncAdapter);
}
}
//Create a class that is derived from
//Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
//You can just instantiate the provider directly and associate it
//with the SyncAgent, but you could use this class to handle client
//provider events and other client-side processing.
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 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 ******");
}
else if (syncType == "subsequent")
{
Console.WriteLine("***** Subsequent Synchronization ****");
}
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.
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("Customer")
'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 two SyncGroups so that changes to OrderHeader
'and OrderDetail are made in one transaction. Depending on
'application requirements, you might include Customer
'in the same group.
Dim customerSyncGroup As New SyncGroup("Customer")
Dim orderSyncGroup As New SyncGroup("Order")
'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 = customerSyncGroup
Me.Configuration.SyncTables.Add(customerSyncTable)
Dim orderHeaderSyncTable As New SyncTable("OrderHeader")
orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
orderHeaderSyncTable.SyncDirection = SyncDirection.DownloadOnly
orderHeaderSyncTable.SyncGroup = orderSyncGroup
Me.Configuration.SyncTables.Add(orderHeaderSyncTable)
Dim orderDetailSyncTable As New SyncTable("OrderDetail")
orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
orderDetailSyncTable.SyncDirection = SyncDirection.DownloadOnly
orderDetailSyncTable.SyncGroup = orderSyncGroup
Me.Configuration.SyncTables.Add(orderDetailSyncTable)
'Specify a value for the @SalesPerson parameter that is added
'in the server synchronization provider. This value would
'typically be provided by a user in the application, but we
'have hardcoded it here for convenience.
Me.Configuration.SyncParameters.Add(New SyncParameter("@SalesPerson", "Brenda Diaz"))
End Sub 'New
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 use a timestamp value
'that is retrieved and stored in the client database.
'During each synchronization, the new anchor value and
'the last anchor value from the previous synchronization
'are used: the set of changes between these upper and
'lower bounds is synchronized.
'
'SyncSession.SyncNewReceivedAnchor is a string constant;
'you could also use @sync_new_received_anchor directly in
'your queries.
Dim selectNewAnchorCommand As New SqlCommand()
Dim newAnchorVariable As String = "@" + SyncSession.SyncNewReceivedAnchor
With selectNewAnchorCommand
.CommandText = "SELECT " + newAnchorVariable + " = min_active_rowversion() - 1"
.Parameters.Add(newAnchorVariable, SqlDbType.Timestamp)
.Parameters(newAnchorVariable).Direction = ParameterDirection.Output
.Connection = serverConn
End With
Me.SelectNewAnchorCommand = selectNewAnchorCommand
'Create a SyncAdapter for each table, and then define
'the commands to synchronize changes:
'* SelectIncrementalInsertsCommand, SelectIncrementalUpdatesCommand,
' and SelectIncrementalDeletesCommand are used to select changes
' from the server that the client provider then applies to the client.
'* Specify if you want only certain columns at the client by
' using the SELECT statement in the command.
'* Filter rows by using the WHERE clause in the command.
' In this case, we filter out SalesPerson.
'
'Customer table.
'
'Create the SyncAdapter.
Dim customerSyncAdapter As New SyncAdapter("Customer")
'Select inserts from the server.
Dim customerIncrInserts As New SqlCommand()
With customerIncrInserts
.CommandText = _
"SELECT CustomerId, CustomerName, CustomerType " _
& "FROM Sales.Customer " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (InsertTimestamp > @sync_last_received_anchor " _
& "AND InsertTimestamp <= @sync_new_received_anchor " _
& "AND InsertId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
customerSyncAdapter.SelectIncrementalInsertsCommand = customerIncrInserts
'Select updates from the server.
Dim customerIncrUpdates As New SqlCommand()
With customerIncrUpdates
.CommandText = _
"SELECT CustomerId, CustomerName, CustomerType " _
& "FROM Sales.Customer " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (UpdateTimestamp > @sync_last_received_anchor " _
& "AND UpdateTimestamp <= @sync_new_received_anchor " _
& "AND UpdateId <> @sync_client_id " _
& "AND NOT (InsertTimestamp > @sync_last_received_anchor " _
& "AND InsertId <> @sync_client_id))"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates
'Select deletes from the server.
Dim customerIncrDeletes As New SqlCommand()
With customerIncrDeletes
.CommandText = _
"SELECT CustomerId, CustomerName, CustomerType " _
& "FROM Sales.Customer_Tombstone " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (@sync_initialized = 1 " _
& "AND DeleteTimestamp > @sync_last_received_anchor " _
& "AND DeleteTimestamp <= @sync_new_received_anchor " _
& "AND DeleteId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
customerSyncAdapter.SelectIncrementalDeletesCommand = customerIncrDeletes
'Add the SyncAdapter to the server synchronization provider.
Me.SyncAdapters.Add(customerSyncAdapter)
'
'OrderHeader table.
'
'Create the SyncAdapter.
Dim orderHeaderSyncAdapter As New SyncAdapter("OrderHeader")
'Select inserts from the server.
Dim orderHeaderIncrInserts As New SqlCommand()
With orderHeaderIncrInserts
.CommandText = _
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " _
& "FROM Sales.OrderHeader oh " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE c.SalesPerson = @SalesPerson " _
& "AND (oh.InsertTimestamp > @sync_last_received_anchor " _
& "AND oh.InsertTimestamp <= @sync_new_received_anchor " _
& "AND oh.InsertId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderHeaderSyncAdapter.SelectIncrementalInsertsCommand = orderHeaderIncrInserts
'Select updates from the server.
Dim orderHeaderIncrUpdates As New SqlCommand()
With orderHeaderIncrUpdates
.CommandText = _
"SELECT oh.OrderId, oh.CustomerId, oh.OrderDate, oh.OrderStatus " _
& "FROM Sales.OrderHeader oh " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE c.SalesPerson = @SalesPerson " _
& "AND (oh.UpdateTimestamp > @sync_last_received_anchor " _
& "AND oh.UpdateTimestamp <= @sync_new_received_anchor " _
& "AND oh.UpdateId <> @sync_client_id " _
& "AND NOT (oh.InsertTimestamp > @sync_last_received_anchor " _
& "AND oh.InsertId <> @sync_client_id))"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderHeaderSyncAdapter.SelectIncrementalUpdatesCommand = orderHeaderIncrUpdates
'Select deletes from the server.
Dim orderHeaderIncrDeletes As New SqlCommand()
With orderHeaderIncrDeletes
.CommandText = _
"SELECT oht.OrderId, oht.CustomerId, oht.OrderDate, oht.OrderStatus " _
& "FROM Sales.OrderHeader_Tombstone oht " _
& "JOIN Sales.Customer c ON oht.CustomerId = c.CustomerId " _
& "WHERE c.SalesPerson = @SalesPerson " _
& "AND (@sync_initialized = 1 " _
& "AND oht.DeleteTimestamp > @sync_last_received_anchor " _
& "AND oht.DeleteTimestamp <= @sync_new_received_anchor " _
& "AND oht.DeleteId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderHeaderSyncAdapter.SelectIncrementalDeletesCommand = orderHeaderIncrDeletes
'Add the SyncAdapter to the server synchronization provider.
Me.SyncAdapters.Add(orderHeaderSyncAdapter)
'
'OrderDetail table.
'
'Create the SyncAdapter.
Dim orderDetailSyncAdapter As New SyncAdapter("OrderDetail")
'Select inserts from the server.
Dim orderDetailIncrInserts As New SqlCommand()
With orderDetailIncrInserts
.CommandText = _
"SELECT od.OrderDetailId, od.OrderId, od.Product, od.Quantity " _
& "FROM Sales.OrderDetail od " _
& "JOIN Sales.OrderHeader oh ON od.OrderId = oh.OrderId " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (od.InsertTimestamp > @sync_last_received_anchor " _
& "AND od.InsertTimestamp <= @sync_new_received_anchor " _
& "AND od.InsertId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderDetailSyncAdapter.SelectIncrementalInsertsCommand = orderDetailIncrInserts
'Select updates from the server.
Dim orderDetailIncrUpdates As New SqlCommand()
With orderDetailIncrUpdates
.CommandText = _
"SELECT od.OrderDetailId, od.OrderId, od.Product, od.Quantity " _
& "FROM Sales.OrderDetail od " _
& "JOIN Sales.OrderHeader oh ON od.OrderId = oh.OrderId " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (od.UpdateTimestamp > @sync_last_received_anchor " _
& "AND od.UpdateTimestamp <= @sync_new_received_anchor " _
& "AND od.UpdateId <> @sync_client_id " _
& "AND NOT (od.InsertTimestamp > @sync_last_received_anchor " _
& "AND od.InsertId <> @sync_client_id))"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderDetailSyncAdapter.SelectIncrementalUpdatesCommand = orderDetailIncrUpdates
'Select deletes from the server.
Dim orderDetailIncrDeletes As New SqlCommand()
With orderDetailIncrDeletes
.CommandText = _
"SELECT odt.OrderDetailId, odt.OrderId, odt.Product, odt.Quantity " _
& "FROM Sales.OrderDetail_Tombstone odt " _
& "JOIN Sales.OrderHeader oh ON odt.OrderId = oh.OrderId " _
& "JOIN Sales.Customer c ON oh.CustomerId = c.CustomerId " _
& "WHERE SalesPerson = @SalesPerson " _
& "AND (@sync_initialized = 1 " _
& "AND odt.DeleteTimestamp > @sync_last_received_anchor " _
& "AND odt.DeleteTimestamp <= @sync_new_received_anchor " _
& "AND odt.DeleteId <> @sync_client_id)"
.Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
.Parameters.Add("@" + SyncSession.SyncInitialized, SqlDbType.Bit)
.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
.Connection = serverConn
End With
orderDetailSyncAdapter.SelectIncrementalDeletesCommand = orderDetailIncrDeletes
'Add the SyncAdapter to the server synchronization provider.
Me.SyncAdapters.Add(orderDetailSyncAdapter)
End Sub 'New
End Class 'SampleServerSyncProvider
'Create a class that is derived from
'Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider.
'You can just instantiate the provider directly and associate it
'with the SyncAgent, but you could use this class to handle client
'provider events and other client-side processing.
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 ******")
ElseIf syncType = "subsequent" Then
Console.WriteLine("***** Subsequent Synchronization ****")
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