如何在客户端上创建数据视图
本主题演示如何使用 Sync Services for ADO.NET 将两个或更多的服务器表合并到客户端数据库中的一个表中。本主题中的示例着重介绍以下 Sync Services 类型和事件:
有关如何运行示例代码的信息,请参见对常见客户端与服务器同步任务进行编程中的“帮助主题中的示例应用程序”。
示例
下面的代码示例演示如何同步 Sync Services 示例数据库中的 Customer
和 CustomerContact
表。在初始同步过程中,使用增量插入命令从两个表中选择数据,将数据下载到客户端,然后插入 CustomerInfo
表中。在后续同步过程中,则将符合要求的变更下载到客户端。
API 的要点
本节提供的代码示例指出了在合并表时要使用的 API 的要点。下面的代码示例指定 SyncTable
和 SyncAdapter
对象,它们的名称均为 CustomerInfo
。将这两个对象均指定为该名称可以使 SyncAgent 和提供程序将来自 Customer
和 CustomerContact
表的选择转换为对 CustomerInfo
表的插入。
SyncTable customerInfoSyncTable = new SyncTable("CustomerInfo");
customerInfoSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerInfoSyncTable.SyncDirection = SyncDirection.DownloadOnly;
this.Configuration.SyncTables.Add(customerInfoSyncTable);
SyncAdapter customerInfoSyncAdapter = new SyncAdapter("CustomerInfo");
Dim customerInfoSyncTable As New SyncTable("CustomerInfo")
customerInfoSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
customerInfoSyncTable.SyncDirection = SyncDirection.DownloadOnly
Me.Configuration.SyncTables.Add(customerInfoSyncTable)
Dim customerInfoSyncAdapter As New SyncAdapter("CustomerInfo")
下面的代码示例包括了 SelectIncrementalInsertsCommand 属性的查询。该查询使用联接来确保在初始同步期间,只有具有联系信息的客户被下载到客户端。在后续同步过程中,将下载对 CustomerContact
表的插入,而只有在与客户对应的行也插入到 CustomerContact
表的情况下,才下载对 Customer
表的插入。请注意,选择列表没有包括所有列。与在同步中使用的所有查询一样,每个查询的逻辑取决于应用程序的要求。例如,也可以下载所有客户,即使他们并没有联系信息。
SqlCommand customerInfoIncrementalInsertsCommand = new SqlCommand();
customerInfoIncrementalInsertsCommand.CommandType = CommandType.Text;
customerInfoIncrementalInsertsCommand.CommandText =
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " +
"FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " +
"c.CustomerId = cc.CustomerId " +
"WHERE ((c.InsertTimestamp > @sync_last_received_anchor " +
"AND c.InsertTimestamp <= @sync_new_received_anchor) OR " +
"(cc.InsertTimestamp > @sync_last_received_anchor " +
"AND cc.InsertTimestamp <= @sync_new_received_anchor))";
customerInfoIncrementalInsertsCommand.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalInsertsCommand.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalInsertsCommand.Connection = serverConn;
customerInfoSyncAdapter.SelectIncrementalInsertsCommand = customerInfoIncrementalInsertsCommand;
Dim customerInfoIncrementalInsertsCommand As New SqlCommand()
With customerInfoIncrementalInsertsCommand
.CommandType = CommandType.Text
.CommandText = _
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " _
& "FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " _
& "c.CustomerId = cc.CustomerId " _
& "WHERE ((c.InsertTimestamp > @sync_last_received_anchor " _
& "AND c.InsertTimestamp <= @sync_new_received_anchor) OR " _
& "(cc.InsertTimestamp > @sync_last_received_anchor " _
& "AND cc.InsertTimestamp <= @sync_new_received_anchor))"
.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp)
.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp)
.Connection = serverConn
End With
customerInfoSyncAdapter.SelectIncrementalInsertsCommand = customerInfoIncrementalInsertsCommand
下面的代码示例包括了 SelectIncrementalDeletesCommand 属性的查询。该查询使用两个 SELECT
语句和一个 UNION
,选择对 Customer
和 CustomerContact
表的删除。第一条 SELECT
语句与 CustomerContact
表进行联接,以检索 PhoneType
列。该列是 CustomerInfo
表的复合主键的一部分。逻辑如下所示:
如果删除了某个客户,将从客户端上删除与该客户对应的所有行。
如果删除了联系信息,则仅在客户端上删除与联系信息相对应的行。
如果同时删除了客户行和客户的联系信息,则至少下载一个额外的删除。在变更应用于客户端数据库时,这不会导致任何错误。
SqlCommand customerInfoIncrementalDeletesCommand = new SqlCommand();
customerInfoIncrementalDeletesCommand.CommandType = CommandType.Text;
customerInfoIncrementalDeletesCommand.CommandText =
"SELECT c.CustomerId, cc.PhoneType " +
"FROM Sales.Customer_Tombstone c JOIN Sales.CustomerContact cc ON " +
"c.CustomerId = cc.CustomerId " +
"WHERE (@sync_initialized = 1 " +
"AND (DeleteTimestamp > @sync_last_received_anchor " +
"AND DeleteTimestamp <= @sync_new_received_anchor)) " +
"UNION " +
"SELECT CustomerId, PhoneType " +
"FROM Sales.CustomerContact_Tombstone " +
"WHERE (@sync_initialized = 1 " +
"AND (DeleteTimestamp > @sync_last_received_anchor " +
"AND DeleteTimestamp <= @sync_new_received_anchor))";
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_initialized", SqlDbType.Bit);
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalDeletesCommand.Connection = serverConn;
customerInfoSyncAdapter.SelectIncrementalDeletesCommand = customerInfoIncrementalDeletesCommand;
Dim customerInfoIncrementalDeletesCommand As New SqlCommand()
With customerInfoIncrementalDeletesCommand
.CommandType = CommandType.Text
.CommandText = _
"SELECT c.CustomerId, cc.PhoneType " _
& "FROM Sales.Customer_Tombstone c JOIN Sales.CustomerContact cc ON " _
& "c.CustomerId = cc.CustomerId " _
& "WHERE (@sync_initialized = 1 " _
& "AND (DeleteTimestamp > @sync_last_received_anchor " _
& "AND DeleteTimestamp <= @sync_new_received_anchor)) " _
& "UNION " _
& "SELECT CustomerId, PhoneType " _
& "FROM Sales.CustomerContact_Tombstone " _
& "WHERE (@sync_initialized = 1 " _
& "AND (DeleteTimestamp > @sync_last_received_anchor " _
& "AND DeleteTimestamp <= @sync_new_received_anchor))"
.Parameters.Add("@sync_initialized", SqlDbType.Bit)
.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp)
.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp)
.Connection = serverConn
End With
customerInfoSyncAdapter.SelectIncrementalDeletesCommand = customerInfoIncrementalDeletesCommand
下面的代码示例创建 CustomerInfo
表的一个复合主键。此键与 CustomerContact
表的键相匹配。Sync Services 可以从服务器上的表中推断出架构,但是在本例中必须指定键。此外,还可以手动创建架构,如如何初始化客户端数据库和处理表架构中所述。
string[] customerInfoPrimaryKey = new string[2];
customerInfoPrimaryKey[0] = "CustomerId";
customerInfoPrimaryKey[1] = "PhoneType";
e.Schema.Tables["CustomerInfo"].PrimaryKey = customerInfoPrimaryKey;
Dim customerInfoPrimaryKey(1) As String
customerInfoPrimaryKey(0) = "CustomerId"
customerInfoPrimaryKey(1) = "PhoneType"
e.Schema.Tables("CustomerInfo").PrimaryKey = customerInfoPrimaryKey
完整的代码示例
下面的完整代码示例包括了上面介绍的代码示例以及用于执行同步的其他代码。示例需要 Utility
类,可通过用于 Sync Services 帮助主题的 Utility 类获得该类。请注意,在 Utility
类中对 Customer
表的插入不会在后续同步中进行下载,因为在 CustomerContact
表中没有与其对应的行。
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 Utility class handles all functionality that is not
//directly related to synchronization, such as holding
//connection string information and making changes to the
//server and client databases.
Utility util = new Utility();
//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.
util.SetClientPassword();
util.RecreateClientDatabase();
//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.
util.MakeDataChangesOnServer("Customer");
util.MakeDataChangesOnServer("CustomerContact");
//Subsequent synchronization.
syncStatistics = sampleSyncAgent.Synchronize();
sampleStats.DisplayStats(syncStatistics, "subsequent");
//Return server data back to its original state.
util.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();
//Add the Customer table: specify a synchronization direction of
//DownloadOnly, and that an existing table should be dropped.
SyncTable customerInfoSyncTable = new SyncTable("CustomerInfo");
customerInfoSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerInfoSyncTable.SyncDirection = SyncDirection.DownloadOnly;
this.Configuration.SyncTables.Add(customerInfoSyncTable);
}
}
//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(util.ServerConnString);
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 the CustomerInfo table. The CustomerInfo
//table on the client is a combination of the Customer and CustomerContact
//tables on the server. This table is download-only, as specified in
//SampleSyncAgent.
SyncAdapter customerInfoSyncAdapter = new SyncAdapter("CustomerInfo");
//Specify synchronization commands. The CustomerInfo table
//is download-only, so we do not define commands to apply changes to
//the server. Each command joins the base tables or tombstone tables
//to select the appropriate incremental changes. For this application,
//the logic is as follows:
//* Select all inserts for customers that have contact information.
// This results in more than one row for a customer if that customer
// has more than one phone number.
//* Select all updates for customer and contact information that has
// already been downloaded.
//* Select all deletes for customer and contact information that has
// already been downloaded. If a customer has been deleted, delete
// all of the rows for that customer. If a phone number has been
// deleted, delete only that row.
//Select inserts.
SqlCommand customerInfoIncrementalInsertsCommand = new SqlCommand();
customerInfoIncrementalInsertsCommand.CommandType = CommandType.Text;
customerInfoIncrementalInsertsCommand.CommandText =
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " +
"FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " +
"c.CustomerId = cc.CustomerId " +
"WHERE ((c.InsertTimestamp > @sync_last_received_anchor " +
"AND c.InsertTimestamp <= @sync_new_received_anchor) OR " +
"(cc.InsertTimestamp > @sync_last_received_anchor " +
"AND cc.InsertTimestamp <= @sync_new_received_anchor))";
customerInfoIncrementalInsertsCommand.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalInsertsCommand.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalInsertsCommand.Connection = serverConn;
customerInfoSyncAdapter.SelectIncrementalInsertsCommand = customerInfoIncrementalInsertsCommand;
//Select updates.
SqlCommand customerInfoIncrementalUpdatesCommand = new SqlCommand();
customerInfoIncrementalUpdatesCommand.CommandType = CommandType.Text;
customerInfoIncrementalUpdatesCommand.CommandText =
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " +
"FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " +
"c.CustomerId = cc.CustomerId " +
"WHERE ((c.UpdateTimestamp > @sync_last_received_anchor " +
"AND c.UpdateTimestamp <= @sync_new_received_anchor " +
"AND c.InsertTimestamp <= @sync_last_received_anchor) " +
"OR (cc.UpdateTimestamp > @sync_last_received_anchor " +
"AND cc.UpdateTimestamp <= @sync_new_received_anchor " +
"AND cc.InsertTimestamp <= @sync_last_received_anchor))";
customerInfoIncrementalUpdatesCommand.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalUpdatesCommand.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalUpdatesCommand.Connection = serverConn;
customerInfoSyncAdapter.SelectIncrementalUpdatesCommand = customerInfoIncrementalUpdatesCommand;
//Select deletes.
SqlCommand customerInfoIncrementalDeletesCommand = new SqlCommand();
customerInfoIncrementalDeletesCommand.CommandType = CommandType.Text;
customerInfoIncrementalDeletesCommand.CommandText =
"SELECT c.CustomerId, cc.PhoneType " +
"FROM Sales.Customer_Tombstone c JOIN Sales.CustomerContact cc ON " +
"c.CustomerId = cc.CustomerId " +
"WHERE (@sync_initialized = 1 " +
"AND (DeleteTimestamp > @sync_last_received_anchor " +
"AND DeleteTimestamp <= @sync_new_received_anchor)) " +
"UNION " +
"SELECT CustomerId, PhoneType " +
"FROM Sales.CustomerContact_Tombstone " +
"WHERE (@sync_initialized = 1 " +
"AND (DeleteTimestamp > @sync_last_received_anchor " +
"AND DeleteTimestamp <= @sync_new_received_anchor))";
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_initialized", SqlDbType.Bit);
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalDeletesCommand.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp);
customerInfoIncrementalDeletesCommand.Connection = serverConn;
customerInfoSyncAdapter.SelectIncrementalDeletesCommand = customerInfoIncrementalDeletesCommand;
//Add the SyncAdapter to the provider.
this.SyncAdapters.Add(customerInfoSyncAdapter);
}
}
//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 here we use this class to handle client
//provider events.
public class SampleClientSyncProvider : SqlCeClientSyncProvider
{
public SampleClientSyncProvider()
{
//Specify a connection string for the sample client database.
Utility util = new Utility();
this.ConnectionString = util.ClientConnString;
//Handle the two schema-related events.
this.CreatingSchema += new EventHandler<CreatingSchemaEventArgs>(SampleClientSyncProvider_CreatingSchema);
this.SchemaCreated += new EventHandler<SchemaCreatedEventArgs>(SampleClientSyncProvider_SchemaCreated);
}
private void SampleClientSyncProvider_CreatingSchema(object sender, CreatingSchemaEventArgs e)
{
Console.Write("Creating schema for " + e.Table.TableName + " | ");
//Create a compostite primary key for the CustomerInfo table.
string[] customerInfoPrimaryKey = new string[2];
customerInfoPrimaryKey[0] = "CustomerId";
customerInfoPrimaryKey[1] = "PhoneType";
e.Schema.Tables["CustomerInfo"].PrimaryKey = customerInfoPrimaryKey;
}
private void SampleClientSyncProvider_SchemaCreated(object sender, SchemaCreatedEventArgs e)
{
Console.WriteLine("Schema created for " + e.Table.TableName);
}
}
//Handle the statistics that are returned by the SyncAgent.
public class SampleStats
{
public void DisplayStats(SyncStatistics syncStatistics, string syncType)
{
Console.WriteLine(String.Empty);
if (syncType == "initial")
{
Console.WriteLine("****** Initial Synchronization ******");
}
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 Utility class handles all functionality that is not
'directly related to synchronization, such as holding
'connection string information and making changes to the
'server and client databases.
Dim util As New Utility()
'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.
util.SetClientPassword()
util.RecreateClientDatabase()
'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.
util.MakeDataChangesOnServer("Customer")
util.MakeDataChangesOnServer("CustomerContact")
'Subsequent synchronization.
syncStatistics = sampleSyncAgent.Synchronize()
sampleStats.DisplayStats(syncStatistics, "subsequent")
'Return server data back to its original state.
util.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()
'Add the Customer table: specify a synchronization direction of
'DownloadOnly, and that an existing table should be dropped.
Dim customerInfoSyncTable As New SyncTable("CustomerInfo")
customerInfoSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
customerInfoSyncTable.SyncDirection = SyncDirection.DownloadOnly
Me.Configuration.SyncTables.Add(customerInfoSyncTable)
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(util.ServerConnString)
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 the CustomerInfo table. The CustomerInfo
'table on the client is a combination of the Customer and CustomerContact
'tables on the server. This table is download-only, as specified in
'SampleSyncAgent.
Dim customerInfoSyncAdapter As New SyncAdapter("CustomerInfo")
'Specify synchronization commands. The CustomerInfo table
'is download-only, so we do not define commands to apply changes to
'the server. Each command joins the base tables or tombstone tables
'to select the appropriate incremental changes. For this application,
'the logic is as follows:
'* Select all inserts for customers that have contact information.
' This results in more than one row for a customer if that customer
' has more than one phone number.
'* Select all updates for customer and contact information that has
' already been downloaded.
'* Select all deletes for customer and contact information that has
' already been downloaded. If a customer has been deleted, delete
' all of the rows for that customer. If a phone number has been
' deleted, delete only that row.
'Select inserts.
Dim customerInfoIncrementalInsertsCommand As New SqlCommand()
With customerInfoIncrementalInsertsCommand
.CommandType = CommandType.Text
.CommandText = _
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " _
& "FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " _
& "c.CustomerId = cc.CustomerId " _
& "WHERE ((c.InsertTimestamp > @sync_last_received_anchor " _
& "AND c.InsertTimestamp <= @sync_new_received_anchor) OR " _
& "(cc.InsertTimestamp > @sync_last_received_anchor " _
& "AND cc.InsertTimestamp <= @sync_new_received_anchor))"
.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp)
.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp)
.Connection = serverConn
End With
customerInfoSyncAdapter.SelectIncrementalInsertsCommand = customerInfoIncrementalInsertsCommand
'Select updates.
Dim customerInfoIncrementalUpdatesCommand As New SqlCommand()
With customerInfoIncrementalUpdatesCommand
.CommandType = CommandType.Text
.CommandText = _
"SELECT c.CustomerId, c.CustomerName, c.SalesPerson, cc.PhoneNumber, cc.PhoneType " _
& "FROM Sales.Customer c JOIN Sales.CustomerContact cc ON " _
& "c.CustomerId = cc.CustomerId " _
& "WHERE ((c.UpdateTimestamp > @sync_last_received_anchor " _
& "AND c.UpdateTimestamp <= @sync_new_received_anchor " _
& "AND c.InsertTimestamp <= @sync_last_received_anchor) " _
& "OR (cc.UpdateTimestamp > @sync_last_received_anchor " _
& "AND cc.UpdateTimestamp <= @sync_new_received_anchor " _
& "AND cc.InsertTimestamp <= @sync_last_received_anchor))"
.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp)
.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp)
.Connection = serverConn
End With
customerInfoSyncAdapter.SelectIncrementalUpdatesCommand = customerInfoIncrementalUpdatesCommand
'Select deletes.
Dim customerInfoIncrementalDeletesCommand As New SqlCommand()
With customerInfoIncrementalDeletesCommand
.CommandType = CommandType.Text
.CommandText = _
"SELECT c.CustomerId, cc.PhoneType " _
& "FROM Sales.Customer_Tombstone c JOIN Sales.CustomerContact cc ON " _
& "c.CustomerId = cc.CustomerId " _
& "WHERE (@sync_initialized = 1 " _
& "AND (DeleteTimestamp > @sync_last_received_anchor " _
& "AND DeleteTimestamp <= @sync_new_received_anchor)) " _
& "UNION " _
& "SELECT CustomerId, PhoneType " _
& "FROM Sales.CustomerContact_Tombstone " _
& "WHERE (@sync_initialized = 1 " _
& "AND (DeleteTimestamp > @sync_last_received_anchor " _
& "AND DeleteTimestamp <= @sync_new_received_anchor))"
.Parameters.Add("@sync_initialized", SqlDbType.Bit)
.Parameters.Add("@sync_last_received_anchor", SqlDbType.Timestamp)
.Parameters.Add("@sync_new_received_anchor", SqlDbType.Timestamp)
.Connection = serverConn
End With
customerInfoSyncAdapter.SelectIncrementalDeletesCommand = customerInfoIncrementalDeletesCommand
'Add the SyncAdapter to the provider.
Me.SyncAdapters.Add(customerInfoSyncAdapter)
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 here we use this class to handle client
'provider events.
Public Class SampleClientSyncProvider
Inherits SqlCeClientSyncProvider
Public Sub New()
'Specify a connection string for the sample client database.
Dim util As New Utility()
Me.ConnectionString = util.ClientConnString
'Handle the two schema-related events.
AddHandler Me.CreatingSchema, AddressOf SampleClientSyncProvider_CreatingSchema
AddHandler Me.SchemaCreated, AddressOf SampleClientSyncProvider_SchemaCreated
End Sub 'New
Private Sub SampleClientSyncProvider_CreatingSchema(ByVal sender As Object, ByVal e As CreatingSchemaEventArgs)
Console.Write("Creating schema for " + e.Table.TableName + " | ")
'Create a compostite primary key for the CustomerInfo table.
Dim customerInfoPrimaryKey(1) As String
customerInfoPrimaryKey(0) = "CustomerId"
customerInfoPrimaryKey(1) = "PhoneType"
e.Schema.Tables("CustomerInfo").PrimaryKey = customerInfoPrimaryKey
End Sub 'SampleClientSyncProvider_CreatingSchema
Private Sub SampleClientSyncProvider_SchemaCreated(ByVal sender As Object, ByVal e As SchemaCreatedEventArgs)
Console.WriteLine("Schema created for " + e.Table.TableName)
End Sub 'SampleClientSyncProvider_SchemaCreated
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