As a follow up post to Part 1 – Upload Synchronization where the Client and Server Primary Keys are different, let’s extend the code from the previous post to enable Bidirectional synchronization where the client and server table has different PKs.
Let’s extend the scenario so that we now want to download changes to the rows belonging to a particular client. In the last post, we identified each client with a GUID ClientId. We’ll use the same ClientId to filter the download.
Here’s the revised code for the server side provisioning:
- //setup the server connection
- var serverConn = new SqlConnection("Data Source=localhost; Initial Catalog=Aggregator; Integrated Security=True");
- // define a new scope named TestScope
- var scopeDesc = new DbSyncScopeDescription("TestScope");
- // get the description of the TestTable table from the server database
- var tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("TestTable", serverConn);
- // add the table description to the sync scope definition
- scopeDesc.Tables.Add(tableDesc);
- // create provisioning scope based on the scope definition
- var serverProvision = new SqlSyncScopeProvisioning(serverConn, scopeDesc);
- // set the filter column on the Orders table to OriginState
- serverProvision.Tables["TestTable"].AddFilterColumn("ClientId");
- // set the filter value to NC
- serverProvision.Tables["TestTable"].FilterClause = "[side].[ClientId] = '89198182-24C9-4CD7-8427-F6790270E62C'";
- if (!serverProvision.ScopeExists("TestScope"))
- {
- // skipping the creation of table since table already exists on server
- serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
- //apply the scope definition
- serverProvision.Apply();
- }
Notice how we specified a static filter on the ClientId column to the same value we set during the upload.
We’ll change the SyncDirection to UploadDownload to enable the bidirectional sync. We then subscribe to the ChangesSelected event on the server side this time to remove the ClientId column before the dataset reaches the client.
- // create the sync orchestrator
- var syncOrchestrator = new SyncOrchestrator();
- // set local provider of orchestrator
- syncOrchestrator.LocalProvider = new SqlSyncProvider("TestScope", clientConn);
- // set the remote provider of orchestrator
- syncOrchestrator.RemoteProvider = new SqlSyncProvider("TestScope", serverConn);
- // set the direction of sync session to Upload and Download
- syncOrchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
- // subscribe for errors that occur when applying changes to the client
- ((SqlSyncProvider)syncOrchestrator.LocalProvider).ChangesSelected += ClientChangesSelected;
- ((SqlSyncProvider)syncOrchestrator.RemoteProvider).ChangesSelected += ServerChangesSelected;
- // execute the synchronization process
- var syncStats = syncOrchestrator.Synchronize();
- // print statistics
- Console.WriteLine("Start Time: " + syncStats.SyncStartTime);
- Console.WriteLine("Total Changes Uploaded: " + syncStats.UploadChangesTotal);
- Console.WriteLine("Total Changes Downloaded: " + syncStats.DownloadChangesTotal);
- Console.WriteLine("Complete Time: " + syncStats.SyncEndTime);
- Console.WriteLine(String.Empty);
- Console.ReadKey();
The sync knowledge on the client side knows only the Id column as the PK of the client table, so in order to check if it has the rows coming from the server, the comparison should only be made on the Id column.
So let’s take out the ClientId column:
- static void ServerChangesSelected(object sender, DbChangesSelectedEventArgs e)
- {
- //let's check if we're synching the table we're interested
- if (e.Context.DataSet.Tables.Contains("testtable"))
- {
- var columns = e.Context.DataSet.Tables["testtable"].Columns;
- if (columns.Contains("ClientId"))
- {
- //let's remove the new column
- columns.Remove("ClientId");
- }
- }
- }
There you go, the client should now be able to receive changes from the server side for rows whose ClientId is equal to its ClientId.
Again, I’d love to hear any feedback if this works for you as well.
The problem with this solution is that each server record is tied to only one client. This creates a business rule whereby Client A could only ever sync to Client A records and Client B could only ever sync Client B records … without this rule, there would be a conflict whenever multiple Client records with the same ID value were retrieved by the filter.
in this scenario, the client records have duplicate/overlapping IDs already even before they’re sync. If each client needs to have both Client A and Client B records, then they should not have been designed to have overlapping keys in the first place. And as mentioned, this is for scenarios where the schema cant be changed on the client and data needs to be aggregated/consolidated on the server.
[…] Part 1 – Upload Synchronization where the Client and Server Primary Keys are different and Part 2 – Bidirectional Synchronization where the Client and Server Primary Keys are different ) and in both approach, we relied on intercepting the change dataset to alter the structure of the […]
Is it possible to use a column other than the primary key to sync on?
Scenario:
Server has a table Customers, it has an int Id primary key and a UID guid column
Client has a int Id primary key, and an UID guid column
The UID is not part of the key, but when syncing it is the value I want to use to determine if one row is equal to another across server and client, on insert on both sides I am happy with auto increment values for the Id’s as I will not be syncing on them.
Any suggestions?
that should be possible, see this post: https://jtabadero.wordpress.com/2011/07/20/part-3-synchronizing-tables-where-client-and-server-primary-keys-are-different/
this is nice concept but this example is for only one table but i want to do it with like 100 tables..what is the best way to achieve that..?