Freigeben über


Vorgehensweise: Bidirektionaler Austausch inkrementeller Datenänderungen zwischen Client und Server

In diesem Thema wird erläutert, wie Sie Datenänderungen durch eine bidirektionale Synchronisierung zwischen einer Serverdatenbank und einer SQL Server Compact-Clientdatenbank austauschen. Die Beispiele in diesem Thema beziehen sich auf die folgenden Sync Framework-Typen und -Ereignisse:

Informationen zum Ausführen von Beispielcode finden Sie unter "Beispielanwendungen in den Themen zur Vorgehensweise" in Programmieren von allgemeinen Client- und Serversynchronisierungsaufgaben.

Beispiel

Der Beispielcode in diesem Thema zeigt, wie Sie die bidirektionale Synchronisierung für die Customer-Tabelle aus der Sync Framework-Beispieldatenbank konfigurieren können. Eine Übersicht über die Eigenschaften, die für die Angabe der Synchronisierungsrichtung verwendet werden, finden Sie unter Vorgehensweise: Angeben der Synchronisierungsart (Momentaufnahmen-, Nur-Download-, Nur-Upload- oder bidirektionale Synchronisierung).

Für die bidirektionale Synchronisierung ist es erforderlich, dass die inkrementellen Datenänderungen in der Server- und in der Clientdatenbank nachverfolgt und übernommen werden. Die Clientdatenbank wird von Sync Framework gesteuert. Ihre Aufgabe ist es, die Serverdatenbank zu konfigurieren und die Befehle anzugeben, mit denen Änderungen in der Serverdatenbank ausgewählt und Änderungen für die Serverdatenbank übernommen werden. Diese Befehle sind im Beispielcode enthalten. Informationen zum Konfigurieren der Serverdatenbank finden Sie unter Änderungsnachverfolgung in der Serverdatenbank.

Schlüsselbestandteile der API

In diesem Abschnitt finden Sie Codebeispiele für die Schlüsselbestandteile der API, die für die bidirektionale Synchronisierung verwendet werden. Das folgende Codebeispiel gibt die Synchronisierungsrichtung an und legt fest, wie die Tabelle auf dem Client erstellt werden soll.

SyncTable customerSyncTable = new SyncTable("Customer");
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
customerSyncTable.SyncDirection = SyncDirection.Bidirectional;
customerSyncTable.SyncGroup = customerSyncGroup;
this.Configuration.SyncTables.Add(customerSyncTable);
Dim customerSyncTable As New SyncTable("Customer")
customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
customerSyncTable.SyncDirection = SyncDirection.Bidirectional
customerSyncTable.SyncGroup = customerSyncGroup
Me.Configuration.SyncTables.Add(customerSyncTable)

Das folgende Codebeispiel gibt einen Befehl zum Abrufen eines neuen Ankerwerts vom Server an. In diesem Fall wird ein Timestampwert abgerufen und in der Clientdatenbank gespeichert. Bei jeder Synchronisierung werden der neue Ankerwert und der letzte Ankerwert aus der vorherigen Synchronisierung verwendet. Alle Änderungen, die zwischen diesen beiden Grenzwerten erfolgt sind, werden synchronisiert. Die Befehle, die zum Auswählen der Änderungen aus der Serverdatenbank verwendet werden, enthalten entsprechende Ankerparameter.

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

Das folgende Codebeispiel gibt einen Befehl an, mit dem die inkrementellen Aktualisierungen auf dem Server ausgewählt werden, um sie dann für den Client zu übernehmen. Dies ist eine SELECT-Anweisung, die die Spalten einschließt, die synchronisiert werden müssen. Das Beispiel enthält eine WHERE-Klausel, die Spalten zur Nachverfolgung und die Ankerwerte verwendet, die der SelectNewAnchorCommand abruft. Die WHERE-Klausel enthält auch den UpdateId <> @sync_client_id-Parameter. Dieser Parameter verhindert, dass hochgeladene Änderungen während der gleichen Synchronisierung sofort wieder zurück zum Client heruntergeladen werden. Wenn die Anwendung verlangt, dass Änderungen als Echo an den Client zurückgegeben werden, können Sie diese Anweisung weglassen. Informationen dazu, wie Sie Daten zum Herunterladen filtern können, finden Sie unter Vorgehensweise: Filtern von Zeilen und Spalten.

