I remember having answered several times how to sync tables with different schemas and I actually thought I have a blog post on it. The other day, the same question popped up again in the forums :Can we sync two database with different schemas and different tables using Sync Framework
I tried to answer the question by posting links to my previous blog posts related to the question and was surprised to find out I haven’t previously blogged about synchronizing tables with different table and column names. I couldn’t find the sample codes I wrote either.
So, here you go.
Assuming we have the following tables with different table names and column names.
Here’s the complete code for synchronizing the above tables.
- class Program
- {
- static void Main()
- {
- //setup the connections
- var serverConn = new SqlConnection("Data Source=localhost; Initial Catalog=SyncFxLabs; Integrated Security=True");
- var clientConn = new SqlConnection("Data Source=localhost; Initial Catalog=SyncFxClient; Integrated Security=True");
- //setup scope name
- const string scopeName = "DifferentSchemaScope";
- //provision server
- var serverProvision = new SqlSyncScopeProvisioning(serverConn);
- if (!serverProvision.ScopeExists(scopeName))
- {
- var serverScopeDesc = new DbSyncScopeDescription(scopeName);
- // add the server table
- var serverTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("SourceOrderTable", serverConn);
- // our server and client tables has different names, so let’s setup a common name
- serverTableDesc.GlobalName = "OrderTable";
- serverScopeDesc.Tables.Add(serverTableDesc);
- serverProvision.PopulateFromScopeDescription(serverScopeDesc);
- //apply the scope definition
- serverProvision.Apply();
- }
- //provision client
- var clientProvision = new SqlSyncScopeProvisioning(clientConn);
- if (!clientProvision.ScopeExists(scopeName))
- {
- var clientScopeDesc = new DbSyncScopeDescription(scopeName);
- // add the client table
- var clientTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("DestinationOrderTable", clientConn);
- clientTableDesc.GlobalName = "OrderTable";
- clientScopeDesc.Tables.Add(clientTableDesc);
- // remove the OrderQty column since the Source table doesnt have it
- clientScopeDesc.Tables["OrderTable"].Columns.Remove(clientScopeDesc.Tables["OrderTable"].Columns["OrderQty"]);
- clientProvision.PopulateFromScopeDescription(clientScopeDesc);
- //apply the scope definition
- clientProvision.Apply();
- }
- // create the sync orchestrator
- var syncOrchestrator = new SyncOrchestrator();
- //setup providers
- var localProvider = new SqlSyncProvider(scopeName, clientConn);
- var remoteProvider = new SqlSyncProvider(scopeName, serverConn);
- // lets intercept the changes so we can rename the columns
- remoteProvider.ChangesSelected += new EventHandler<DbChangesSelectedEventArgs>(remoteProvider_ChangesSelected);
- syncOrchestrator.LocalProvider = localProvider;
- syncOrchestrator.RemoteProvider = remoteProvider;
- // set the direction of sync session Download
- syncOrchestrator.Direction = SyncDirectionOrder.Download;
- // execute the synchronization process
- var syncStats = syncOrchestrator.Synchronize();
- // print statistics
- Console.WriteLine("Start Time: " + syncStats.SyncStartTime);
- Console.WriteLine("Total Changes Downloaded: " + syncStats.DownloadChangesTotal);
- Console.WriteLine("Complete Time: " + syncStats.SyncEndTime);
- Console.WriteLine(String.Empty);
- Console.ReadKey();
- }
- static void remoteProvider_ChangesSelected(object sender, DbChangesSelectedEventArgs e)
- {
- //let’s check if we’re synching the table we’re interested
- if (e.Context.DataSet.Tables.Contains("OrderTable"))
- {
- var dataTable = e.Context.DataSet.Tables["OrderTable"];
- //rename the columns to match the destination table’s column names
- dataTable.Columns["OrderId"].ColumnName = "OrderNo";
- dataTable.Columns["OrderDesc"].ColumnName = "OrderDetail";
- }
- }
- }
Both tables are added to the scope using standard provisioning code. Since we have different table names, we assign a common name to the tables using the GlobalName property (see: Synching tables with different table names)
Unfortunately, there is no such property for column names. So we have to let Sync Framework provision the tables as it is and intercept the changes so we can map the column names to the target table before the changes are applied.
We do this by subscribing to the ChangesSelected event, inspecting the dataset for the changes that was selected, then accessing the DataTable and changing the column names to match the destination table’s column names and let the dataset flow thru to the destination.
So there you go, we’re now able to sync tables with different names and column names. You can do other transformations as well using the same approach such as combining multiple columns into a single column (e.g., Firstname + Lastname mapped to Fullname) or even mapping different data types. I’ll try to post samples for the latter scenarios on subsequent posts.
Hope you find this useful.
Cheers,
JuneT
what if the destination is mysql and not sql? your code works great if both the sources are sql… is it easy to extend this code to interop with mysql?
there’s on out-of-the-box sync provider for mySql so you will have to write your own custom sync provider.
it is not working :
//rename the columns to match the destination table’s column names
dataTable.Columns[“OrderId”].ColumnName = “OrderNo”;
dataTable.Columns[“OrderDesc”].ColumnName = “OrderDetail”;
datatable doesnt include orderid and orderdesc. it has got OrderNo and OrderDetail
NullReferenceException : Object reference not set to an instance of an object.
are you sure you’re doing it right? without the code or the exact stack trace, hard to tell what’s going on
[…] https://jtabadero.wordpress.com/2011/08/19/part-4-synchronizing-tables-with-different-table-names-and… http://www.devart.com/dotconnect/oracle/docs/SyncFramework.html But i have a Nullreference exception. OrderTable havent got correct columns. Look pics. How to solve this problem. […]
How can i post my codes?
You can download here my code what is wrong?
https://app.box.com/s/ssm0x1ufsylwyj2vu23f
My Sql below:
id should be primary key….
CREATE SCHEMA myTest
CREATE TABLE [myTest].[SourceOrderTable](
[OrderId] [int] IDENTITY(1,1) NOT NULL,
[OrderDesc] [nvarchar](50) NULL)
CREATE TABLE [myTest].[DestinationOrderTable](
[OrderNo] [int] IDENTITY(1,1) NOT NULL,
[OrderDetail] [nvarchar](50) NULL,
[OrderQty] int NULL)
i have already replied to the same on StackOverflow
Did you download my code?
[…] Synchronizing Tables With Different Table Names and Column Names: https://jtabadero.wordpress.com/2011/08/19/part-4-synchronizing-tables-with-different-table-names-and… […]
Have you written a blog on the different datatypes yet? My scenario is my local SQL Server has int PK values but my Azure DB uses GUIDs for identity columns. My plan is to use what you show in blog post…
so that the primary key issue is resolved. The next problem I have is with foreign keys in other tables. They are converted from ints to GUIDs as well and I would need to use a SQL query to transform the ID to link it to the correct GUID.
the article you referred to is not converting PKs from one data type to another. it’s more of “tricking” Sync Fx to use another column as a PK. While you can use the same trick of intercepting and manipulating the change dataset before they can applied, i wouldn’t normally recommend the same approach where the type of manipulation done is more on transforming the data that is best done by other tools.
Failed to execute the command ‘BulkInsertCommand’ for table ‘OrderTable’; the transaction was rolled back. Ensure that the command syntax is correct.