共用方式為


HOW TO:初始化用戶端資料庫及使用資料表結構描述

本主題描述如何初始化 SQL Server Compact 用戶端資料庫,並使用該資料庫中的結構描述。本主題的範例將重點放在下列 Sync Framework 型別與事件:

如需有關如何執行範例程式碼的詳細資訊,請參閱撰寫一般用戶端和伺服器同步處理工作中的<HOW-TO 主題中的範例應用程式>。

了解初始化

初始化用戶端資料庫一般牽涉到將使用者結構描述和資料複製到資料庫,以及啟用資料庫上的變更追蹤。(變更追蹤不能用於快照集 (Snapshot) 同步處理)。如需詳細資訊,請參閱用戶端與伺服器同步處理的架構與類別。根據預設,使用者結構描述和資料,會由伺服器同步處理提供者從伺服器資料庫上複製,然後使用用戶端同步處理提供者,套用至用戶端資料庫。根據預設,即使是在僅限上傳的情節中,結構描述也是在用戶端上建立的。您也可以手動建立用戶端資料庫,並指定在資料庫中的資料表應該在同步處理發生時保留下來。

根據預設,下列條件約束不會複製到用戶端:FOREIGN KEY 條件約束、UNIQUE 條件約束和 DEFAULT 條件約束。如範例中所示,您可以在應用程式需要它們的時候,加入這些條件約束。此外,有些資料行型別在用戶端上的處理方式也與在伺服器上不同。如需詳細資訊,請參閱資料類型對應及考量

在用戶端資料庫中進行結構描述初始化後,系統就會使用在 SelectIncrementalInsertsCommand 屬性中指定的查詢來下載參與快照集、僅限下載和雙向 (Bidirectional) 同步處理的資料表的初始資料。系統也會指派用戶端 ID 給用戶端資料庫。這個 ID 是一個 GUID,透過 ClientId 屬性公開 (Expose),讓伺服器對該用戶端可進行唯一識別。在建立用戶端資料庫後,針對其應用程式是您部署中的一部分的資料庫,您可以封裝 (Package) 它的複本。在第一次同步處理期間,在每個用戶端上都會產生用戶端 ID。

範例

根據預設,初始化用戶端資料庫不需要其他的程式碼。如需範例,請參閱 HOW TO:下載資料的快照集至用戶端中的程式碼。不過,API 能提供更多的彈性。下列範例示範四種可在用戶端資料庫中初始化並使用資料表結構描述的方法:

  • Customer 資料表使用呼叫 Utility 類別上的方法進行初始化。此方法使用 SQL,在用戶端上建立結構描述。關於 Utility 類別,請參閱資料庫提供者公用程式類別的 HOW-TO 主題。沒有出現在伺服器上的 SalesNotes 資料行,會加入資料表中。此資料行可用於用戶端,但是資料就不會進行同步。當 Customer 資料表中定義了 SyncTable,也會指定 CreationOption 屬性的 UseExistingTableOrFail 值。如此可確保在第一次同步處理期間不會覆寫掉該資料表。

  • CustomerContact 資料表會根據預設值初始化。針對這個資料表和下面兩個資料表,DropExistingOrCreateNewTable 的值都已經指定了。

  • 會以傳遞資料集到 SyncSchema 建構函式 (Constructor) 的方式,初始化 OrderHeader 資料表。而資料集是以呼叫 Utility 類別上的方法來建立的。

  • 透過加入資料表與其資料行至使用 OrderHeader 資料集建立的 SyncSchema 物件,來進行 OrderDetail 資料表的初始化。如程式碼中所示,對於在用戶端上建立結構描述的方法,您有很大的控制權。Sync Framework 讓您能夠指定 ADO.NET 所公開的完整範圍的結構描述相關屬性。