SqlCommand customerIncrUpdates = new SqlCommand();
customerIncrUpdates.CommandText =
    "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " +
    "FROM Sales.Customer " +
    "WHERE (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("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerIncrUpdates.Connection = serverConn;
customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates;
Dim customerIncrUpdates As New SqlCommand()
With customerIncrUpdates
    .CommandText = _
        "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " _
      & "FROM Sales.Customer " _
      & "WHERE (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("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
    .Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
    .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
    .Connection = serverConn
End With
customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates

Das folgende Codebeispiel gibt einen Befehl an, mit dem die Aktualisierungen vom Client auf den Server übernommen werden. Dabei handelt es sich um eine UPDATE-Anweisung, die die Spalten einschließt, die aktualisiert werden müssen. Das Beispiel enthält eine WHERE-Klausel, die ebenfalls die Ankerwerte und die Client-ID verwendet. Die WHERE-Klausel enthält auch den @sync_force_write = 1-Parameter. Dieser Parameter erzwingt die Übernahme sich widersprechender Aktualisierungen. Weitere Informationen zu @sync_force_write und den anderen Sitzungsvariablen finden Sie unter Vorgehensweise: Verwenden von Sitzungsvariablen.

SqlCommand customerUpdates = new SqlCommand();
customerUpdates.CommandText =
    "UPDATE Sales.Customer SET " + 
    "CustomerName = @CustomerName, SalesPerson = @SalesPerson, " + 
    "CustomerType = @CustomerType, " + 
    "UpdateId = @sync_client_id " +           
    "WHERE (CustomerId = @CustomerId) " + 
    "AND (@sync_force_write = 1 " + 
    "OR (UpdateTimestamp <= @sync_last_received_anchor " +
    "OR UpdateId = @sync_client_id)) " + 
    "SET @sync_row_count = @@rowcount";
customerUpdates.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
customerUpdates.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
customerUpdates.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
customerUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
customerUpdates.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
customerUpdates.Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit);
customerUpdates.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
customerUpdates.Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int);
customerUpdates.Connection = serverConn;
customerSyncAdapter.UpdateCommand = customerUpdates;
Dim customerUpdates As New SqlCommand()
With customerUpdates
    .CommandText = _
        "UPDATE Sales.Customer SET " _
      & "CustomerName = @CustomerName, SalesPerson = @SalesPerson, " _
      & "CustomerType = @CustomerType, " _
      & "UpdateId = @sync_client_id " _
      & "WHERE (CustomerId = @CustomerId) " _
      & "AND (@sync_force_write = 1 " _
      & "OR (UpdateTimestamp <= @sync_last_received_anchor " _
      & "OR UpdateId = @sync_client_id)) " _
      & "SET @sync_row_count = @@rowcount"
    .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
    .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
    .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
    .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
    .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
    .Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit)
    .Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
    .Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int)
    .Connection = serverConn
End With
customerSyncAdapter.UpdateCommand = customerUpdates

Vollständiges Codebeispiel

