How to: Configure N-Tier Synchronization
This topic shows you how to configure N-tier synchronization for Sync Framework. The examples in this topic focus on the following Sync Framework types:
For information about how to run sample code, see "Example Applications in the How to Topics" in Programming Common Client and Server Synchronization Tasks.
Understanding N-Tier Synchronization
In an N-tier synchronization architecture, synchronization components are spread across multiple tiers, as shown in the following illustration.
In a two-tier architecture, all components reside on the client, and the client communicates directly with the server. In an N-tier architecture, the server synchronization provider resides on the server or another tier, and communication between the tiers is handled by the ServerSyncProviderProxy object and a service, such as a Windows Communication Foundation (WCF) service. One of the strengths of Sync Framework is the ease with which you can move code from a two-tier architecture to an N-tier architecture. If the code is factored appropriately, after you create a service and add a proxy, only a small change to synchronization agent code is required. Client and server synchronization providers do not have to be changed.
Example
The following code examples show the main components that are involved in an N-tier architecture. Additional WCF components are required. These components are generated for you if you use Visual Studio 2008. For more information, see the Visual Studio documentation.
Key Parts of the API
This section provides code examples that demonstrate the similarities between two-tier and N-tier synchronization, and point out the key parts of the API to use when you are configuring N-tier synchronization. The following code example is from a class that derives from SyncAgent. For two-tier synchronization, the client provider and server provider are both referenced directly in the synchronization agent as local and remote providers.
this.LocalProvider = new SampleClientSyncProvider();
this.RemoteProvider = new SampleServerSyncProvider();
Me.LocalProvider = New SampleClientSyncProvider()
Me.RemoteProvider = New SampleServerSyncProvider()
The following code example references the client provider directly, as in the two-tier example. However, the remote provider now references a proxy instead of directly referencing the server provider. The proxy is created by passing a reference to a WCF service.
this.LocalProvider = new SampleClientSyncProvider();
ServiceReference.ServiceForSyncClient serviceProxy = new ServiceReference.ServiceForSyncClient();
this.RemoteProvider = new ServerSyncProviderProxy(serviceProxy);
Me.LocalProvider = New SampleClientSyncProvider()
Dim serviceProxy As New ServiceReference.ServiceForSyncClient()
Me.RemoteProvider = New ServerSyncProviderProxy(serviceProxy)
The following code examples create client and server providers. This code is identical whether a two-tier or N-tier architecture is used.
public class SampleClientSyncProvider : SqlCeClientSyncProvider
{
public SampleClientSyncProvider()
{
//Specify a connection string for the sample client database.
Utility util = new Utility();
this.ConnectionString = util.ClientConnString;
}
}
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.
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 manually, or
//by using the SqlSyncAdapterBuilder as in the following
//code.
SqlSyncAdapterBuilder customerBuilder = new SqlSyncAdapterBuilder(serverConn);
customerBuilder.TableName = "Sales.Customer";
customerBuilder.TombstoneTableName = customerBuilder.TableName + "_Tombstone";
customerBuilder.SyncDirection = SyncDirection.DownloadOnly;
customerBuilder.CreationTrackingColumn = "InsertTimestamp";
customerBuilder.UpdateTrackingColumn = "UpdateTimestamp";
customerBuilder.DeletionTrackingColumn = "DeleteTimestamp";
SyncAdapter customerSyncAdapter = customerBuilder.ToSyncAdapter();
customerSyncAdapter.TableName = "Customer";
this.SyncAdapters.Add(customerSyncAdapter);
}
}
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
End Sub
End Class
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.
Dim selectNewAnchorCommand As New SqlCommand()
Dim newAnchorVariable As String = "@" + 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
Me.SelectNewAnchorCommand = selectNewAnchorCommand
'Create a SyncAdapter for the Customer table manually, or
'by using the SqlSyncAdapterBuilder as in the following
'code.
Dim customerBuilder As New SqlSyncAdapterBuilder(serverConn)
customerBuilder.TableName = "Sales.Customer"
customerBuilder.TombstoneTableName = customerBuilder.TableName + "_Tombstone"
customerBuilder.SyncDirection = SyncDirection.DownloadOnly
customerBuilder.CreationTrackingColumn = "InsertTimestamp"
customerBuilder.UpdateTrackingColumn = "UpdateTimestamp"
customerBuilder.DeletionTrackingColumn = "DeleteTimestamp"
Dim customerSyncAdapter As SyncAdapter = customerBuilder.ToSyncAdapter()
customerSyncAdapter.TableName = "Customer"
Me.SyncAdapters.Add(customerSyncAdapter)
End Sub
End Class
The following code example creates an interface that the service implements. The interface includes the four key methods of the server provider.
[ServiceContract]
public interface IServiceForSync
{
[OperationContract()]
SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession);
[OperationContract()]
SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession);
[OperationContract()]
SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession);
[OperationContract()]
SyncServerInfo GetServerInfo(SyncSession syncSession);
}
<ServiceContract()> _
Public Interface IServiceForSync
<OperationContract()> _
Function ApplyChanges(ByVal groupMetadata As SyncGroupMetadata, ByVal dataSet As DataSet, ByVal syncSession As SyncSession) As SyncContext
<OperationContract()> _
Function GetChanges(ByVal groupMetadata As SyncGroupMetadata, ByVal syncSession As SyncSession) As SyncContext
<OperationContract()> _
Function GetSchema(ByVal tableNames As Collection(Of String), ByVal syncSession As SyncSession) As SyncSchema
<OperationContract()> _
Function GetServerInfo(ByVal syncSession As SyncSession) As SyncServerInfo
End Interface
The following code example creates a service. This service implements the interface that was created in the previous code example and references the server provider.
public class ServiceForSync : IServiceForSync
{
private SampleServerSyncProvider _serverSyncProvider;
public ServiceForSync()
{
this._serverSyncProvider = new SampleServerSyncProvider();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession) {
return this._serverSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession) {
return this._serverSyncProvider.GetChanges(groupMetadata, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession) {
return this._serverSyncProvider.GetSchema(tableNames, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncServerInfo GetServerInfo(SyncSession syncSession) {
return this._serverSyncProvider.GetServerInfo(syncSession);
}
}
Public Class ServiceForSync
Implements IServiceForSync
Private _serverSyncProvider As SampleServerSyncProvider
Public Sub New()
Me._serverSyncProvider = New SampleServerSyncProvider()
End Sub
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Overridable Function ApplyChanges(ByVal groupMetadata As SyncGroupMetadata, ByVal dataSet As DataSet, ByVal syncSession As SyncSession) As SyncContext
Return Me._serverSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession)
End Function
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Overridable Function GetChanges(ByVal groupMetadata As SyncGroupMetadata, ByVal syncSession As SyncSession) As SyncContext
Return Me._serverSyncProvider.GetChanges(groupMetadata, syncSession)
End Function
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Overridable Function GetSchema(ByVal tableNames As Collection(Of String), ByVal syncSession As SyncSession) As SyncSchema
Return Me._serverSyncProvider.GetSchema(tableNames, syncSession)
End Function
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Overridable Function GetServerInfo(ByVal syncSession As SyncSession) As SyncServerInfo
Return Me._serverSyncProvider.GetServerInfo(syncSession)
End Function
End Class