程式碼中也顯示兩個與建立結構描述有關的 SqlCeClientSyncProvider 事件:

  • CreatingSchema. 此事件會在結構描述建立之前引發。它的用途是透過使用 API 變更結構描述。

  • SchemaCreated. 此事件會在結構描述建立之後引發。它的用途是透過使用 SQL 變更結構描述。

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);

            //Create the Customer table on the client by using SQL. We add
            //a SalesNotes column that will not be synchronized.
            //When we create the Customer SyncTable, we specify that
            //Sync Framework should use an existing table.          
            Utility.CreateTableOnClient();

            //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 and client.
            Utility.MakeDataChangesOnServer("Customer");
            Utility.MakeDataChangesOnClient("Customer");

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

            //Return the 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
            //and CustomerContact in the same group.
            SyncGroup customerSyncGroup = new SyncGroup("Customer");
            SyncGroup orderSyncGroup = new SyncGroup("Order");

            //Add each table: specify a synchronization direction of
            //Bidirectional. We create the Customer table before sync:
            //we set CreationOption to UseExistingTableOrFail so
            //we are sure that the table exists.
            SyncTable customerSyncTable = new SyncTable("Customer");
            customerSyncTable.CreationOption = TableCreationOption.UseExistingTableOrFail;
            customerSyncTable.SyncDirection = SyncDirection.Bidirectional;
            customerSyncTable.SyncGroup = customerSyncGroup;
            this.Configuration.SyncTables.Add(customerSyncTable);

            SyncTable customerContactSyncTable = new SyncTable("CustomerContact");
            customerContactSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            customerContactSyncTable.SyncDirection = SyncDirection.Bidirectional;
            customerContactSyncTable.SyncGroup = customerSyncGroup;
            this.Configuration.SyncTables.Add(customerContactSyncTable);

            SyncTable orderHeaderSyncTable = new SyncTable("OrderHeader");
            orderHeaderSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            orderHeaderSyncTable.SyncDirection = SyncDirection.Bidirectional;
            orderHeaderSyncTable.SyncGroup = orderSyncGroup;
            this.Configuration.SyncTables.Add(orderHeaderSyncTable);            

            SyncTable orderDetailSyncTable = new SyncTable("OrderDetail");
            orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            orderDetailSyncTable.SyncDirection = SyncDirection.Bidirectional;
            orderDetailSyncTable.SyncGroup = orderSyncGroup;
            this.Configuration.SyncTables.Add(orderDetailSyncTable);
        }
    }

    //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 SyncAdapters for each table by using the SqlSyncAdapterBuilder:
            //  * Specify the base table and tombstone table names.
            //  * Specify the columns that are used to track when
            //    and where changes are made.
            //  * Specify bidirectional synchronization, so that all
            //    commands are generated.
            //  * Call ToSyncAdapter to create the SyncAdapter.
            //  * Specify a name for the SyncAdapter that matches the
            //    the name specified for the corresponding SyncTable.
            //    Do not include the schema names (Sales in this case).
                        
            //Customer table
            SqlSyncAdapterBuilder customerBuilder = new SqlSyncAdapterBuilder(serverConn);
            
            customerBuilder.TableName = "Sales.Customer";
            customerBuilder.TombstoneTableName = customerBuilder.TableName + "_Tombstone";
            customerBuilder.SyncDirection = SyncDirection.Bidirectional;
            customerBuilder.CreationTrackingColumn = "InsertTimestamp";
            customerBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            customerBuilder.DeletionTrackingColumn = "DeleteTimestamp";
            customerBuilder.CreationOriginatorIdColumn = "InsertId";
            customerBuilder.UpdateOriginatorIdColumn = "UpdateId";
            customerBuilder.DeletionOriginatorIdColumn = "DeleteId";

            SyncAdapter customerSyncAdapter = customerBuilder.ToSyncAdapter();
            customerSyncAdapter.TableName = "Customer";
            this.SyncAdapters.Add(customerSyncAdapter);


            //CustomerContact table.
            SqlSyncAdapterBuilder customerContactBuilder = new SqlSyncAdapterBuilder(serverConn);

            customerContactBuilder.TableName = "Sales.CustomerContact";
            customerContactBuilder.TombstoneTableName = customerContactBuilder.TableName + "_Tombstone";
            customerContactBuilder.SyncDirection = SyncDirection.Bidirectional;
            customerContactBuilder.CreationTrackingColumn = "InsertTimestamp";
            customerContactBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            customerContactBuilder.DeletionTrackingColumn = "DeleteTimestamp";
            customerContactBuilder.CreationOriginatorIdColumn = "InsertId";
            customerContactBuilder.UpdateOriginatorIdColumn = "UpdateId";
            customerContactBuilder.DeletionOriginatorIdColumn = "DeleteId";
            
            SyncAdapter customerContactSyncAdapter = customerContactBuilder.ToSyncAdapter();
            customerContactSyncAdapter.TableName = "CustomerContact";
            this.SyncAdapters.Add(customerContactSyncAdapter);


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

            orderHeaderBuilder.TableName = "Sales.OrderHeader";
            orderHeaderBuilder.TombstoneTableName = orderHeaderBuilder.TableName + "_Tombstone";
            orderHeaderBuilder.SyncDirection = SyncDirection.Bidirectional;
            orderHeaderBuilder.CreationTrackingColumn = "InsertTimestamp";
            orderHeaderBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            orderHeaderBuilder.DeletionTrackingColumn = "DeleteTimestamp";
            orderHeaderBuilder.CreationOriginatorIdColumn = "InsertId";
            orderHeaderBuilder.UpdateOriginatorIdColumn = "UpdateId";
            orderHeaderBuilder.DeletionOriginatorIdColumn = "DeleteId";

            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.Bidirectional;
            orderDetailBuilder.CreationTrackingColumn = "InsertTimestamp";
            orderDetailBuilder.UpdateTrackingColumn = "UpdateTimestamp";
            orderDetailBuilder.DeletionTrackingColumn = "DeleteTimestamp";
            orderDetailBuilder.CreationOriginatorIdColumn = "InsertId";
            orderDetailBuilder.UpdateOriginatorIdColumn = "UpdateId";
            orderDetailBuilder.DeletionOriginatorIdColumn = "DeleteId";

            SyncAdapter orderDetailSyncAdapter = orderDetailBuilder.ToSyncAdapter();
            orderDetailSyncAdapter.TableName = "OrderDetail";
            this.SyncAdapters.Add(orderDetailSyncAdapter);

            //Create the schema for the OrderHeader and OrderDetail tables.
            //We first create a schema based on a DataSet that contains only
            //the OrderHeader table. As with the SyncAdapter, the table name
            //must match the SyncTable name. We then add the schema for the 
            //OrderDetail table; this is the place to map data types if
            //your application requires it.
            DataSet orderHeaderDataSet = Utility.CreateDataSetFromServer();
            orderHeaderDataSet.Tables[0].TableName = "OrderHeader";
            this.Schema = new SyncSchema(orderHeaderDataSet);
            
            this.Schema.Tables.Add("OrderDetail");

            this.Schema.Tables["OrderDetail"].Columns.Add("OrderDetailId");
            this.Schema.Tables["OrderDetail"].Columns["OrderDetailId"].ProviderDataType = "int";
            this.Schema.Tables["OrderDetail"].Columns["OrderDetailId"].AllowNull = false;

            this.Schema.Tables["OrderDetail"].Columns.Add("OrderId");
            this.Schema.Tables["OrderDetail"].Columns["OrderId"].ProviderDataType = "uniqueidentifier";
            this.Schema.Tables["OrderDetail"].Columns["OrderId"].RowGuid = true;
            this.Schema.Tables["OrderDetail"].Columns["OrderId"].AllowNull = false;

            this.Schema.Tables["OrderDetail"].Columns.Add("Product");
            this.Schema.Tables["OrderDetail"].Columns["Product"].ProviderDataType = "nvarchar";
            this.Schema.Tables["OrderDetail"].Columns["Product"].MaxLength = 100;
            this.Schema.Tables["OrderDetail"].Columns["Product"].AllowNull = false;

            this.Schema.Tables["OrderDetail"].Columns.Add("Quantity");
            this.Schema.Tables["OrderDetail"].Columns["Quantity"].ProviderDataType = "int";
            this.Schema.Tables["OrderDetail"].Columns["Quantity"].AllowNull = false;
           
            //The primary key columns are passed as a string array.
            string[] orderDetailPrimaryKey = new string[2];
            orderDetailPrimaryKey[0] = "OrderDetailId";
            orderDetailPrimaryKey[1] = "OrderId";
            this.Schema.Tables["OrderDetail"].PrimaryKey = orderDetailPrimaryKey;
        }
    }

    //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 = Utility.ConnStr_SqlCeClientSync;

            //We use the CreatingSchema event to change the schema
            //by using the API. We use the SchemaCreated event 
            //to change the schema by using SQL.
            //Note that both schema events fire for the Customer table,
            //even though we already created the table. This allows us
            //to work with the table at this point if we need to.
            this.CreatingSchema += new EventHandler<CreatingSchemaEventArgs>(SampleClientSyncProvider_CreatingSchema);
            this.SchemaCreated += new EventHandler<SchemaCreatedEventArgs>(SampleClientSyncProvider_SchemaCreated);
        }

        private void SampleClientSyncProvider_CreatingSchema(object sender, CreatingSchemaEventArgs e)
        {
            
            string tableName = e.Table.TableName;
            
            Console.Write("Creating schema for " + tableName + " | ");
            
            if (tableName == "OrderHeader")
            {
                //Set the RowGuid property because it is not copied
                //to the client by default. This is also a good time
                //to specify literal defaults with .Columns[ColName].DefaultValue,
                //but we will specify defaults like NEWID() by calling
                //ALTER TABLE after the table is created.
                e.Schema.Tables["OrderHeader"].Columns["OrderId"].RowGuid = true;
                
            }

            if (tableName == "OrderDetail")
            {
                //Add a foreign key between the OrderDetail and OrderHeader tables.
                e.Schema.Tables["OrderDetail"].ForeignKeys.Add("FK_OrderDetail_OrderHeader", "OrderHeader", "OrderId", "OrderDetail", "OrderId");
            }
        }

        private void SampleClientSyncProvider_SchemaCreated(object sender, SchemaCreatedEventArgs e)
        {
            string tableName = e.Table.TableName; 
            Utility util = new Utility();

            //Call ALTER TABLE on the client. This must be done
            //over the same connection and within the same
            //transaction that Sync Framework uses
            //to create the schema on the client.
            if (tableName == "OrderHeader")
            {
                Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, "OrderHeader");                
            }

            if (tableName == "OrderDetail")
            {
                Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, "OrderDetail");                
            }

            Console.WriteLine("Schema created for " + 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 Uploaded: " + syncStatistics.TotalChangesUploaded);
            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)

        'Create the Customer table on the client by using SQL. We add
        'a SalesNotes column that will not be synchronized.
        'When we create the Customer SyncTable, we specify that
        'Sync Framework should use an existing table.          
        Utility.CreateTableOnClient()

        '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 and client.
        Utility.MakeDataChangesOnServer("Customer")
        Utility.MakeDataChangesOnClient("Customer")

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

        'Return the 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
        'and CustomerContact in the same group.
        Dim customerSyncGroup As New SyncGroup("Customer")
        Dim orderSyncGroup As New SyncGroup("Order")

        'Add each table: specify a synchronization direction of
        'Bidirectional. We create the Customer table before sync:
        'we set CreationOption to UseExistingTableOrFail so
        'we are sure that the table exists.
        Dim customerSyncTable As New SyncTable("Customer")
        customerSyncTable.CreationOption = TableCreationOption.UseExistingTableOrFail
        customerSyncTable.SyncDirection = SyncDirection.Bidirectional
        customerSyncTable.SyncGroup = customerSyncGroup
        Me.Configuration.SyncTables.Add(customerSyncTable)

        Dim customerContactSyncTable As New SyncTable("CustomerContact")
        customerContactSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
        customerContactSyncTable.SyncDirection = SyncDirection.Bidirectional
        customerContactSyncTable.SyncGroup = customerSyncGroup
        Me.Configuration.SyncTables.Add(customerContactSyncTable)

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

        Dim orderDetailSyncTable As New SyncTable("OrderDetail")
        orderDetailSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
        orderDetailSyncTable.SyncDirection = SyncDirection.Bidirectional
        orderDetailSyncTable.SyncGroup = orderSyncGroup
        Me.Configuration.SyncTables.Add(orderDetailSyncTable)

    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 SyncAdapters for each table by using the SqlSyncAdapterBuilder:
        '  * Specify the base table and tombstone table names.
        '  * Specify the columns that are used to track when
        '    and where changes are made.
        '  * Specify bidirectional synchronization, so that all
        '    commands are generated.
        '  * Call ToSyncAdapter to create the SyncAdapter.
        '  * Specify a name for the SyncAdapter that matches the
        '    the name specified for the corresponding SyncTable.
        '    Do not include the schema names (Sales in this case).
        'Customer table
        Dim customerBuilder As New SqlSyncAdapterBuilder(serverConn)

        With customerBuilder
            .TableName = "Sales.Customer"
            .TombstoneTableName = customerBuilder.TableName + "_Tombstone"
            .SyncDirection = SyncDirection.Bidirectional
            .CreationTrackingColumn = "InsertTimestamp"
            .UpdateTrackingColumn = "UpdateTimestamp"
            .DeletionTrackingColumn = "DeleteTimestamp"
            .CreationOriginatorIdColumn = "InsertId"
            .UpdateOriginatorIdColumn = "UpdateId"
            .DeletionOriginatorIdColumn = "DeleteId"
        End With

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


        'CustomerContact table.
        Dim customerContactBuilder As New SqlSyncAdapterBuilder(serverConn)

        With customerContactBuilder
            .TableName = "Sales.CustomerContact"
            .TombstoneTableName = customerContactBuilder.TableName + "_Tombstone"
            .SyncDirection = SyncDirection.Bidirectional
            .CreationTrackingColumn = "InsertTimestamp"
            .UpdateTrackingColumn = "UpdateTimestamp"
            .DeletionTrackingColumn = "DeleteTimestamp"
            .CreationOriginatorIdColumn = "InsertId"
            .UpdateOriginatorIdColumn = "UpdateId"
            .DeletionOriginatorIdColumn = "DeleteId"
        End With

        Dim customerContactSyncAdapter As SyncAdapter = customerContactBuilder.ToSyncAdapter()
        customerContactSyncAdapter.TableName = "CustomerContact"
        Me.SyncAdapters.Add(customerContactSyncAdapter)


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

        With orderHeaderBuilder
            .TableName = "Sales.OrderHeader"
            .TombstoneTableName = orderHeaderBuilder.TableName + "_Tombstone"
            .SyncDirection = SyncDirection.Bidirectional
            .CreationTrackingColumn = "InsertTimestamp"
            .UpdateTrackingColumn = "UpdateTimestamp"
            .DeletionTrackingColumn = "DeleteTimestamp"
            .CreationOriginatorIdColumn = "InsertId"
            .UpdateOriginatorIdColumn = "UpdateId"
            .DeletionOriginatorIdColumn = "DeleteId"
        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.Bidirectional
            .CreationTrackingColumn = "InsertTimestamp"
            .UpdateTrackingColumn = "UpdateTimestamp"
            .DeletionTrackingColumn = "DeleteTimestamp"
            .CreationOriginatorIdColumn = "InsertId"
            .UpdateOriginatorIdColumn = "UpdateId"
            .DeletionOriginatorIdColumn = "DeleteId"
        End With

        Dim orderDetailSyncAdapter As SyncAdapter = orderDetailBuilder.ToSyncAdapter()
        orderDetailSyncAdapter.TableName = "OrderDetail"
        Me.SyncAdapters.Add(orderDetailSyncAdapter)

        'Create the schema for the OrderHeader and OrderDetail tables.
        'We first create a schema based on a DataSet that contains only
        'the OrderHeader table. As with the SyncAdapter, the table name
        'must match the SyncTable name. We then add the schema for the 
        'OrderDetail table; this is the place to map data types if
        'your application requires it.
        Dim orderHeaderDataSet As DataSet = Utility.CreateDataSetFromServer()
        orderHeaderDataSet.Tables(0).TableName = "OrderHeader"
        Me.Schema = New SyncSchema(orderHeaderDataSet)

        With Me.Schema
            .Tables.Add("OrderDetail")

            .Tables("OrderDetail").Columns.Add("OrderDetailId")
            .Tables("OrderDetail").Columns("OrderDetailId").ProviderDataType = "int"
            .Tables("OrderDetail").Columns("OrderDetailId").AllowNull = False

            .Tables("OrderDetail").Columns.Add("OrderId")
            .Tables("OrderDetail").Columns("OrderId").ProviderDataType = "uniqueidentifier"
            .Tables("OrderDetail").Columns("OrderId").RowGuid = True
            .Tables("OrderDetail").Columns("OrderId").AllowNull = False

            .Tables("OrderDetail").Columns.Add("Product")
            .Tables("OrderDetail").Columns("Product").ProviderDataType = "nvarchar"
            .Tables("OrderDetail").Columns("Product").MaxLength = 100
            .Tables("OrderDetail").Columns("Product").AllowNull = False

            .Tables("OrderDetail").Columns.Add("Quantity")
            .Tables("OrderDetail").Columns("Quantity").ProviderDataType = "int"
            .Tables("OrderDetail").Columns("Quantity").AllowNull = False
        End With        

        'The primary key columns are passed as a string array.
        Dim orderDetailPrimaryKey(1) As String
        orderDetailPrimaryKey(0) = "OrderDetailId"
        orderDetailPrimaryKey(1) = "OrderId"
        Me.Schema.Tables("OrderDetail").PrimaryKey = orderDetailPrimaryKey

    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 = Utility.ConnStr_SqlCeClientSync

        'We use the CreatingSchema event to change the schema
        'by using the API. We use the SchemaCreated event 
        'to change the schema by using SQL.
        'Note that both schema events fire for the Customer table,
        'even though we already created the table. This allows us
        'to work with the table at this point if we need to.
        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)

        Dim tableName As String = e.Table.TableName

        Console.Write("Creating schema for " + tableName + " | ")

        If tableName = "OrderHeader" Then
            'Set the RowGuid property because it is not copied
            'to the client by default. This is also a good time
            'to specify literal defaults with .Columns[ColName].DefaultValue,
            'but we will specify defaults like NEWID() by calling
            'ALTER TABLE after the table is created.
            e.Schema.Tables("OrderHeader").Columns("OrderId").RowGuid = True
        End If

        If tableName = "OrderDetail" Then
            'Add a foreign key  between the OrderHeader and OrderDetail tables.
            e.Schema.Tables("OrderDetail").ForeignKeys.Add("FK_OrderDetail_OrderHeader", "OrderHeader", "OrderId", "OrderDetail", "OrderId")
        End If


    End Sub 'SampleClientSyncProvider_CreatingSchema


    Private Sub SampleClientSyncProvider_SchemaCreated(ByVal sender As Object, ByVal e As SchemaCreatedEventArgs)
        Dim tableName As String = e.Table.TableName
        Dim util As New Utility()

        'Call ALTER TABLE on the client. This must be done
        'over the same connection and within the same
        'transaction that Sync Framework uses
        'to create the schema on the client.
        If tableName = "OrderHeader" Then
            Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, "OrderHeader")
        End If

        If tableName = "OrderDetail" Then
            Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, "OrderDetail")            
        End If

        Console.WriteLine("Schema created for " + 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 Uploaded: " & syncStatistics.TotalChangesUploaded)
        Console.WriteLine("Total Changes Downloaded: " & syncStatistics.TotalChangesDownloaded)
        Console.WriteLine("Complete Time: " & syncStatistics.SyncCompleteTime)
        Console.WriteLine(String.Empty)

    End Sub 'DisplayStats
End Class 'SampleStats

請參閱

概念

撰寫一般用戶端和伺服器同步處理工作