Das folgende vollständige Codebeispiel enthält die Codebeispiele, die weiter oben beschrieben wurden, sowie zusätzlichen Code zum Ausführen der Synchronisierung. Achten Sie auch darauf, wie mithilfe von SyncStatistics Informationen zur Synchronisierungssitzung angezeigt werden und wie die Ereignisse CreatingSchema und SchemaCreated zum Hinzufügen eines Standards für die CustomerId-Spalte genutzt werden. Für das Beispiel wird die in 'Utility'-Klasse für Datenbankanbieter - Themen zur Vorgehensweise enthaltene Utility-Klasse benötigt.

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 and client.
            Utility.MakeDataChangesOnServer("Customer");
            Utility.MakeDataChangesOnClient("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 a Customer SyncGroup. This is not required
            //for the single table we are synchronizing; it is typically
            //used so that changes to multiple related tables are 
            //synchronized at the same time.
            SyncGroup customerSyncGroup = new SyncGroup("Customer");

            //Add the Customer table: specify a synchronization direction of
            //Bidirectional, and that an existing table should be dropped.
            SyncTable customerSyncTable = new SyncTable("Customer");
            customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
            customerSyncTable.SyncDirection = SyncDirection.Bidirectional;
            customerSyncTable.SyncGroup = customerSyncGroup;
            this.Configuration.SyncTables.Add(customerSyncTable);
        }
    }

    //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 the Customer 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.
            //* InsertCommand, UpdateCommand, and DeleteCommand are used to apply
            //  to the server the changes that the client provider has selected
            //  from the client.

            //Create the SyncAdapter.
            SyncAdapter customerSyncAdapter = new SyncAdapter("Customer");            
            
            //Select inserts from the server.
            SqlCommand customerIncrInserts = new SqlCommand();
            customerIncrInserts.CommandText =  
                "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " +
                "FROM Sales.Customer " +
                "WHERE (InsertTimestamp > @sync_last_received_anchor " +
                "AND InsertTimestamp <= @sync_new_received_anchor " +
                "AND InsertId <> @sync_client_id)";
            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;

            //Apply inserts to the server.
            SqlCommand customerInserts = new SqlCommand();
            customerInserts.CommandText =
                "INSERT INTO Sales.Customer (CustomerId, CustomerName, SalesPerson, CustomerType, InsertId, UpdateId) " +
                "VALUES (@CustomerId, @CustomerName, @SalesPerson, @CustomerType, @sync_client_id, @sync_client_id) " +
                "SET @sync_row_count = @@rowcount";
            customerInserts.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            customerInserts.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            customerInserts.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            customerInserts.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            customerInserts.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
            customerInserts.Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int);
            customerInserts.Connection = serverConn;
            customerSyncAdapter.InsertCommand = customerInserts;
                                    
            
            //Select updates from the server.
            SqlCommand customerIncrUpdates = new SqlCommand();
            customerIncrUpdates.CommandText =
                "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " +
                "FROM Sales.Customer " +
                "WHERE (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("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
            customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp);
            customerIncrUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
            customerIncrUpdates.Connection = serverConn;
            customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates;
            
            //Apply updates to the server.
            SqlCommand customerUpdates = new SqlCommand();
            customerUpdates.CommandText =
                "UPDATE Sales.Customer SET " + 
                "CustomerName = @CustomerName, SalesPerson = @SalesPerson, " + 
                "CustomerType = @CustomerType, " + 
                "UpdateId = @sync_client_id " +           
                "WHERE (CustomerId = @CustomerId) " + 
                "AND (@sync_force_write = 1 " + 
                "OR (UpdateTimestamp <= @sync_last_received_anchor " +
                "OR UpdateId = @sync_client_id)) " + 
                "SET @sync_row_count = @@rowcount";
            customerUpdates.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            customerUpdates.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            customerUpdates.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            customerUpdates.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
            customerUpdates.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            customerUpdates.Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit);
            customerUpdates.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
            customerUpdates.Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int);
            customerUpdates.Connection = serverConn;
            customerSyncAdapter.UpdateCommand = customerUpdates;


            //Select deletes from the server.
            SqlCommand customerIncrDeletes = new SqlCommand();
            customerIncrDeletes.CommandText =
                "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " +
                "FROM Sales.Customer_Tombstone " +
                "WHERE (@sync_initialized = 1 " +
                "AND DeleteTimestamp > @sync_last_received_anchor " +
                "AND DeleteTimestamp <= @sync_new_received_anchor " +
                "AND DeleteId <> @sync_client_id)";
            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;

            //Apply deletes to the server.            
            SqlCommand customerDeletes = new SqlCommand();
            customerDeletes.CommandText =
                "DELETE FROM Sales.Customer " +
                "WHERE (CustomerId = @CustomerId) " + 
                "AND (@sync_force_write = 1 " + 
                "OR (UpdateTimestamp <= @sync_last_received_anchor " + 
                "OR UpdateId = @sync_client_id)) " + 
                "SET @sync_row_count = @@rowcount " + 
                "IF (@sync_row_count > 0)  BEGIN " + 
                "UPDATE Sales.Customer_Tombstone " + 
                "SET DeleteId = @sync_client_id " +
                "WHERE (CustomerId = @CustomerId) " + 
                "END";
            customerDeletes.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);       
            customerDeletes.Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit);
            customerDeletes.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp);
            customerDeletes.Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier);
            customerDeletes.Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int);           
            customerDeletes.Connection = serverConn;
            customerSyncAdapter.DeleteCommand = customerDeletes;     


            //Add the SyncAdapter to the server synchronization provider.
            this.SyncAdapters.Add(customerSyncAdapter);

        }
    }

    //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.
            this.CreatingSchema +=new EventHandler<CreatingSchemaEventArgs>(SampleClientSyncProvider_CreatingSchema);
            this.SchemaCreated +=new EventHandler<SchemaCreatedEventArgs>(SampleClientSyncProvider_SchemaCreated);
        }

        private void SampleClientSyncProvider_CreatingSchema(object sender, CreatingSchemaEventArgs e)
        {
            //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.
            Console.Write("Creating schema for " + e.Table.TableName + " | ");                        
            e.Schema.Tables["Customer"].Columns["CustomerId"].RowGuid = true;
        }

        private void SampleClientSyncProvider_SchemaCreated(object sender, SchemaCreatedEventArgs e)
        {        
            //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.
            Utility util = new Utility();
            Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, e.Table.TableName);
            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 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)

        '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 server data back to its original state.
        Utility.CleanUpServer()

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

    End Sub 'Main
