using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.Synchronization.Data.Server; using Microsoft.Synchronization.Data; using System.Data.SqlClient; using Microsoft.Samples.Synchronization.Data.SqlExpress; using System.Data; namespace Ebit.Crm8bitModule { public partial class ItemSyncAgentSample : Microsoft.Synchronization.SyncAgent { public ItemSyncAgentSample() { } public event EventHandler SyncProgress; public event EventHandler ClientApplyChangeFailed; public event EventHandler ServerApplyChangeFailed; protected SqlConnection serverConnection = null; protected SqlConnection clientConnection = null; private string _clientConnectionString; public string ClientConnectionString { get { return _clientConnectionString; } set { _clientConnectionString = value; TryInitialize(); } } private string _serverConnectionString; public string ServerConnectionString { get { return _serverConnectionString; } set { _serverConnectionString = value; TryInitialize(); } } protected DbServerSyncProvider serverSyncProvider; protected SqlExpressClientSyncProvider clientSyncProvider; // this method hides the base Synchronize method: this is necessary because the providers // open database connections as soon as they are set. And we need them to be opened // immediately before sync. public new void Synchronize() { serverConnection = new SqlConnection(ServerConnectionString); serverSyncProvider.Connection = serverConnection; // note: the connection will be immediately opened by the provider clientConnection = new SqlConnection(ClientConnectionString); clientSyncProvider.Connection = clientConnection; // note: the connection will be immediately opened by the provider base.Synchronize(); } void clientSyncProvider_ApplyChangeFailed(object sender, ApplyChangeFailedEventArgs e) { if (ClientApplyChangeFailed != null) { ClientApplyChangeFailed(sender, e); } } void serverSyncProvider_ApplyChangeFailed(object sender, ApplyChangeFailedEventArgs e) { if (ServerApplyChangeFailed != null) { ServerApplyChangeFailed(sender, e); } } void serverSyncProvider_SyncProgress(object sender, SyncProgressEventArgs e) { if (SyncProgress != null) { SyncProgress(this, e); } } protected void TryInitialize() { if (string.IsNullOrEmpty(ClientConnectionString) || string.IsNullOrEmpty(ServerConnectionString)) { return; } Initialize(); } protected void Initialize() { serverSyncProvider = new DbServerSyncProvider(); clientSyncProvider = new SqlExpressClientSyncProvider(); serverSyncProvider.SyncProgress += new EventHandler(serverSyncProvider_SyncProgress); serverSyncProvider.ApplyChangeFailed += new EventHandler(serverSyncProvider_ApplyChangeFailed); clientSyncProvider.ApplyChangeFailed += new EventHandler(clientSyncProvider_ApplyChangeFailed); this.LocalProvider = clientSyncProvider; this.RemoteProvider = serverSyncProvider; // TODO: detect if the provider can run under full privileges SetupSyncProvider(false, serverSyncProvider, true); SetupSyncProvider(true, clientSyncProvider, false); InitializeSyncTables(); return; } /// /// /// /// True if the command is to be executed under full /// privileges /// protected static SqlCommand CreateSelectNewAnchorCommand() { // select new anchor command SqlCommand selectNewAnchorCmd = new SqlCommand(); selectNewAnchorCmd.CommandType = System.Data.CommandType.Text; selectNewAnchorCmd.CommandText = "SELECT @" + SyncSession.SyncNewReceivedAnchor + " = GETUTCDATE(); "; selectNewAnchorCmd.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, System.Data.SqlDbType.DateTime).Direction = ParameterDirection.Output; return selectNewAnchorCmd; } public static void SetupSyncProvider(bool isClient, Microsoft.Synchronization.SyncProvider syncProvider, bool fullPrivilege) { if (syncProvider == null) { throw new ArgumentNullException("syncProvider"); } SyncAdapter itemAdapter = new ItemSyncAdapter(); SyncAdapter item2ndPassAdapter = new Item2ndPassSyncAdapter(); // because of crap microsoft architecture, we cannot access the SyncAdapters collection in a unified way if (syncProvider is DbServerSyncProvider) { // add all first-pass providers here ((DbServerSyncProvider)syncProvider).SyncAdapters.Add(itemAdapter); // add all second-pass providers here ((DbServerSyncProvider)syncProvider).SyncAdapters.Add(item2ndPassAdapter); ((DbServerSyncProvider)syncProvider).SelectNewAnchorCommand = CreateSelectNewAnchorCommand(); } else if (syncProvider is SqlExpressClientSyncProvider) { // add all first-pass providers here ((SqlExpressClientSyncProvider)syncProvider).SyncAdapters.Add(itemAdapter); // add all second-pass providers here ((SqlExpressClientSyncProvider)syncProvider).SyncAdapters.Add(item2ndPassAdapter); ((SqlExpressClientSyncProvider)syncProvider).SelectNewAnchorCommand = CreateSelectNewAnchorCommand(); } else { throw new InvalidOperationException("Unknown SyncProvider type: " + syncProvider.GetType()); } } private Microsoft.Synchronization.Data.SyncGroup _globalSyncGroup; public Microsoft.Synchronization.Data.SyncGroup GlobalSyncGroup { get { return _globalSyncGroup; } } private ItemSyncTable _itemTable; public ItemSyncTable ItemTable { get { return _itemTable; } } private Item2ndPassSyncTable _item2ndPassTable; public Item2ndPassSyncTable Item2ndPassTable { get { return _item2ndPassTable; } } private void InitializeSyncTables() { // Create a global SyncGroup. this._globalSyncGroup = new Microsoft.Synchronization.Data.SyncGroup("ItemSyncAgentGlobalSyncGroup"); // Create SyncTables. this._itemTable = new ItemSyncTable(); this._itemTable.SyncGroup = _globalSyncGroup; this.Configuration.SyncTables.Add(this._itemTable); this._item2ndPassTable = new Item2ndPassSyncTable(); this._item2ndPassTable.SyncGroup = _globalSyncGroup; this.Configuration.SyncTables.Add(this._item2ndPassTable); } public partial class ItemSyncTable : Microsoft.Synchronization.Data.SyncTable { partial void OnInitialized(); public ItemSyncTable() { this.InitializeTableOptions(); this.OnInitialized(); } [System.Diagnostics.DebuggerNonUserCodeAttribute()] private void InitializeTableOptions() { this.TableName = "Item"; this.CreationOption = Microsoft.Synchronization.Data.TableCreationOption.UseExistingTableOrFail; this.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional; // as viewed from the client } } public partial class Item2ndPassSyncTable : Microsoft.Synchronization.Data.SyncTable { partial void OnInitialized(); public Item2ndPassSyncTable() { this.InitializeTableOptions(); this.OnInitialized(); } [System.Diagnostics.DebuggerNonUserCodeAttribute()] private void InitializeTableOptions() { this.TableName = "Item2ndPass"; this.CreationOption = Microsoft.Synchronization.Data.TableCreationOption.UseExistingTableOrFail; this.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional; // as viewed from the client } } [Serializable] public partial class ItemSyncAdapter : Microsoft.Synchronization.Data.Server.SyncAdapter { partial void OnInitialized(); public ItemSyncAdapter() { this.InitializeCommands(); this.InitializeAdapterProperties(); this.OnInitialized(); } private void InitializeCommands() { // InsertCommand this.InsertCommand = new System.Data.SqlClient.SqlCommand(); // 1899-12-25 00:00:00.000 is a special value telling the replication trigger to skip this row this.InsertCommand.CommandText = @" DECLARE @replication_previous_identity_value INT SET @replication_previous_identity_value = (SELECT TOP 0 NULL FROM Item WITH (TABLOCKX)) SET @replication_previous_identity_value = IDENT_CURRENT('Item')SET IDENTITY_INSERT Item ON INSERT INTO Item ([ID], [Name], CreatedDate, LastUpdatedDate) VALUES ( @ID, @Name, @sync_last_received_anchor, '1899-12-25 00:00:00.000') SET @sync_row_count = @@rowcount DBCC CHECKIDENT('Item', RESEED, @replication_previous_identity_value) SET IDENTITY_INSERT Item OFF"; this.InsertCommand.CommandType = System.Data.CommandType.Text; this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@Name", System.Data.SqlDbType.VarChar)); this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); System.Data.SqlClient.SqlParameter insertcommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int); insertcommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output; this.InsertCommand.Parameters.Add(insertcommand_sync_row_countParameter); // UpdateCommand this.UpdateCommand = new System.Data.SqlClient.SqlCommand(); this.UpdateCommand.CommandText = @"UPDATE Item SET [Name] = @Name, [ParentID] = @ParentID, CreatedDate='1899-12-25 00:00:00.000', LastUpdatedDate=@sync_last_received_anchor WHERE ([ID] = @ID) AND (@sync_force_write = 1 OR ([LastUpdatedDate] IS NULL OR [LastUpdatedDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount"; this.UpdateCommand.CommandType = System.Data.CommandType.Text; this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@Name", System.Data.SqlDbType.VarChar)); this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ParentID", System.Data.SqlDbType.Int)); this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_force_write", System.Data.SqlDbType.Bit)); this.UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); System.Data.SqlClient.SqlParameter updatecommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int); updatecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output; this.UpdateCommand.Parameters.Add(updatecommand_sync_row_countParameter); // DeleteCommand this.DeleteCommand = new System.Data.SqlClient.SqlCommand(); this.DeleteCommand.CommandText = @"UPDATE Item SET [ParentID] = NULL WHERE [ParentID] = @ID UPDATE ItemRevision SET [ItemID] = NULL WHERE [ItemID] = @ID DELETE FROM Item WHERE ([ID] = @ID) AND (@sync_force_write = 1 OR ([LastUpdatedDate] <= @sync_last_received_anchor OR [LastUpdatedDate] IS NULL )) SET @sync_row_count = @@rowcount"; this.DeleteCommand.CommandType = System.Data.CommandType.Text; this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_force_write", System.Data.SqlDbType.Bit)); this.DeleteCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); System.Data.SqlClient.SqlParameter deletecommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int); deletecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output; this.DeleteCommand.Parameters.Add(deletecommand_sync_row_countParameter); // SelectConflictUpdatedRowsCommand this.SelectConflictUpdatedRowsCommand = new System.Data.SqlClient.SqlCommand(); this.SelectConflictUpdatedRowsCommand.CommandText = @"SELECT [ID], [Name], [ParentID] FROM Item WHERE ([ID] = @ID)"; this.SelectConflictUpdatedRowsCommand.CommandType = System.Data.CommandType.Text; this.SelectConflictUpdatedRowsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); // SelectConflictDeletedRowsCommand this.SelectConflictDeletedRowsCommand = new System.Data.SqlClient.SqlCommand(); this.SelectConflictDeletedRowsCommand.CommandText = @"SELECT FirstID AS ID FROM sys_ReplicationTombstone WHERE NameOfTable = 'Item' AND FirstID = @ID"; this.SelectConflictDeletedRowsCommand.CommandType = System.Data.CommandType.Text; this.SelectConflictDeletedRowsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); // SelectIncrementalInsertsCommand this.SelectIncrementalInsertsCommand = new System.Data.SqlClient.SqlCommand(); this.SelectIncrementalInsertsCommand.CommandText = @"SELECT [ID], [Name], [ParentID] FROM Item WHERE ([CreatedDate] > @sync_last_received_anchor AND [CreatedDate] <= @sync_new_received_anchor)"; this.SelectIncrementalInsertsCommand.CommandType = System.Data.CommandType.Text; this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime)); // SelectIncrementalUpdatesCommand this.SelectIncrementalUpdatesCommand = new System.Data.SqlClient.SqlCommand(); this.SelectIncrementalUpdatesCommand.CommandText = @"SELECT [ID], [Name], [ParentID] FROM Item WHERE ([LastUpdatedDate] > @sync_last_received_anchor AND [LastUpdatedDate] <= @sync_new_received_anchor AND [CreatedDate] <= @sync_last_received_anchor)"; this.SelectIncrementalUpdatesCommand.CommandType = System.Data.CommandType.Text; this.SelectIncrementalUpdatesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); this.SelectIncrementalUpdatesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime)); // SelectIncrementalDeletesCommand this.SelectIncrementalDeletesCommand = new System.Data.SqlClient.SqlCommand(); this.SelectIncrementalDeletesCommand.CommandText = @"SELECT FirstID AS ID FROM sys_ReplicationTombstone WHERE NameOfTable = 'Item'" + " AND DeletionDate > @sync_last_received_anchor AND DeletionDate <= @sync_new_received_anchor"; this.SelectIncrementalDeletesCommand.CommandType = System.Data.CommandType.Text; this.SelectIncrementalDeletesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); this.SelectIncrementalDeletesCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime)); } private void InitializeAdapterProperties() { this.TableName = "Item"; } } // end ItemSyncAdapter [Serializable] public partial class Item2ndPassSyncAdapter : Microsoft.Synchronization.Data.Server.SyncAdapter { partial void OnInitialized(); public Item2ndPassSyncAdapter() { this.InitializeCommands(); this.InitializeAdapterProperties(); this.OnInitialized(); } private void InitializeCommands() { // InsertCommand this.InsertCommand = new System.Data.SqlClient.SqlCommand(); this.InsertCommand.CommandText = @"UPDATE Item SET [ParentID] = @ParentID , CreatedDate='1899-12-25 00:00:00.000', LastUpdatedDate=@sync_last_received_anchor WHERE ([ID] = @ID) AND (@sync_force_write = 1 OR ([LastUpdatedDate] IS NULL OR [LastUpdatedDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount"; // 1899-12-25 00:00:00.000 is a special value telling the replication trigger to skip this row this.InsertCommand.CommandType = System.Data.CommandType.Text; this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ParentID", System.Data.SqlDbType.Int)); this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int)); this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); this.InsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_force_write", System.Data.SqlDbType.Bit)); System.Data.SqlClient.SqlParameter insertcommand_sync_row_countParameter = new System.Data.SqlClient.SqlParameter("@sync_row_count", System.Data.SqlDbType.Int); insertcommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output; this.InsertCommand.Parameters.Add(insertcommand_sync_row_countParameter); // SelectIncrementalInsertsCommand this.SelectIncrementalInsertsCommand = new System.Data.SqlClient.SqlCommand(); this.SelectIncrementalInsertsCommand.CommandText = @"SELECT [ID], [ParentID] FROM Item WHERE ([CreatedDate] > @sync_last_received_anchor AND [CreatedDate] <= @sync_new_received_anchor)"; this.SelectIncrementalInsertsCommand.CommandType = System.Data.CommandType.Text; this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_last_received_anchor", System.Data.SqlDbType.DateTime)); this.SelectIncrementalInsertsCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sync_new_received_anchor", System.Data.SqlDbType.DateTime)); } private void InitializeAdapterProperties() { this.TableName = "Item2ndPass"; } } // end Item2ndPassSyncAdapter } }