End Class 'Program

'Create a class that is derived from 
'Microsoft.Synchronization.SyncAgent.
Public Class SampleSyncAgent
    Inherits SyncAgent

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

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

        'Create a Customer SyncGroup. This is not required
        'for the single table we are synchronizing; it is typically
        'used so that changes to multiple related tables are 
        'synchronized at the same time.
        Dim customerSyncGroup As New SyncGroup("Customer")

        'Add the Customer table: specify a synchronization direction of
        'Bidirectional, and that an existing table should be dropped.
        Dim customerSyncTable As New SyncTable("Customer")
        customerSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable
        customerSyncTable.SyncDirection = SyncDirection.Bidirectional
        customerSyncTable.SyncGroup = customerSyncGroup
        Me.Configuration.SyncTables.Add(customerSyncTable)

    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 the Customer 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.
        '* InsertCommand, UpdateCommand, and DeleteCommand are used to apply
        '  to the server the changes that the client provider has selected
        '  from the client.

        '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, SalesPerson, CustomerType " _
              & "FROM Sales.Customer " _
              & "WHERE (InsertTimestamp > @sync_last_received_anchor " _
              & "AND InsertTimestamp <= @sync_new_received_anchor " _
              & "AND InsertId <> @sync_client_id)"
            .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

        'Apply inserts to the server.
        Dim customerInserts As New SqlCommand()
        With customerInserts
            .CommandText = _
                "INSERT INTO Sales.Customer (CustomerId, CustomerName, SalesPerson, CustomerType, InsertId, UpdateId) " _
              & "VALUES (@CustomerId, @CustomerName, @SalesPerson, @CustomerType, @sync_client_id, @sync_client_id) " _
              & "SET @sync_row_count = @@rowcount"
            .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
            .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
            .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
            .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
            .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
            .Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int)
            .Connection = serverConn
        End With
        customerSyncAdapter.InsertCommand = customerInserts


        'Select updates from the server.
        Dim customerIncrUpdates As New SqlCommand()
        With customerIncrUpdates
            .CommandText = _
                "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " _
              & "FROM Sales.Customer " _
              & "WHERE (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("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
            .Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp)
            .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
            .Connection = serverConn
        End With
        customerSyncAdapter.SelectIncrementalUpdatesCommand = customerIncrUpdates

        'Apply updates to the server.
        Dim customerUpdates As New SqlCommand()
        With customerUpdates
            .CommandText = _
                "UPDATE Sales.Customer SET " _
              & "CustomerName = @CustomerName, SalesPerson = @SalesPerson, " _
              & "CustomerType = @CustomerType, " _
              & "UpdateId = @sync_client_id " _
              & "WHERE (CustomerId = @CustomerId) " _
              & "AND (@sync_force_write = 1 " _
              & "OR (UpdateTimestamp <= @sync_last_received_anchor " _
              & "OR UpdateId = @sync_client_id)) " _
              & "SET @sync_row_count = @@rowcount"
            .Parameters.Add("@CustomerName", SqlDbType.NVarChar)
            .Parameters.Add("@SalesPerson", SqlDbType.NVarChar)
            .Parameters.Add("@CustomerType", SqlDbType.NVarChar)
            .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
            .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
            .Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit)
            .Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
            .Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int)
            .Connection = serverConn
        End With
        customerSyncAdapter.UpdateCommand = customerUpdates

        'Select deletes from the server.
        Dim customerIncrDeletes As New SqlCommand()
        With customerIncrDeletes
            .CommandText = _
                "SELECT CustomerId, CustomerName, SalesPerson, CustomerType " _
              & "FROM Sales.Customer_Tombstone " _
              & "WHERE (@sync_initialized = 1 " _
              & "AND DeleteTimestamp > @sync_last_received_anchor " _
              & "AND DeleteTimestamp <= @sync_new_received_anchor " _
              & "AND DeleteId <> @sync_client_id)"
            .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

        'Apply deletes to the server.            
        Dim customerDeletes As New SqlCommand()
        With customerDeletes
            .CommandText = _
                "DELETE FROM Sales.Customer " _
              & "WHERE (CustomerId = @CustomerId) " _
              & "AND (@sync_force_write = 1 " _
              & "OR (UpdateTimestamp <= @sync_last_received_anchor " _
              & "OR UpdateId = @sync_client_id)) " _
              & "SET @sync_row_count = @@rowcount " _
              & "IF (@sync_row_count > 0)  BEGIN " _
              & "UPDATE Sales.Customer_Tombstone " _
              & "SET DeleteId = @sync_client_id " _
              & "WHERE (CustomerId = @CustomerId) " _
              & "END"
            .Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier)
            .Parameters.Add("@" + SyncSession.SyncForceWrite, SqlDbType.Bit)
            .Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.Timestamp)
            .Parameters.Add("@" + SyncSession.SyncClientId, SqlDbType.UniqueIdentifier)
            .Parameters.Add("@" + SyncSession.SyncRowCount, SqlDbType.Int)
            .Connection = serverConn
        End With
        customerSyncAdapter.DeleteCommand = customerDeletes


        'Add the SyncAdapter to the server synchronization provider.
        Me.SyncAdapters.Add(customerSyncAdapter)

    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.
        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)
        '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.
        Console.Write("Creating schema for " + e.Table.TableName + " | ")
        e.Schema.Tables("Customer").Columns("CustomerId").RowGuid = True

    End Sub 'SampleClientSyncProvider_CreatingSchema


    Private Sub SampleClientSyncProvider_SchemaCreated(ByVal sender As Object, ByVal e As SchemaCreatedEventArgs)
        '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.
        Dim util As New Utility()
        Utility.MakeSchemaChangesOnClient(e.Connection, e.Transaction, e.Table.TableName)
        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 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

Siehe auch

Konzepte

Architektur und Klassen für die Client- und Serversynchronisierung
Vorgehensweise: Angeben der Synchronisierungsart (Momentaufnahmen-, Nur-Download-, Nur-Upload- oder bidirektionale Synchronisierung)