public RelationalProviderProxy(string scopeName, string hostName)
        {
            this.scopeName = scopeName;

            this.hostName = hostName;
            this.CreateProxy(hostName);
            this.proxy.Initialize(scopeName, hostName);
            DbSyncAdapter dbSyncAdapter = new DbSyncAdapter();
        }
Пример #2
0
        /// <summary>
        /// Create a new BatchInfo, containing all BatchPartInfo
        /// </summary>
        public BatchInfo(SyncSet inSchema, string rootDirectory = null, string directoryName = null)
        {
            // We need to create a change table set, containing table with columns not readonly
            foreach (var table in inSchema.Tables)
            {
                DbSyncAdapter.CreateChangesTable(inSchema.Tables[table.TableName, table.SchemaName], this.SanitizedSchema);
            }

            this.DirectoryRoot  = rootDirectory;
            this.BatchPartsInfo = new List <BatchPartInfo>();
            this.DirectoryName  = string.IsNullOrEmpty(directoryName) ? string.Concat(DateTime.UtcNow.ToString("yyyy_MM_dd_ss"), Path.GetRandomFileName().Replace(".", "")) : directoryName;
        }
Пример #3
0
        public static DbSyncAdapter CreateSyncAdpters()
        {
            DbSyncAdapter syncAdapter = new DbSyncAdapter("cards");

            syncAdapter.RowIdColumns.Add("card_id");


            syncAdapter.SelectIncrementalChangesCommand = SyncAdapterCommandHelper.GetSelectIncrementalChangesCommand();
            syncAdapter.InsertCommand    = SyncAdapterCommandHelper.GetInsertCommand();
            syncAdapter.UpdateCommand    = SyncAdapterCommandHelper.GetUpdateCommand();
            syncAdapter.DeleteCommand    = SyncAdapterCommandHelper.GetDeleteCommand();
            syncAdapter.SelectRowCommand = SyncAdapterCommandHelper.GetSelectRowCommand();

            syncAdapter.InsertMetadataCommand           = SyncAdapterCommandHelper.GetInsertMetadataCommand();
            syncAdapter.UpdateMetadataCommand           = SyncAdapterCommandHelper.GetUpdateMetadataCommand();
            syncAdapter.DeleteMetadataCommand           = SyncAdapterCommandHelper.GetDeleteMetadataCommand();
            syncAdapter.SelectMetadataForCleanupCommand = SyncAdapterCommandHelper.GetSelectMetadataForCleanupCommand(100);

            return(syncAdapter);
        }
Пример #4
0
        DbSyncProvider SetupSyncProvider(string connectionString, DbSyncProvider peerProvider)
        {
            const int TombstoneAgingInHours = 100;

            SqlConnection connection = new SqlConnection(connectionString);

            peerProvider.Connection = connection;

            //
            // 1. Create sync adapter for each sync table and attach it to the provider
            // Following DataAdapter style in ADO.NET, DbSyncAdapter is the equivelent for sync.
            // The code below shows how to create DbSyncAdapter objects for orders
            // and order_details tables using stored procedures stored on the database.
            //

            peerProvider.ScopeName = "Sales";

            // orders table
            DbSyncAdapter adaptorOrders = new DbSyncAdapter("orders");

            adaptorOrders.RowIdColumns.Add("order_id");



            // select incremental changes command
            SqlCommand chgsOrdersCmd = new SqlCommand();

            chgsOrdersCmd.CommandType = CommandType.StoredProcedure;
            chgsOrdersCmd.CommandText = "sp_orders_selectchanges";
            chgsOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);

            adaptorOrders.SelectIncrementalChangesCommand = chgsOrdersCmd;


            // insert row command
            SqlCommand insOrdersCmd = new SqlCommand();

            insOrdersCmd.CommandType = CommandType.StoredProcedure;
            insOrdersCmd.CommandText = "sp_orders_applyinsert";
            insOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insOrdersCmd.Parameters.Add("@order_date", SqlDbType.DateTime);
            insOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.InsertCommand = insOrdersCmd;


            // update row command
            SqlCommand updOrdersCmd = new SqlCommand();

            updOrdersCmd.CommandType = CommandType.StoredProcedure;
            updOrdersCmd.CommandText = "sp_orders_applyupdate";
            updOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updOrdersCmd.Parameters.Add("@order_date", SqlDbType.DateTime);
            updOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.UpdateCommand = updOrdersCmd;


            // delete row command
            SqlCommand delOrdersCmd = new SqlCommand();

            delOrdersCmd.CommandType = CommandType.StoredProcedure;
            delOrdersCmd.CommandText = "sp_orders_applydelete";
            delOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delOrdersCmd.Parameters.Add("@sync_force_write", SqlDbType.Int);
            delOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.DeleteCommand = delOrdersCmd;

            // get row command
            SqlCommand selRowOrdersCmd = new SqlCommand();

            selRowOrdersCmd.CommandType = CommandType.StoredProcedure;
            selRowOrdersCmd.CommandText = "sp_orders_selectrow";
            selRowOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);

            adaptorOrders.SelectRowCommand = selRowOrdersCmd;


            // insert row metadata command
            SqlCommand insMetadataOrdersCmd = new SqlCommand();

            insMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            insMetadataOrdersCmd.CommandText = "sp_orders_insertmetadata";
            insMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.InsertMetadataCommand = insMetadataOrdersCmd;


            // update row metadata command
            SqlCommand updMetadataOrdersCmd = new SqlCommand();

            updMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            updMetadataOrdersCmd.CommandText = "sp_orders_updatemetadata";
            updMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.UpdateMetadataCommand = updMetadataOrdersCmd;

            // delete row metadata command
            SqlCommand delMetadataOrdersCmd = new SqlCommand();

            delMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            delMetadataOrdersCmd.CommandText = "sp_orders_deletemetadata";
            delMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.DeleteMetadataCommand = delMetadataOrdersCmd;


            // get tombstones for cleanup
            SqlCommand selTombstonesOrdersCmd = new SqlCommand();

            selTombstonesOrdersCmd.CommandType = CommandType.StoredProcedure;
            selTombstonesOrdersCmd.CommandText = "sp_orders_selecttombstones";
            selTombstonesOrdersCmd.Parameters.Add("@tombstone_aging_in_hours", SqlDbType.Int).Value = TombstoneAgingInHours;

            adaptorOrders.SelectMetadataForCleanupCommand = selTombstonesOrdersCmd;

            peerProvider.SyncAdapters.Add(adaptorOrders);


            // order_details table
            DbSyncAdapter adaptorOrderDetails = new DbSyncAdapter("order_details");

            adaptorOrderDetails.RowIdColumns.Add("order_id");

            // select incremental inserts command
            SqlCommand chgsOrderDetailsCmd = new SqlCommand();

            chgsOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            chgsOrderDetailsCmd.CommandText = "sp_order_details_selectchanges";
            chgsOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);

            adaptorOrderDetails.SelectIncrementalChangesCommand = chgsOrderDetailsCmd;


            // insert row command
            SqlCommand insOrderDetailsCmd = new SqlCommand();

            insOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            insOrderDetailsCmd.CommandText = "sp_order_details_applyinsert";
            insOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@order_details_id", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@product", SqlDbType.VarChar, 100);
            insOrderDetailsCmd.Parameters.Add("@quantity", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.InsertCommand = insOrderDetailsCmd;


            // update row command
            SqlCommand updOrderDetailsCmd = new SqlCommand();

            updOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            updOrderDetailsCmd.CommandText = "sp_order_details_applyupdate";
            updOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@order_details_id", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@product", SqlDbType.VarChar, 100);
            updOrderDetailsCmd.Parameters.Add("@quantity", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.UpdateCommand = updOrderDetailsCmd;


            // delete row command
            SqlCommand delOrderDetailsCmd = new SqlCommand();

            delOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            delOrderDetailsCmd.CommandText = "sp_order_details_applydelete";
            delOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delOrderDetailsCmd.Parameters.Add("@sync_force_write", SqlDbType.Int);
            delOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.DeleteCommand = delOrderDetailsCmd;

            // get row command
            SqlCommand selRowOrderDetailsCmd = new SqlCommand();

            selRowOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            selRowOrderDetailsCmd.CommandText = "sp_order_details_selectrow";
            selRowOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);

            adaptorOrderDetails.SelectRowCommand = selRowOrderDetailsCmd;


            // insert row metadata command
            SqlCommand insMetadataOrderDetailsCmd = new SqlCommand();

            insMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            insMetadataOrderDetailsCmd.CommandText = "sp_order_details_insertmetadata";
            insMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.InsertMetadataCommand = insMetadataOrderDetailsCmd;


            // update row metadata command
            SqlCommand updMetadataOrderDetailsCmd = new SqlCommand();

            updMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            updMetadataOrderDetailsCmd.CommandText = "sp_order_details_updatemetadata";
            updMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.UpdateMetadataCommand = updMetadataOrderDetailsCmd;

            // delete row metadata command
            SqlCommand delMetadataOrderDetailsCmd = new SqlCommand();

            delMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            delMetadataOrderDetailsCmd.CommandText = "sp_order_details_deletemetadata";
            delMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.DeleteMetadataCommand = delMetadataOrderDetailsCmd;


            // get tombstones for cleanup
            SqlCommand selTombstonesOrderDetailsCmd = new SqlCommand();

            selTombstonesOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            selTombstonesOrderDetailsCmd.CommandText = "sp_order_details_selecttombstones";
            selTombstonesOrderDetailsCmd.Parameters.Add("@tombstone_aging_in_hours", SqlDbType.Int).Value = TombstoneAgingInHours;

            adaptorOrderDetails.SelectMetadataForCleanupCommand = selTombstonesOrderDetailsCmd;


            peerProvider.SyncAdapters.Add(adaptorOrderDetails);

            //
            // 2. Setup provider wide commands
            // There are few commands on the provider itself and not on a table sync adapter:
            // SelectNewTimestampCommand: Returns the new high watermark for current sync
            // SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge and scope version (timestamp)
            // UpdateScopeInfoCommand: Sets the new values for sync knowledge and cleanup knowledge
            //

            SqlCommand anchorCmd = new SqlCommand();

            anchorCmd.CommandType = CommandType.Text;
            anchorCmd.CommandText = "select @" + DbSyncSession.SyncNewTimestamp + " = @@DBTS";  // for SQL Server 2005 SP2, use "min_active_rowversion() - 1"
            anchorCmd.Parameters.Add("@" + DbSyncSession.SyncNewTimestamp, SqlDbType.BigInt).Direction = ParameterDirection.Output;

            peerProvider.SelectNewTimestampCommand = anchorCmd;

            //
            // Select local replica info
            //
            SqlCommand selReplicaInfoCmd = new SqlCommand();

            selReplicaInfoCmd.CommandType = CommandType.Text;
            selReplicaInfoCmd.CommandText = "select " +
                                            "@" + DbSyncSession.SyncScopeId + " = scope_Id, " +
                                            "@" + DbSyncSession.SyncScopeKnowledge + " = scope_sync_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeCleanupKnowledge + " = scope_tombstone_cleanup_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeTimestamp + " = scope_timestamp " +
                                            "from scope_info " +
                                            "where scope_name = @" + DbSyncSession.SyncScopeName;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier).Direction               = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000).Direction        = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000).Direction = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt).Direction = ParameterDirection.Output;
            peerProvider.SelectScopeInfoCommand = selReplicaInfoCmd;

            //
            // Update local replica info
            //
            SqlCommand updReplicaInfoCmd = new SqlCommand();

            updReplicaInfoCmd.CommandType = CommandType.Text;
            updReplicaInfoCmd.CommandText = "update  scope_info set " +
                                            "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " +
                                            "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                            "where scope_name = @" + DbSyncSession.SyncScopeName + " ;" +
                                            "set @" + DbSyncSession.SyncRowCount + " = @@rowcount";
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            peerProvider.UpdateScopeInfoCommand = updReplicaInfoCmd;



            //
            // 3. Track sync process
            //
            peerProvider.SyncProgress      += new EventHandler <DbSyncProgressEventArgs>(ShowProgress);
            peerProvider.ApplyChangeFailed += new EventHandler <DbApplyChangeFailedEventArgs>(ShowFailures);
            peerProvider.SyncPeerOutdated  += new EventHandler <DbOutdatedEventArgs>(ShowOutdated);

            return(peerProvider);
        }
        InternalApplyThenGetChangesAsync(ClientScopeInfo clientScopeInfo, SyncContext context, BatchInfo clientBatchInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ChangesApplying, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            SyncSet         schema;
            ServerScopeInfo serverScopeInfo;

            // is it something that could happens ?
            if (clientScopeInfo.Schema == null)
            {
                // Make a remote call to get Schema from remote provider
                (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(
                    context, null, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                schema = serverScopeInfo.Schema;
            }
            else
            {
                schema = clientScopeInfo.Schema;
            }

            schema.EnsureSchema();

            // if we don't have any BatchPartsInfo, just generate a new one to get, at least, something to send to the server
            // and get a response with new data from server
            if (clientBatchInfo == null)
            {
                clientBatchInfo = new BatchInfo(schema);
            }

            // --------------------------------------------------------------
            // STEP 1 : Send everything to the server side
            // --------------------------------------------------------------

            HttpResponseMessage response = null;

            // If not in memory and BatchPartsInfo.Count == 0, nothing to send.
            // But we need to send something, so generate a little batch part
            if (clientBatchInfo.BatchPartsInfo.Count == 0)
            {
                var changesToSend = new HttpMessageSendChangesRequest(context, clientScopeInfo);

                var containerSet = new ContainerSet();
                changesToSend.Changes     = containerSet;
                changesToSend.IsLastBatch = true;
                changesToSend.BatchIndex  = 0;
                changesToSend.BatchCount  = clientBatchInfo.BatchPartsInfo == null ? 0 : clientBatchInfo.BatchPartsInfo.Count;
                var inMemoryRowsCount = changesToSend.Changes.RowsCount();

                context.ProgressPercentage += 0.125;

                await this.InterceptAsync(new HttpSendingClientChangesRequestArgs(changesToSend, inMemoryRowsCount, inMemoryRowsCount, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

                // serialize message
                var serializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>();
                var binaryData = await serializer.SerializeAsync(changesToSend);

                response = await this.httpRequestHandler.ProcessRequestAsync
                               (this.HttpClient, context, this.ServiceUri, binaryData, HttpStep.SendChangesInProgress,
                               this.SerializerFactory, this.Converter, this.Options.BatchSize, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);
            }
            else
            {
                int tmpRowsSendedCount = 0;

                // Foreach part, will have to send them to the remote
                // once finished, return context
                var initialPctProgress1 = context.ProgressPercentage;
                var localSerializer     = new LocalJsonSerializer();

                var interceptorsReading = this.interceptors.GetInterceptors <DeserializingRowArgs>();
                if (interceptorsReading.Count > 0)
                {
                    localSerializer.OnReadingRow(async(schemaTable, rowString) =>
                    {
                        var args = new DeserializingRowArgs(context, schemaTable, rowString);
                        await this.InterceptAsync(args);
                        return(args.Result);
                    });
                }
                foreach (var bpi in clientBatchInfo.BatchPartsInfo.OrderBy(bpi => bpi.Index))
                {
                    // Get the updatable schema for the only table contained in the batchpartinfo
                    var schemaTable = DbSyncAdapter.CreateChangesTable(schema.Tables[bpi.Tables[0].TableName, bpi.Tables[0].SchemaName]);

                    // Generate the ContainerSet containing rows to send to the user
                    var containerSet   = new ContainerSet();
                    var containerTable = new ContainerTable(schemaTable);
                    var fullPath       = Path.Combine(clientBatchInfo.GetDirectoryFullPath(), bpi.FileName);
                    containerSet.Tables.Add(containerTable);

                    // read rows from file
                    foreach (var row in localSerializer.ReadRowsFromFile(fullPath, schemaTable))
                    {
                        containerTable.Rows.Add(row.ToArray());
                    }

                    // Call the converter if needed
                    if (this.Converter != null && containerTable.HasRows)
                    {
                        BeforeSerializeRows(containerTable, schemaTable, this.Converter);
                    }

                    // Create the send changes request
                    var changesToSend = new HttpMessageSendChangesRequest(context, clientScopeInfo)
                    {
                        Changes     = containerSet,
                        IsLastBatch = bpi.IsLastBatch,
                        BatchIndex  = bpi.Index,
                        BatchCount  = clientBatchInfo.BatchPartsInfo.Count
                    };

                    tmpRowsSendedCount += containerTable.Rows.Count;

                    context.ProgressPercentage = initialPctProgress1 + ((changesToSend.BatchIndex + 1) * 0.2d / changesToSend.BatchCount);
                    await this.InterceptAsync(new HttpSendingClientChangesRequestArgs(changesToSend, tmpRowsSendedCount, clientBatchInfo.RowsCount, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

                    // serialize message
                    var serializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>();
                    var binaryData = await serializer.SerializeAsync(changesToSend);

                    response = await this.httpRequestHandler.ProcessRequestAsync
                                   (this.HttpClient, context, this.ServiceUri, binaryData, HttpStep.SendChangesInProgress,
                                   this.SerializerFactory, this.Converter, this.Options.BatchSize, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);

                    // See #721 for issue and #721 for PR from slagtejn
                    if (!bpi.IsLastBatch)
                    {
                        response.Dispose();
                    }
                }
            }

            // --------------------------------------------------------------
            // STEP 2 : Receive everything from the server side
            // --------------------------------------------------------------

            // Now we have sent all the datas to the server and now :
            // We have a FIRST response from the server with new datas
            // 1) Could be the only one response
            // 2) Could be the first response and we need to download all batchs

            context.SyncStage = SyncStage.ChangesSelecting;
            var initialPctProgress = 0.55;

            context.ProgressPercentage = initialPctProgress;

            // Create the BatchInfo
            var serverBatchInfo = new BatchInfo(schema);

            HttpMessageSummaryResponse summaryResponseContent = null;

            // Deserialize response incoming from server
            using (var streamResponse = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
            {
                var responseSerializer = this.SerializerFactory.GetSerializer <HttpMessageSummaryResponse>();
                summaryResponseContent = await responseSerializer.DeserializeAsync(streamResponse);
            }

            serverBatchInfo.RowsCount = summaryResponseContent.BatchInfo.RowsCount;
            serverBatchInfo.Timestamp = summaryResponseContent.RemoteClientTimestamp;
            context = summaryResponseContent.SyncContext;

            if (summaryResponseContent.BatchInfo.BatchPartsInfo != null)
            {
                foreach (var bpi in summaryResponseContent.BatchInfo.BatchPartsInfo)
                {
                    serverBatchInfo.BatchPartsInfo.Add(bpi);
                }
            }


            // From here, we need to serialize everything on disk

            // Generate the batch directory
            var batchDirectoryRoot = this.Options.BatchDirectory;
            var batchDirectoryName = string.Concat(DateTime.UtcNow.ToString("yyyy_MM_dd_ss"), Path.GetRandomFileName().Replace(".", ""));

            serverBatchInfo.DirectoryRoot = batchDirectoryRoot;
            serverBatchInfo.DirectoryName = batchDirectoryName;

            if (!Directory.Exists(serverBatchInfo.GetDirectoryFullPath()))
            {
                Directory.CreateDirectory(serverBatchInfo.GetDirectoryFullPath());
            }

            // If we have a snapshot we are raising the batches downloading process that will occurs
            await this.InterceptAsync(new HttpBatchesDownloadingArgs(context, serverBatchInfo, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

            // function used to download one part
            var dl = new Func <BatchPartInfo, Task>(async(bpi) =>
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                var changesToSend3 = new HttpMessageGetMoreChangesRequest(context, bpi.Index);

                var serializer3 = this.SerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>();
                var binaryData3 = await serializer3.SerializeAsync(changesToSend3).ConfigureAwait(false);
                var step3       = HttpStep.GetMoreChanges;

                await this.InterceptAsync(new HttpGettingServerChangesRequestArgs(bpi.Index, serverBatchInfo.BatchPartsInfo.Count, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

                // Raise get changes request
                context.ProgressPercentage = initialPctProgress + ((bpi.Index + 1) * 0.2d / serverBatchInfo.BatchPartsInfo.Count);

                var response = await this.httpRequestHandler.ProcessRequestAsync(
                    this.HttpClient, context, this.ServiceUri, binaryData3, step3,
                    this.SerializerFactory, this.Converter, 0, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);

                if (this.SerializerFactory.Key != "json")
                {
                    var webSerializer        = this.SerializerFactory.GetSerializer <HttpMessageSendChangesResponse>();
                    using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                    var getMoreChanges       = await webSerializer.DeserializeAsync(responseStream);

                    context = getMoreChanges.SyncContext;

                    if (getMoreChanges != null && getMoreChanges.Changes != null && getMoreChanges.Changes.HasRows)
                    {
                        var localSerializer = new LocalJsonSerializer();

                        var interceptorsWriting = this.interceptors.GetInterceptors <SerializingRowArgs>();
                        if (interceptorsWriting.Count > 0)
                        {
                            localSerializer.OnWritingRow(async(syncTable, rowArray) =>
                            {
                                var args = new SerializingRowArgs(context, syncTable, rowArray);
                                await this.InterceptAsync(args, progress, cancellationToken).ConfigureAwait(false);
                                return(args.Result);
                            });
                        }


                        // Should have only one table
                        var table       = getMoreChanges.Changes.Tables[0];
                        var schemaTable = DbSyncAdapter.CreateChangesTable(schema.Tables[table.TableName, table.SchemaName]);

                        var fullPath = Path.Combine(serverBatchInfo.GetDirectoryFullPath(), bpi.FileName);

                        // open the file and write table header
                        await localSerializer.OpenFileAsync(fullPath, schemaTable).ConfigureAwait(false);

                        foreach (var row in table.Rows)
                        {
                            await localSerializer.WriteRowToFileAsync(new SyncRow(schemaTable, row), schemaTable).ConfigureAwait(false);
                        }

                        // Close file
                        await localSerializer.CloseFileAsync(fullPath, schemaTable).ConfigureAwait(false);
                    }
                }
                else
                {
                    // Serialize
                    await SerializeAsync(response, bpi.FileName, serverBatchInfo.GetDirectoryFullPath(), this).ConfigureAwait(false);
                }

                // Raise response from server containing a batch changes
                await this.InterceptAsync(new HttpGettingServerChangesResponseArgs(serverBatchInfo, bpi.Index, bpi.RowsCount, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);
            });

            // Parrallel download of all bpis (which will launch the delete directory on the server side)
            await serverBatchInfo.BatchPartsInfo.ForEachAsync(bpi => dl(bpi), this.MaxDownladingDegreeOfParallelism).ConfigureAwait(false);

            // Send order of end of download
            var lastBpi = serverBatchInfo.BatchPartsInfo.FirstOrDefault(bpi => bpi.IsLastBatch);

            if (lastBpi != null)
            {
                var endOfDownloadChanges = new HttpMessageGetMoreChangesRequest(context, lastBpi.Index);

                var serializerEndOfDownloadChanges = this.SerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>();
                var binaryData3 = await serializerEndOfDownloadChanges.SerializeAsync(endOfDownloadChanges).ConfigureAwait(false);

                var endResponse = await this.httpRequestHandler.ProcessRequestAsync(
                    this.HttpClient, context, this.ServiceUri, binaryData3, HttpStep.SendEndDownloadChanges,
                    this.SerializerFactory, this.Converter, 0, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);

                // Deserialize response incoming from server
                // This is the last response
                // Should contains step HttpStep.SendEndDownloadChanges
                using var streamResponse = await endResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

                var endResponseSerializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesResponse>();
                var endResponseContent    = await endResponseSerializer.DeserializeAsync(streamResponse);

                context = endResponseContent.SyncContext;
            }

            // generate the new scope item
            this.CompleteTime = DateTime.UtcNow;

            await this.InterceptAsync(new HttpBatchesDownloadedArgs(summaryResponseContent, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

            var serverSyncChanges = new ServerSyncChanges(
                summaryResponseContent.RemoteClientTimestamp,
                serverBatchInfo,
                summaryResponseContent.ServerChangesSelected
                );


            return(context, serverSyncChanges, summaryResponseContent.ClientChangesApplied, summaryResponseContent.ConflictResolutionPolicy);
        }
        //</snippetOCSv2_CS_Peer_Cleanup_ConfigureCeSyncProvider>


        //<snippetOCSv2_CS_Peer_Cleanup_ConfigureDbSyncProvider>
        public DbSyncProvider ConfigureDbSyncProvider(string peerConnString, int metadataAgingInDays)
        {
            DbSyncProvider sampleDbProvider = new DbSyncProvider();

            //<snippetOCSv2_CS_Peer_Cleanup_Scope>
            SqlConnection peerConnection = new SqlConnection(peerConnString);

            sampleDbProvider.Connection = peerConnection;
            sampleDbProvider.ScopeName  = "Sales";
            //</snippetOCSv2_CS_Peer_Cleanup_Scope>

            //Create a DbSyncAdapter object for the Customer table and associate it
            //with the DbSyncProvider. Following the DataAdapter style in ADO.NET,
            //DbSyncAdapter is the equivalent for synchronization. The commands that
            //are specified for the DbSyncAdapter object call stored procedures
            //that are created in each peer database.
            //<snippetOCSv2_CS_Peer_Cleanup_AdapterCustomer>
            DbSyncAdapter adapterCustomer = new DbSyncAdapter("Customer");


            //Specify the primary key, which Sync Services uses
            //to identify each row during synchronization.
            adapterCustomer.RowIdColumns.Add("CustomerId");
            //</snippetOCSv2_CS_Peer_Cleanup_AdapterCustomer>


            //Specify the command to select incremental changes.
            //In this command and other commands, session variables are
            //used to pass information at runtime. DbSyncSession.SyncMetadataOnly
            //and SyncMinTimestamp are two of the string constants that
            //the DbSyncSession class exposes. You could also include
            //@sync_metadata_only and @sync_min_timestamp directly in your
            //queries:
            //*  sync_metadata_only is used by Sync Services as an optimization
            //   in some queries.
            //* The value of the sync_min_timestamp session variable is compared to
            //   values in the sync_row_timestamp column in the tracking table to
            //   determine which rows to select.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectIncrementalChangesCommand>
            SqlCommand chgsCustomerCmd = new SqlCommand();

            chgsCustomerCmd.CommandType = CommandType.StoredProcedure;
            chgsCustomerCmd.CommandText = "Sync.sp_Customer_SelectChanges";
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncInitialize, SqlDbType.Int);

            adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectIncrementalChangesCommand>

            //Specify the command to insert rows.
            //The sync_row_count session variable is used in this command
            //and other commands to return a count of the rows affected by an operation.
            //A count of 0 indicates that an operation failed.
            //<snippetOCSv2_CS_Peer_Cleanup_InsertCommand>
            SqlCommand insCustomerCmd = new SqlCommand();

            insCustomerCmd.CommandType = CommandType.StoredProcedure;
            insCustomerCmd.CommandText = "Sync.sp_Customer_ApplyInsert";
            insCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            insCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.InsertCommand = insCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_InsertCommand>


            //Specify the command to update rows.
            //The value of the sync_min_timestamp session variable is compared to
            //values in the sync_row_timestamp column in the tracking table to
            //determine which rows to update.
            //<snippetOCSv2_CS_Peer_Cleanup_UpdateCommand>
            SqlCommand updCustomerCmd = new SqlCommand();

            updCustomerCmd.CommandType = CommandType.StoredProcedure;
            updCustomerCmd.CommandText = "Sync.sp_Customer_ApplyUpdate";
            updCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            updCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

            adapterCustomer.UpdateCommand = updCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_UpdateCommand>


            //Specify the command to delete rows.
            //The value of the sync_min_timestamp session variable is compared to
            //values in the sync_row_timestamp column in the tracking table to
            //determine which rows to delete.
            //<snippetOCSv2_CS_Peer_Cleanup_DeleteCommand>
            SqlCommand delCustomerCmd = new SqlCommand();

            delCustomerCmd.CommandType = CommandType.StoredProcedure;
            delCustomerCmd.CommandText = "Sync.sp_Customer_ApplyDelete";
            delCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

            adapterCustomer.DeleteCommand = delCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_DeleteCommand>

            //Specify the command to select any conflicting rows.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectRowCommand>
            SqlCommand selRowCustomerCmd = new SqlCommand();

            selRowCustomerCmd.CommandType = CommandType.StoredProcedure;
            selRowCustomerCmd.CommandText = "Sync.sp_Customer_SelectRow";
            selRowCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            selRowCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);

            adapterCustomer.SelectRowCommand = selRowCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectRowCommand>


            //Specify the command to insert metadata rows.
            //The session variables in this command relate to columns in
            //the tracking table.
            //<snippetOCSv2_CS_Peer_Cleanup_InsertMetadataCommand>
            SqlCommand insMetadataCustomerCmd = new SqlCommand();

            insMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            insMetadataCustomerCmd.CommandText = "Sync.sp_Customer_InsertMetadata";
            insMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.InsertMetadataCommand = insMetadataCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_InsertMetadataCommand>


            //Specify the command to update metadata rows.
            //<snippetOCSv2_CS_Peer_Cleanup_UpdateMetadataCommand>
            SqlCommand updMetadataCustomerCmd = new SqlCommand();

            updMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            updMetadataCustomerCmd.CommandText = "Sync.sp_Customer_UpdateMetadata";
            updMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_UpdateMetadataCommand>

            //Specify the command to delete metadata rows.
            //<snippetOCSv2_CS_Peer_Cleanup_DeleteMetadataCommand>
            SqlCommand delMetadataCustomerCmd = new SqlCommand();

            delMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            delMetadataCustomerCmd.CommandText = "Sync.sp_Customer_DeleteMetadata";
            delMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.DeleteMetadataCommand = delMetadataCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_DeleteMetadataCommand>

            //Specify the command to select metadata rows for cleanup.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectMetadataForCleanupCommand>
            SqlCommand selMetadataCustomerCmd = new SqlCommand();

            selMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            selMetadataCustomerCmd.CommandText = "Sync.sp_Customer_SelectMetadata";
            selMetadataCustomerCmd.Parameters.Add("@metadata_aging_in_days", SqlDbType.Int).Value = metadataAgingInDays;
            selMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncScopeLocalId, SqlDbType.Int);

            adapterCustomer.SelectMetadataForCleanupCommand = selMetadataCustomerCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectMetadataForCleanupCommand>

            //Add the adapter to the provider.
            sampleDbProvider.SyncAdapters.Add(adapterCustomer);


            // Configure commands that relate to the provider itself rather
            // than the DbSyncAdapter object for each table:
            // * SelectNewTimestampCommand: Returns the new high watermark for
            //   the current synchronization session.
            // * SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge,
            //   and a scope version (timestamp).
            // * UpdateScopeInfoCommand: Sets new values for sync knowledge and cleanup knowledge.
            // * SelectTableMaxTimestampsCommand (optional): Returns the maximum timestamp from each base table
            //   or tracking table, to determine whether for each table the destination already
            //   has all of the changes from the source. If a destination table has all the changes,
            //   SelectIncrementalChangesCommand is not called for that table.
            // * SelectOverlappingScopesCommand: returns the scope name and table name for all tables
            //   in the specified scope that are also included in other scopes.
            // * UpdateScopeCleanupTimestampCommand: updates the scope_cleanup_timestamp column for a
            //   particular scope in the scope_info table, to mark the point up to which cleanup
            //   has been performed for the scope.


            //Select a new timestamp.
            //During each synchronization, the new value and
            //the last value from the previous synchronization
            //are used: the set of changes between these upper and
            //lower bounds is synchronized.
            //<snippetOCSv2_CS_Peer_Cleanup_NewAnchorCommand>
            SqlCommand selectNewTimestampCommand = new SqlCommand();
            string     newTimestampVariable      = "@" + DbSyncSession.SyncNewTimestamp;

            selectNewTimestampCommand.CommandText = "SELECT " + newTimestampVariable + " = min_active_rowversion() - 1";
            selectNewTimestampCommand.Parameters.Add(newTimestampVariable, SqlDbType.Timestamp);
            selectNewTimestampCommand.Parameters[newTimestampVariable].Direction = ParameterDirection.Output;

            sampleDbProvider.SelectNewTimestampCommand = selectNewTimestampCommand;
            //</snippetOCSv2_CS_Peer_Cleanup_NewAnchorCommand>

            //Specify the command to select local replica metadata.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectScopeInfoCommand>
            SqlCommand selReplicaInfoCmd = new SqlCommand();

            selReplicaInfoCmd.CommandType = CommandType.Text;
            selReplicaInfoCmd.CommandText = "SELECT " +
                                            "scope_id, " +
                                            "scope_local_id, " +
                                            "scope_sync_knowledge, " +
                                            "scope_tombstone_cleanup_knowledge, " +
                                            "scope_timestamp " +
                                            "FROM Sync.ScopeInfo " +
                                            "WHERE scope_name = @" + DbSyncSession.SyncScopeName;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);

            sampleDbProvider.SelectScopeInfoCommand = selReplicaInfoCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectScopeInfoCommand>


            //Specify the command to update local replica metadata.
            //<snippetOCSv2_CS_Peer_Cleanup_UpdateScopeInfoCommand>
            SqlCommand updReplicaInfoCmd = new SqlCommand();

            updReplicaInfoCmd.CommandType = CommandType.Text;
            updReplicaInfoCmd.CommandText = "UPDATE  Sync.ScopeInfo SET " +
                                            "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " +
                                            "scope_id = @" + DbSyncSession.SyncScopeId + ", " +
                                            "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                            "WHERE scope_name = @" + DbSyncSession.SyncScopeName + " AND " +
                                            " ( @" + DbSyncSession.SyncCheckConcurrency + " = 0 OR scope_timestamp = @" + DbSyncSession.SyncScopeTimestamp + "); " +
                                            "SET @" + DbSyncSession.SyncRowCount + " = @@rowcount";
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            sampleDbProvider.UpdateScopeInfoCommand = updReplicaInfoCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_UpdateScopeInfoCommand>


            //Return the maximum timestamp from the Customer_Tracking table.
            //If more tables are synchronized, the query should UNION
            //all of the results. The table name is not schema-qualified
            //in this case because the name was not schema qualified in the
            //DbSyncAdapter constructor.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectTableMaxTimestampsCommand>
            SqlCommand selTableMaxTsCmd = new SqlCommand();

            selTableMaxTsCmd.CommandType = CommandType.Text;
            selTableMaxTsCmd.CommandText = "SELECT 'Customer' AS table_name, " +
                                           "MAX(local_update_peer_timestamp) AS max_timestamp " +
                                           "FROM Sync.Customer_Tracking";
            sampleDbProvider.SelectTableMaxTimestampsCommand = selTableMaxTsCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectTableMaxTimestampsCommand>


            //Specify the command to select table and scope names for
            //any tables that are in more than one scope.
            //<snippetOCSv2_CS_Peer_Cleanup_SelectOverlappingScopesCommand>
            SqlCommand overlappingScopesCmd = new SqlCommand();

            overlappingScopesCmd.CommandType = CommandType.StoredProcedure;
            overlappingScopesCmd.CommandText = "Sync.sp_SelectSharedScopes";
            overlappingScopesCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            sampleDbProvider.SelectOverlappingScopesCommand = overlappingScopesCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_SelectOverlappingScopesCommand>

            //Specify the command that updates the scope information table
            //to indicate to which point metadata has been cleaned up for a scope.
            //<snippetOCSv2_CS_Peer_Cleanup_UpdateScopeCleanupTimestampCommand>
            SqlCommand updScopeCleanupInfoCmd = new SqlCommand();

            updScopeCleanupInfoCmd.CommandType = CommandType.Text;
            updScopeCleanupInfoCmd.CommandText = "UPDATE  scope_info set " +
                                                 " scope_cleanup_timestamp = @" + DbSyncSession.SyncScopeCleanupTimestamp +
                                                 " WHERE scope_name = @" + DbSyncSession.SyncScopeName +
                                                 " AND(scope_cleanup_timestamp is null or scope_cleanup_timestamp <  @" + DbSyncSession.SyncScopeCleanupTimestamp + ");" +
                                                 " SET @" + DbSyncSession.SyncRowCount + " = @@rowcount";
            updScopeCleanupInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupTimestamp, SqlDbType.BigInt);
            updScopeCleanupInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            updScopeCleanupInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            sampleDbProvider.UpdateScopeCleanupTimestampCommand = updScopeCleanupInfoCmd;
            //</snippetOCSv2_CS_Peer_Cleanup_UpdateScopeCleanupTimestampCommand>

            return(sampleDbProvider);
        }
Пример #7
0
        DbSyncProvider SetupSyncProvider(string connectionString, DbSyncProvider peerProvider)
        {
            const int TombstoneAgingInHours = 100;

            SqlConnection connection = new SqlConnection(connectionString);
            peerProvider.Connection = connection;

            //
            // 1. Create sync adapter for each sync table and attach it to the provider
            // Following DataAdapter style in ADO.NET, DbSyncAdapter is the equivelent for sync.
            // The code below shows how to create DbSyncAdapter objects for orders
            // and order_details tables using stored procedures stored on the database.
            //

            peerProvider.ScopeName = "Sales";

            // orders table
            DbSyncAdapter adaptorOrders = new DbSyncAdapter("orders");
            adaptorOrders.RowIdColumns.Add("order_id");

            // select incremental changes command
            SqlCommand chgsOrdersCmd = new SqlCommand();
            chgsOrdersCmd.CommandType = CommandType.StoredProcedure;
            chgsOrdersCmd.CommandText = "sp_orders_selectchanges";
            chgsOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);

            adaptorOrders.SelectIncrementalChangesCommand = chgsOrdersCmd;

            // insert row command
            SqlCommand insOrdersCmd = new SqlCommand();
            insOrdersCmd.CommandType = CommandType.StoredProcedure;
            insOrdersCmd.CommandText = "sp_orders_applyinsert";
            insOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insOrdersCmd.Parameters.Add("@order_date", SqlDbType.DateTime);
            insOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.InsertCommand = insOrdersCmd;

            // update row command
            SqlCommand updOrdersCmd = new SqlCommand();
            updOrdersCmd.CommandType = CommandType.StoredProcedure;
            updOrdersCmd.CommandText = "sp_orders_applyupdate";
            updOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updOrdersCmd.Parameters.Add("@order_date", SqlDbType.DateTime);
            updOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);
            updOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.UpdateCommand = updOrdersCmd;

            // delete row command
            SqlCommand delOrdersCmd = new SqlCommand();
            delOrdersCmd.CommandType = CommandType.StoredProcedure;
            delOrdersCmd.CommandText = "sp_orders_applydelete";
            delOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);
            delOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.DeleteCommand = delOrdersCmd;

            // get row command
            SqlCommand selRowOrdersCmd = new SqlCommand();
            selRowOrdersCmd.CommandType = CommandType.StoredProcedure;
            selRowOrdersCmd.CommandText = "sp_orders_selectrow";
            selRowOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);

            adaptorOrders.SelectRowCommand = selRowOrdersCmd;

            // insert row metadata command
            SqlCommand insMetadataOrdersCmd = new SqlCommand();
            insMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            insMetadataOrdersCmd.CommandText = "sp_orders_insertmetadata";
            insMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.InsertMetadataCommand = insMetadataOrdersCmd;

            // update row metadata command
            SqlCommand updMetadataOrdersCmd = new SqlCommand();
            updMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            updMetadataOrdersCmd.CommandText = "sp_orders_updatemetadata";
            updMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.UpdateMetadataCommand = updMetadataOrdersCmd;

            // delete row metadata command
            SqlCommand delMetadataOrdersCmd = new SqlCommand();
            delMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
            delMetadataOrdersCmd.CommandText = "sp_orders_deletemetadata";
            delMetadataOrdersCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataOrdersCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrders.DeleteMetadataCommand = delMetadataOrdersCmd;

            // get tombstones for cleanup
            SqlCommand selTombstonesOrdersCmd = new SqlCommand();
            selTombstonesOrdersCmd.CommandType = CommandType.StoredProcedure;
            selTombstonesOrdersCmd.CommandText = "sp_orders_selecttombstones";
            selTombstonesOrdersCmd.Parameters.Add("@tombstone_aging_in_hours", SqlDbType.Int).Value = TombstoneAgingInHours;

            adaptorOrders.SelectMetadataForCleanupCommand = selTombstonesOrdersCmd;

            peerProvider.SyncAdapters.Add(adaptorOrders);

            // order_details table
            DbSyncAdapter adaptorOrderDetails = new DbSyncAdapter("order_details");
            adaptorOrderDetails.RowIdColumns.Add("order_id");

            // select incremental inserts command
            SqlCommand chgsOrderDetailsCmd = new SqlCommand();
            chgsOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            chgsOrderDetailsCmd.CommandText = "sp_order_details_selectchanges";
            chgsOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);

            adaptorOrderDetails.SelectIncrementalChangesCommand = chgsOrderDetailsCmd;

            // insert row command
            SqlCommand insOrderDetailsCmd = new SqlCommand();
            insOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            insOrderDetailsCmd.CommandText = "sp_order_details_applyinsert";
            insOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@order_details_id", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@product", SqlDbType.VarChar, 100);
            insOrderDetailsCmd.Parameters.Add("@quantity", SqlDbType.Int);
            insOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.InsertCommand = insOrderDetailsCmd;

            // update row command
            SqlCommand updOrderDetailsCmd = new SqlCommand();
            updOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            updOrderDetailsCmd.CommandText = "sp_order_details_applyupdate";
            updOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@order_details_id", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@product", SqlDbType.VarChar, 100);
            updOrderDetailsCmd.Parameters.Add("@quantity", SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);
            updOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.UpdateCommand = updOrderDetailsCmd;

            // delete row command
            SqlCommand delOrderDetailsCmd = new SqlCommand();
            delOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            delOrderDetailsCmd.CommandText = "sp_order_details_applydelete";
            delOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);
            delOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.DeleteCommand = delOrderDetailsCmd;

            // get row command
            SqlCommand selRowOrderDetailsCmd = new SqlCommand();
            selRowOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            selRowOrderDetailsCmd.CommandText = "sp_order_details_selectrow";
            selRowOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);

            adaptorOrderDetails.SelectRowCommand = selRowOrderDetailsCmd;

            // insert row metadata command
            SqlCommand insMetadataOrderDetailsCmd = new SqlCommand();
            insMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            insMetadataOrderDetailsCmd.CommandText = "sp_order_details_insertmetadata";
            insMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.InsertMetadataCommand = insMetadataOrderDetailsCmd;

            // update row metadata command
            SqlCommand updMetadataOrderDetailsCmd = new SqlCommand();
            updMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            updMetadataOrderDetailsCmd.CommandText = "sp_order_details_updatemetadata";
            updMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.UpdateMetadataCommand = updMetadataOrderDetailsCmd;

            // delete row metadata command
            SqlCommand delMetadataOrderDetailsCmd = new SqlCommand();
            delMetadataOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            delMetadataOrderDetailsCmd.CommandText = "sp_order_details_deletemetadata";
            delMetadataOrderDetailsCmd.Parameters.Add("@order_id", SqlDbType.Int);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataOrderDetailsCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adaptorOrderDetails.DeleteMetadataCommand = delMetadataOrderDetailsCmd;

            // get tombstones for cleanup
            SqlCommand selTombstonesOrderDetailsCmd = new SqlCommand();
            selTombstonesOrderDetailsCmd.CommandType = CommandType.StoredProcedure;
            selTombstonesOrderDetailsCmd.CommandText = "sp_order_details_selecttombstones";
            selTombstonesOrderDetailsCmd.Parameters.Add("@tombstone_aging_in_hours", SqlDbType.Int).Value = TombstoneAgingInHours;

            adaptorOrderDetails.SelectMetadataForCleanupCommand = selTombstonesOrderDetailsCmd;

            peerProvider.SyncAdapters.Add(adaptorOrderDetails);

            //
            // 2. Setup provider wide commands
            // There are few commands on the provider itself and not on a table sync adapter:
            // SelectNewTimestampCommand: Returns the new high watermark for current sync
            // SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge and scope version (timestamp)
            // UpdateScopeInfoCommand: Sets the new values for sync knowledge and cleanup knowledge
            //

            SqlCommand anchorCmd = new SqlCommand();
            anchorCmd.CommandType = CommandType.Text;
            anchorCmd.CommandText = "select @" + DbSyncSession.SyncNewTimestamp + " = @@DBTS";  // for SQL Server 2005 SP2, use "min_active_rowversion() - 1"
            anchorCmd.Parameters.Add("@" + DbSyncSession.SyncNewTimestamp, SqlDbType.BigInt).Direction = ParameterDirection.Output;

            peerProvider.SelectNewTimestampCommand = anchorCmd;

            //
            // Select local replica info
            //
            SqlCommand selReplicaInfoCmd = new SqlCommand();
            selReplicaInfoCmd.CommandType = CommandType.Text;
            selReplicaInfoCmd.CommandText = "select " +
                                            "@" + DbSyncSession.SyncScopeId + " = scope_Id, " +
                                            "@" + DbSyncSession.SyncScopeKnowledge + " = scope_sync_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeCleanupKnowledge + " = scope_tombstone_cleanup_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeTimestamp + " = scope_timestamp " +
                                            "from scope_info " +
                                            "where scope_name = @" + DbSyncSession.SyncScopeName;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier).Direction = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000).Direction = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000).Direction = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt).Direction = ParameterDirection.Output;
            peerProvider.SelectScopeInfoCommand = selReplicaInfoCmd;

            //
            // Update local replica info
            //
            SqlCommand updReplicaInfoCmd = new SqlCommand();
            updReplicaInfoCmd.CommandType = CommandType.Text;
            updReplicaInfoCmd.CommandText = "update  scope_info set " +
                                            "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " +
                                            "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                            "where scope_name = @" + DbSyncSession.SyncScopeName + " ;" +
                                            "set @" + DbSyncSession.SyncRowCount + " = @@rowcount";
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            peerProvider.UpdateScopeInfoCommand = updReplicaInfoCmd;

            //
            // 3. Track sync process
            //
            peerProvider.SyncProgress += new EventHandler<DbSyncProgressEventArgs>(ShowProgress);
            peerProvider.ApplyChangeFailed += new EventHandler<DbApplyChangeFailedEventArgs>(ShowFailures);

            return peerProvider;
        }
        /// <summary>
        /// Create a response message content based on a requested index in a server batch info
        /// </summary>
        private HttpMessageSendChangesResponse GetChangesResponse(SyncContext syncContext, long remoteClientTimestamp, BatchInfo serverBatchInfo,
                                                                  DatabaseChangesSelected serverChangesSelected, int batchIndexRequested, ConflictResolutionPolicy policy)
        {
            // 1) Create the http message content response
            var changesResponse = new HttpMessageSendChangesResponse(syncContext);

            changesResponse.ChangesSelected          = serverChangesSelected;
            changesResponse.ServerStep               = HttpStep.GetChanges;
            changesResponse.ConflictResolutionPolicy = policy;

            // If nothing to do, just send back
            if (serverBatchInfo.InMemory || serverBatchInfo.BatchPartsInfo.Count == 0)
            {
                if (this.ClientConverter != null && serverBatchInfo.InMemoryData.HasRows)
                {
                    BeforeSerializeRows(serverBatchInfo.InMemoryData, this.ClientConverter);
                }

                changesResponse.Changes               = serverBatchInfo.InMemoryData.GetContainerSet();
                changesResponse.BatchIndex            = 0;
                changesResponse.IsLastBatch           = true;
                changesResponse.RemoteClientTimestamp = remoteClientTimestamp;
                return(changesResponse);
            }

            // Get the batch part index requested
            var batchPartInfo = serverBatchInfo.BatchPartsInfo.First(d => d.Index == batchIndexRequested);

            // if we are not in memory, we set the BI in session, to be able to get it back on next request

            // create the in memory changes set
            var changesSet = new SyncSet(Schema.ScopeName);

            foreach (var table in Schema.Tables)
            {
                DbSyncAdapter.CreateChangesTable(Schema.Tables[table.TableName, table.SchemaName], changesSet);
            }

            batchPartInfo.LoadBatch(changesSet, serverBatchInfo.GetDirectoryFullPath());

            // if client request a conversion on each row, apply the conversion
            if (this.ClientConverter != null && batchPartInfo.Data.HasRows)
            {
                BeforeSerializeRows(batchPartInfo.Data, this.ClientConverter);
            }

            changesResponse.Changes = batchPartInfo.Data.GetContainerSet();

            changesResponse.BatchIndex            = batchIndexRequested;
            changesResponse.IsLastBatch           = batchPartInfo.IsLastBatch;
            changesResponse.RemoteClientTimestamp = remoteClientTimestamp;
            changesResponse.ServerStep            = batchPartInfo.IsLastBatch ? HttpStep.GetChanges : HttpStep.GetChangesInProgress;

            // If we have only one bpi, we can safely delete it
            if (batchPartInfo.IsLastBatch)
            {
                // delete the folder (not the BatchPartInfo, because we have a reference on it)
                if (this.Options.CleanFolder)
                {
                    var shouldDeleteFolder = true;
                    if (!string.IsNullOrEmpty(this.Options.SnapshotsDirectory))
                    {
                        var dirInfo  = new DirectoryInfo(serverBatchInfo.DirectoryRoot);
                        var snapInfo = new DirectoryInfo(this.Options.SnapshotsDirectory);
                        shouldDeleteFolder = dirInfo.FullName != snapInfo.FullName;
                    }

                    if (shouldDeleteFolder)
                    {
                        serverBatchInfo.TryRemoveDirectory();
                    }
                }
            }

            return(changesResponse);
        }
        /// <summary>
        /// Get changes from
        /// </summary>
        internal async Task <HttpMessageSendChangesResponse> ApplyThenGetChangesAsync(
            HttpMessageSendChangesRequest httpMessage, SessionCache sessionCache, int clientBatchSize, CancellationToken cancellationToken)
        {
            // Get if we need to serialize data or making everything in memory
            var clientWorkInMemory = clientBatchSize == 0;

            // Check schema.
            // If client has stored the schema, the EnsureScope will not be called on server.
            if (this.Schema == null || !this.Schema.HasTables || !this.Schema.HasColumns)
            {
                var(_, newSchema) = await this.EnsureSchemaAsync(
                    httpMessage.SyncContext, this.Setup, cancellationToken).ConfigureAwait(false);

                newSchema.EnsureSchema();
                this.Schema = newSchema;
            }

            // ------------------------------------------------------------
            // FIRST STEP : receive client changes
            // ------------------------------------------------------------

            // We are receiving changes from client
            // BatchInfo containing all BatchPartInfo objects
            // Retrieve batchinfo instance if exists

            // Get batch info from session cache if exists, otherwise create it
            if (sessionCache.ClientBatchInfo == null)
            {
                sessionCache.ClientBatchInfo = new BatchInfo(clientWorkInMemory, Schema, this.Options.BatchDirectory);
            }

            // create the in memory changes set
            var changesSet = new SyncSet(Schema.ScopeName);

            foreach (var table in httpMessage.Changes.Tables)
            {
                DbSyncAdapter.CreateChangesTable(Schema.Tables[table.TableName, table.SchemaName], changesSet);
            }

            changesSet.ImportContainerSet(httpMessage.Changes, false);

            // If client has made a conversion on each line, apply the reverse side of it
            if (this.ClientConverter != null && changesSet.HasRows)
            {
                AfterDeserializedRows(changesSet, this.ClientConverter);
            }

            // add changes to the batch info
            sessionCache.ClientBatchInfo.AddChanges(changesSet, httpMessage.BatchIndex, httpMessage.IsLastBatch);


            // Clear the httpMessage set
            if (!clientWorkInMemory && httpMessage.Changes != null)
            {
                httpMessage.Changes.Clear();
            }

            // Until we don't have received all the batches, wait for more
            if (!httpMessage.IsLastBatch)
            {
                return new HttpMessageSendChangesResponse(httpMessage.SyncContext)
                       {
                           ServerStep = HttpStep.SendChangesInProgress
                       }
            }
            ;

            // ------------------------------------------------------------
            // SECOND STEP : apply then return server changes
            // ------------------------------------------------------------

            // get changes
            var(context, remoteClientTimestamp, serverBatchInfo, policy, serverChangesSelected) =
                await this.ApplyThenGetChangesAsync(httpMessage.SyncContext,
                                                    httpMessage.Scope, this.Schema, sessionCache.ClientBatchInfo, this.Options.DisableConstraintsOnApplyChanges,
                                                    this.Options.UseBulkOperations, false, this.Options.CleanFolder, clientBatchSize,
                                                    this.Options.BatchDirectory, this.Options.ConflictResolutionPolicy, cancellationToken).ConfigureAwait(false);


            // Save the server batch info object to cache if not working in memory
            if (!clientWorkInMemory)
            {
                sessionCache.RemoteClientTimestamp = remoteClientTimestamp;

                sessionCache.ServerBatchInfo       = serverBatchInfo;
                sessionCache.ServerChangesSelected = serverChangesSelected;
            }

            // Get the firt response to send back to client
            return(GetChangesResponse(context, remoteClientTimestamp, serverBatchInfo, serverChangesSelected, 0, policy));
        }
        /// <summary>
        /// Configure the Oracle DbSyncprovider. Usual configuration similar to OCS V2 samples.
        /// </summary>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public OracleDbSyncProvider ConfigureDBSyncProvider(string connectionString)
        {
            OracleDbSyncProvider provider = new OracleDbSyncProvider();

            provider.ScopeName  = SyncUtils.ScopeName;
            provider.Connection = new OracleConnection();
            provider.Connection.ConnectionString = connectionString;

            for (int i = 0; i < SyncUtils.SyncAdapterTables.Length; i++)
            {
                //Add each table as a DbSyncAdapter to the provider
                DbSyncAdapter adapter = new DbSyncAdapter(SyncUtils.SyncAdapterTables[i]);
                adapter.RowIdColumns.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i]);

                // select incremental changes command
                OracleCommand chgsOrdersCmd = new OracleCommand();
                chgsOrdersCmd.CommandType = CommandType.StoredProcedure;
                chgsOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_selectchanges";
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncMetadataOnly, OracleType.Int32);
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, OracleType.Number);
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, OracleType.Int32);
                chgsOrdersCmd.Parameters.Add("sync_changes", OracleType.Cursor).Direction = ParameterDirection.Output;

                adapter.SelectIncrementalChangesCommand = chgsOrdersCmd;


                // insert row command
                OracleCommand insOrdersCmd = new OracleCommand();
                insOrdersCmd.CommandType = CommandType.StoredProcedure;
                insOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applyinsert";
                insOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                if (SyncUtils.SyncAdapterTables[i] == "orders")
                {
                    insOrdersCmd.Parameters.Add("order_date", OracleType.DateTime);
                }
                else
                {
                    insOrdersCmd.Parameters.Add("product", OracleType.NVarChar, 100);
                    insOrdersCmd.Parameters.Add("quantity", OracleType.Int32);
                    insOrdersCmd.Parameters.Add("order_id", OracleType.Int32);
                }
                insOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;

                adapter.InsertCommand = insOrdersCmd;


                // update row command
                OracleCommand updOrdersCmd = new OracleCommand();
                updOrdersCmd.CommandType = CommandType.StoredProcedure;
                updOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applyupdate";
                updOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                if (SyncUtils.SyncAdapterTables[i] == "orders")
                {
                    updOrdersCmd.Parameters.Add("order_date", OracleType.DateTime);
                }
                else
                {
                    updOrdersCmd.Parameters.Add("product", OracleType.NVarChar, 100);
                    updOrdersCmd.Parameters.Add("quantity", OracleType.Int32);
                    updOrdersCmd.Parameters.Add("order_id", OracleType.Int32);
                }
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, OracleType.Number);
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncForceWrite, OracleType.Int32);

                adapter.UpdateCommand = updOrdersCmd;


                // delete row command
                OracleCommand delOrdersCmd = new OracleCommand();
                delOrdersCmd.CommandType = CommandType.StoredProcedure;
                delOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applydelete";
                delOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, OracleType.Number);
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncForceWrite, OracleType.Int32);

                adapter.DeleteCommand = delOrdersCmd;

                // get row command
                OracleCommand selRowOrdersCmd = new OracleCommand();
                selRowOrdersCmd.CommandType = CommandType.StoredProcedure;
                selRowOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_selectrow";
                selRowOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                selRowOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, OracleType.Int32);
                selRowOrdersCmd.Parameters.Add("selectedRow", OracleType.Cursor).Direction = ParameterDirection.Output;

                adapter.SelectRowCommand = selRowOrdersCmd;


                // insert row metadata command
                OracleCommand insMetadataOrdersCmd = new OracleCommand();
                insMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                insMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_insert_md";
                insMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, OracleType.Number);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerKey, OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerTimestamp, OracleType.Number);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerKey, OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerTimestamp, OracleType.Number);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowIsTombstone, OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, OracleType.Int32);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;

                adapter.InsertMetadataCommand = insMetadataOrdersCmd;


                // update row metadata command
                OracleCommand updMetadataOrdersCmd = new OracleCommand();
                updMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                updMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_update_md";
                updMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, OracleType.Number);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerKey, OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerTimestamp, OracleType.Number);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerKey, OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerTimestamp, OracleType.Number);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowIsTombstone, OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, OracleType.Int32);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;

                adapter.UpdateMetadataCommand = (IDbCommand)updMetadataOrdersCmd.Clone();

                // delete row metadata command
                OracleCommand delMetadataOrdersCmd = new OracleCommand();
                delMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                delMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_delete_md";
                delMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], OracleType.Int32);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, OracleType.Int32);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, OracleType.Number);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;

                adapter.DeleteMetadataCommand = delMetadataOrdersCmd;


                // get tombstones for cleanup
                OracleCommand selTombstonesOrdersCmd = new OracleCommand();
                selTombstonesOrdersCmd.CommandType = CommandType.StoredProcedure;
                selTombstonesOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_select_ts";
                selTombstonesOrdersCmd.Parameters.Add("tombstone_aging_in_hours", OracleType.Int32).Value = SyncUtils.TombstoneAgingInHours;
                selTombstonesOrdersCmd.Parameters.Add("sync_scope_local_id", OracleType.Int32);

                adapter.SelectMetadataForCleanupCommand = selTombstonesOrdersCmd;

                provider.SyncAdapters.Add(adapter);
            }

            // 2. Setup provider wide commands
            // There are few commands on the provider itself and not on a table sync adapter:
            // SelectNewTimestampCommand: Returns the new high watermark for current sync
            // SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge and scope version (timestamp)
            // UpdateScopeInfoCommand: Sets the new values for sync knowledge and cleanup knowledge
            //

            OracleCommand anchorCmd = new OracleCommand();

            anchorCmd.CommandType = CommandType.StoredProcedure;
            anchorCmd.CommandText = "SP_GET_TIMESTAMP";  // for SQL Server 2005 SP2, use "min_active_rowversion() - 1"
            anchorCmd.Parameters.Add(DbSyncSession.SyncNewTimestamp, OracleType.UInt32).Direction = ParameterDirection.Output;

            provider.SelectNewTimestampCommand = anchorCmd;

            //
            // Select local replica info
            //
            OracleCommand selReplicaInfoCmd = new OracleCommand();

            selReplicaInfoCmd.CommandType = CommandType.Text;
            selReplicaInfoCmd.CommandText = "select " +
                                            "scope_id, " +
                                            "scope_local_id, " +
                                            "scope_sync_knowledge, " +
                                            "scope_forgotten_knowledge, " +
                                            "scope_timestamp " +
                                            "from scope_info " +
                                            "where scope_name = :" + DbSyncSession.SyncScopeName;
            selReplicaInfoCmd.Parameters.Clear();
            selReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeName, OracleType.NVarChar).Direction = ParameterDirection.Input;
            provider.SelectScopeInfoCommand = selReplicaInfoCmd;

            //
            // Update local replica info
            //
            OracleCommand updReplicaInfoCmd = new OracleCommand();

            updReplicaInfoCmd.CommandType = CommandType.Text;
            updReplicaInfoCmd.CommandText = "begin update scope_info set " +
                                            "scope_sync_knowledge = :" + DbSyncSession.SyncScopeKnowledge + ", " +
                                            "scope_id = :" + DbSyncSession.SyncScopeId + ", " +
                                            "scope_forgotten_knowledge = :" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                            "where scope_name = :" + DbSyncSession.SyncScopeName + " and " +
                                            " ( :" + DbSyncSession.SyncCheckConcurrency + " = 0 or scope_timestamp = :" + DbSyncSession.SyncScopeTimestamp + "); " +
                                            ":" + DbSyncSession.SyncRowCount + " := sql%rowcount; End;";
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeKnowledge, OracleType.Raw, 10000);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeCleanupKnowledge, OracleType.Raw, 10000);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeName, OracleType.NVarChar, 100);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, OracleType.Int32);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeId, OracleType.Raw);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeTimestamp, OracleType.Number);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;
            provider.UpdateScopeInfoCommand = updReplicaInfoCmd;

            //
            // Select overlapping scopes
            //
            // get tombstones for cleanup
            OracleCommand overlappingScopesCmd = new OracleCommand();

            overlappingScopesCmd.CommandType = CommandType.StoredProcedure;
            overlappingScopesCmd.CommandText = "sp_select_shared_scopes";
            overlappingScopesCmd.Parameters.Add(":" + DbSyncSession.SyncScopeName, OracleType.NVarChar, 100);
            provider.SelectOverlappingScopesCommand = overlappingScopesCmd;

            //
            // Update table cleanup info
            //
            OracleCommand updScopeCleanupInfoCmd = new OracleCommand();

            updScopeCleanupInfoCmd.CommandType = CommandType.Text;
            updScopeCleanupInfoCmd.CommandText = "update  scope_info set " +
                                                 "scope_cleanup_timestamp = :" + DbSyncSession.SyncScopeCleanupTimestamp + " " +
                                                 "where scope_name = :" + DbSyncSession.SyncScopeName + " and " +
                                                 "(scope_cleanup_timestamp is null or scope_cleanup_timestamp <  :" + DbSyncSession.SyncScopeCleanupTimestamp + ");" +
                                                 "set :" + DbSyncSession.SyncRowCount + " = ::rowcount";
            updScopeCleanupInfoCmd.Parameters.Add(":" + DbSyncSession.SyncScopeCleanupTimestamp, OracleType.Number);
            updScopeCleanupInfoCmd.Parameters.Add(":" + DbSyncSession.SyncScopeName, OracleType.NVarChar, 100);
            updScopeCleanupInfoCmd.Parameters.Add(":" + DbSyncSession.SyncRowCount, OracleType.Int32).Direction = ParameterDirection.Output;
            provider.UpdateScopeCleanupTimestampCommand = updScopeCleanupInfoCmd;

            //Register the BatchSpooled and BatchApplied events. These are fired when a provider is either enumerating or applying changes in batches.
            provider.BatchApplied += new EventHandler <DbBatchAppliedEventArgs>(provider_BatchApplied);
            provider.BatchSpooled += new EventHandler <DbBatchSpooledEventArgs>(provider_BatchSpooled);

            return(provider);
        }
Пример #11
0
        InternalGetSnapshotAsync(ServerScopeInfo serverScopeInfo, SyncContext context, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ScopeLoading, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            // Make a remote call to get Schema from remote provider
            if (serverScopeInfo.Schema == null)
            {
                (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(
                    context, null, false, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                serverScopeInfo.Schema.EnsureSchema();
            }

            // Generate a batch directory
            var batchDirectoryRoot     = this.Options.BatchDirectory;
            var batchDirectoryName     = string.Concat(DateTime.UtcNow.ToString("yyyy_MM_dd_ss"), Path.GetRandomFileName().Replace(".", ""));
            var batchDirectoryFullPath = Path.Combine(batchDirectoryRoot, batchDirectoryName);

            if (!Directory.Exists(batchDirectoryFullPath))
            {
                Directory.CreateDirectory(batchDirectoryFullPath);
            }

            // Create the BatchInfo serialized (forced because in a snapshot call, so we are obviously serialized on disk)
            var serverBatchInfo = new BatchInfo(serverScopeInfo.Schema, batchDirectoryRoot, batchDirectoryName);

            // Firstly, get the snapshot summary
            var changesToSend = new HttpMessageSendChangesRequest(context, null);
            var serializer    = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>();
            var binaryData    = await serializer.SerializeAsync(changesToSend);

            var response0 = await this.httpRequestHandler.ProcessRequestAsync(
                this.HttpClient, context, this.ServiceUri, binaryData, HttpStep.GetSummary,
                this.SerializerFactory, this.Converter, 0, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);

            HttpMessageSummaryResponse summaryResponseContent = null;

            using (var streamResponse = await response0.Content.ReadAsStreamAsync().ConfigureAwait(false))
            {
                var responseSerializer = this.SerializerFactory.GetSerializer <HttpMessageSummaryResponse>();

                if (streamResponse.CanRead)
                {
                    summaryResponseContent = await responseSerializer.DeserializeAsync(streamResponse);

                    serverBatchInfo.RowsCount = summaryResponseContent.BatchInfo?.RowsCount ?? 0;

                    if (summaryResponseContent.BatchInfo?.BatchPartsInfo != null)
                    {
                        foreach (var bpi in summaryResponseContent.BatchInfo.BatchPartsInfo)
                        {
                            serverBatchInfo.BatchPartsInfo.Add(bpi);
                        }
                    }
                }
            }

            if (summaryResponseContent == null)
            {
                throw new Exception("Summary can't be null");
            }

            context = summaryResponseContent.SyncContext;

            // no snapshot
            if ((serverBatchInfo.BatchPartsInfo == null || serverBatchInfo.BatchPartsInfo.Count <= 0) && serverBatchInfo.RowsCount <= 0)
            {
                return(context, 0, null, new DatabaseChangesSelected());
            }

            // If we have a snapshot we are raising the batches downloading process that will occurs
            await this.InterceptAsync(new HttpBatchesDownloadingArgs(context, serverBatchInfo, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

            await serverBatchInfo.BatchPartsInfo.ForEachAsync(async bpi =>
            {
                var changesToSend3 = new HttpMessageGetMoreChangesRequest(context, bpi.Index);
                var serializer3    = this.SerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>();
                var binaryData3    = await serializer3.SerializeAsync(changesToSend3);

                await this.InterceptAsync(new HttpGettingServerChangesRequestArgs(bpi.Index, serverBatchInfo.BatchPartsInfo.Count, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

                var response = await this.httpRequestHandler.ProcessRequestAsync(
                    this.HttpClient, context, this.ServiceUri, binaryData3, HttpStep.GetMoreChanges,
                    this.SerializerFactory, this.Converter, 0, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false);

                if (this.SerializerFactory.Key != "json")
                {
                    var s = this.SerializerFactory.GetSerializer <HttpMessageSendChangesResponse>();
                    using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                    var getMoreChanges       = await s.DeserializeAsync(responseStream);

                    if (getMoreChanges != null && getMoreChanges.Changes != null && getMoreChanges.Changes.HasRows)
                    {
                        var localSerializer = new LocalJsonSerializer();

                        var interceptorsWriting = this.interceptors.GetInterceptors <SerializingRowArgs>();
                        if (interceptorsWriting.Count > 0)
                        {
                            localSerializer.OnWritingRow(async(syncTable, rowArray) =>
                            {
                                var args = new SerializingRowArgs(context, syncTable, rowArray);
                                await this.InterceptAsync(args, progress, cancellationToken).ConfigureAwait(false);
                                return(args.Result);
                            });
                        }
                        // Should have only one table
                        var table       = getMoreChanges.Changes.Tables[0];
                        var schemaTable = DbSyncAdapter.CreateChangesTable(serverScopeInfo.Schema.Tables[table.TableName, table.SchemaName]);

                        var fullPath = Path.Combine(batchDirectoryFullPath, bpi.FileName);

                        // open the file and write table header
                        await localSerializer.OpenFileAsync(fullPath, schemaTable).ConfigureAwait(false);

                        foreach (var row in table.Rows)
                        {
                            await localSerializer.WriteRowToFileAsync(new SyncRow(schemaTable, row), schemaTable).ConfigureAwait(false);
                        }

                        // Close file
                        await localSerializer.CloseFileAsync(fullPath, schemaTable).ConfigureAwait(false);
                    }
                }
                else
                {
                    // Serialize
                    await SerializeAsync(response, bpi.FileName, batchDirectoryFullPath, this).ConfigureAwait(false);
                }


                // Raise response from server containing a batch changes
                await this.InterceptAsync(new HttpGettingServerChangesResponseArgs(serverBatchInfo, bpi.Index, bpi.RowsCount, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);
            });

            await this.InterceptAsync(new HttpBatchesDownloadedArgs(summaryResponseContent, summaryResponseContent.SyncContext, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false);

            return(context, summaryResponseContent.RemoteClientTimestamp, serverBatchInfo, summaryResponseContent.ServerChangesSelected);
        }
        public DbSyncProvider SetupSyncProvider(string scopeName, string peerConnString)
        {
            const int MetadataAgingInHours = 100;

            DbSyncProvider peerProvider = new DbSyncProvider();

            SqlConnection peerConnection = new SqlConnection(peerConnString);

            peerProvider.Connection = peerConnection;
            peerProvider.ScopeName  = scopeName;


            DbSyncAdapter adapterCustomer = new DbSyncAdapter("Customer");

            adapterCustomer.RowIdColumns.Add("CustomerId");


            SqlCommand chgsCustomerCmd = new SqlCommand();

            chgsCustomerCmd.CommandType = CommandType.StoredProcedure;
            chgsCustomerCmd.CommandText = "Sales.sp_Customer_SelectChanges";
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMetadataOnly, SqlDbType.Int);
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            chgsCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncInitialize, SqlDbType.Int);

            adapterCustomer.SelectIncrementalChangesCommand = chgsCustomerCmd;


            SqlCommand insCustomerCmd = new SqlCommand();

            insCustomerCmd.CommandType = CommandType.StoredProcedure;
            insCustomerCmd.CommandText = "Sales.sp_Customer_ApplyInsert";
            insCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            insCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            insCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.InsertCommand = insCustomerCmd;


            SqlCommand updCustomerCmd = new SqlCommand();

            updCustomerCmd.CommandType = CommandType.StoredProcedure;
            updCustomerCmd.CommandText = "Sales.sp_Customer_ApplyUpdate";
            updCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            updCustomerCmd.Parameters.Add("@CustomerName", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@SalesPerson", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@CustomerType", SqlDbType.NVarChar);
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;
            updCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncForceWrite, SqlDbType.Int);

            adapterCustomer.UpdateCommand = updCustomerCmd;


            SqlCommand delCustomerCmd = new SqlCommand();

            delCustomerCmd.CommandType = CommandType.StoredProcedure;
            delCustomerCmd.CommandText = "Sales.sp_Customer_ApplyDelete";
            delCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncMinTimestamp, SqlDbType.BigInt);
            delCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.DeleteCommand = delCustomerCmd;


            SqlCommand selRowCustomerCmd = new SqlCommand();

            selRowCustomerCmd.CommandType = CommandType.StoredProcedure;
            selRowCustomerCmd.CommandText = "Sales.sp_Customer_SelectRow";
            selRowCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);

            adapterCustomer.SelectRowCommand = selRowCustomerCmd;


            SqlCommand insMetadataCustomerCmd = new SqlCommand();

            insMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            insMetadataCustomerCmd.CommandText = "Sales.sp_Customer_InsertMetadata";
            insMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowIsTombstone, SqlDbType.Int);
            insMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.InsertMetadataCommand = insMetadataCustomerCmd;


            SqlCommand updMetadataCustomerCmd = new SqlCommand();

            updMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            updMetadataCustomerCmd.CommandText = "Sales.sp_Customer_UpdateMetadata";
            updMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerKey, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCreatePeerTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerKey, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncUpdatePeerTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            updMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.UpdateMetadataCommand = updMetadataCustomerCmd;


            SqlCommand delMetadataCustomerCmd = new SqlCommand();

            delMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            delMetadataCustomerCmd.CommandText = "Sales.sp_Customer_DeleteMetadata";
            delMetadataCustomerCmd.Parameters.Add("@CustomerId", SqlDbType.UniqueIdentifier);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowTimestamp, SqlDbType.BigInt);
            delMetadataCustomerCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            adapterCustomer.DeleteMetadataCommand = delMetadataCustomerCmd;


            SqlCommand selMetadataCustomerCmd = new SqlCommand();

            selMetadataCustomerCmd.CommandType = CommandType.StoredProcedure;
            selMetadataCustomerCmd.CommandText = "Sales.sp_Customer_SelectMetadata";
            selMetadataCustomerCmd.Parameters.Add("@metadata_aging_in_hours", SqlDbType.Int).Value = MetadataAgingInHours;

            adapterCustomer.SelectMetadataForCleanupCommand = selMetadataCustomerCmd;

            peerProvider.SyncAdapters.Add(adapterCustomer);

            SqlCommand selectNewTimestampCommand = new SqlCommand();
            string     newTimestampVariable      = "@" + DbSyncSession.SyncNewTimestamp;

            selectNewTimestampCommand.CommandText = "SELECT " + newTimestampVariable + " = min_active_rowversion() - 1";
            selectNewTimestampCommand.Parameters.Add(newTimestampVariable, SqlDbType.Timestamp);
            selectNewTimestampCommand.Parameters[newTimestampVariable].Direction = ParameterDirection.Output;

            peerProvider.SelectNewTimestampCommand = selectNewTimestampCommand;


            SqlCommand selReplicaInfoCmd = new SqlCommand();

            selReplicaInfoCmd.CommandType = CommandType.Text;
            selReplicaInfoCmd.CommandText = "SELECT " +
                                            "@" + DbSyncSession.SyncScopeId + " = scope_id, " +
                                            "@" + DbSyncSession.SyncScopeKnowledge + " = scope_sync_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeCleanupKnowledge + " = scope_tombstone_cleanup_knowledge, " +
                                            "@" + DbSyncSession.SyncScopeTimestamp + " = scope_timestamp " +
                                            "FROM Sales.ScopeInfo " +
                                            "WHERE scope_name = @" + DbSyncSession.SyncScopeName;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeId, SqlDbType.UniqueIdentifier).Direction               = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000).Direction        = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000).Direction = ParameterDirection.Output;
            selReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt).Direction = ParameterDirection.Output;

            peerProvider.SelectScopeInfoCommand = selReplicaInfoCmd;


            SqlCommand updReplicaInfoCmd = new SqlCommand();

            updReplicaInfoCmd.CommandType = CommandType.Text;
            updReplicaInfoCmd.CommandText = "UPDATE  Sales.ScopeInfo SET " +
                                            "scope_sync_knowledge = @" + DbSyncSession.SyncScopeKnowledge + ", " +
                                            "scope_tombstone_cleanup_knowledge = @" + DbSyncSession.SyncScopeCleanupKnowledge + " " +
                                            "WHERE scope_name = @" + DbSyncSession.SyncScopeName + " AND " +
                                            " ( @" + DbSyncSession.SyncCheckConcurrency + " = 0 or scope_timestamp = @" + DbSyncSession.SyncScopeTimestamp + "); " +
                                            "SET @" + DbSyncSession.SyncRowCount + " = @@rowcount";
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeCleanupKnowledge, SqlDbType.VarBinary, 10000);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeName, SqlDbType.NVarChar, 100);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncCheckConcurrency, SqlDbType.Int);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncScopeTimestamp, SqlDbType.BigInt);
            updReplicaInfoCmd.Parameters.Add("@" + DbSyncSession.SyncRowCount, SqlDbType.Int).Direction = ParameterDirection.Output;

            peerProvider.UpdateScopeInfoCommand = updReplicaInfoCmd;

            return(peerProvider);
        }
        public FirebirdDbSyncProvider()
        {
            // We need to modify this particular column name because the default is longer than Firebird allows.
            this.ScopeForgottenKnowledgeColName = "scope_forgotten_knowledge";

            for (int i = 0; i < SyncUtils.SyncAdapterTables.Length; i++)
            {
                //Add each table as a DbSyncAdapter to the provider
                DbSyncAdapter adapter = new DbSyncAdapter(SyncUtils.SyncAdapterTables[i]);
                adapter.RowIdColumns.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i]);

                // select incremental changes command
                FbCommand chgsOrdersCmd = new FbCommand();
                chgsOrdersCmd.CommandType = CommandType.StoredProcedure;
                chgsOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_selectchanges";
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, FbDbType.Integer);                 // TODO: timestamp?
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncMetadataOnly, FbDbType.Integer);
                chgsOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, FbDbType.Integer);
                // chgsOrdersCmd.Parameters.Add("sync_changes", FbDbType.Cursor).Direction = ParameterDirection.Output;
                adapter.SelectIncrementalChangesCommand = chgsOrdersCmd;


                // insert row command
                FbCommand insOrdersCmd = new FbCommand();
                insOrdersCmd.CommandType = CommandType.StoredProcedure;
                insOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applyinsert";
                if (SyncUtils.SyncAdapterTables[i] == "order_details")
                {
                    insOrdersCmd.Parameters.Add("order_id", FbDbType.Integer);
                }
                insOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                if (SyncUtils.SyncAdapterTables[i] == "orders")
                {
                    insOrdersCmd.Parameters.Add("order_date", FbDbType.Date);
                }
                else
                {
                    insOrdersCmd.Parameters.Add("product", FbDbType.VarChar, 100);
                    insOrdersCmd.Parameters.Add("quantity", FbDbType.Integer);
                }
                insOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.InsertCommand = insOrdersCmd;


                // update row command
                FbCommand updOrdersCmd = new FbCommand();
                updOrdersCmd.CommandType = CommandType.StoredProcedure;
                updOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applyupdate";
                if (SyncUtils.SyncAdapterTables[i] == "order_details")
                {
                    updOrdersCmd.Parameters.Add("order_id", FbDbType.Integer);
                }
                updOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                if (SyncUtils.SyncAdapterTables[i] == "orders")
                {
                    updOrdersCmd.Parameters.Add("order_date", FbDbType.Date);
                }
                else
                {
                    updOrdersCmd.Parameters.Add("quantity", FbDbType.Integer);
                    updOrdersCmd.Parameters.Add("product", FbDbType.VarChar, 100);
                }
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncForceWrite, FbDbType.Integer);
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, FbDbType.Integer);
                updOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.UpdateCommand = updOrdersCmd;


                // delete row command
                FbCommand delOrdersCmd = new FbCommand();
                delOrdersCmd.CommandType = CommandType.StoredProcedure;
                delOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_applydelete";
                delOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncMinTimestamp, FbDbType.Integer);
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncForceWrite, FbDbType.Integer);
                delOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.DeleteCommand = delOrdersCmd;

                // get row command
                FbCommand selRowOrdersCmd = new FbCommand();
                selRowOrdersCmd.CommandType = CommandType.StoredProcedure;
                selRowOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_selectrow";
                selRowOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                selRowOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, FbDbType.Integer);
                // selRowOrdersCmd.Parameters.Add("selectedRow", FbDbType.Cursor).Direction = ParameterDirection.Output;
                adapter.SelectRowCommand = selRowOrdersCmd;


                // insert row metadata command
                FbCommand insMetadataOrdersCmd = new FbCommand();
                insMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                insMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_insert_md";
                insMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerKey, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerTimestamp, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerKey, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerTimestamp, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowIsTombstone, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, FbDbType.Integer);
                insMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.InsertMetadataCommand = insMetadataOrdersCmd;

                // update row metadata command
                FbCommand updMetadataOrdersCmd = new FbCommand();
                updMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                updMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_update_md";
                updMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncScopeLocalId, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowIsTombstone, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerKey, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCreatePeerTimestamp, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerKey, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncUpdatePeerTimestamp, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, FbDbType.Integer);
                updMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.UpdateMetadataCommand = updMetadataOrdersCmd;

                // delete row metadata command
                FbCommand delMetadataOrdersCmd = new FbCommand();
                delMetadataOrdersCmd.CommandType = CommandType.StoredProcedure;
                delMetadataOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_delete_md";
                delMetadataOrdersCmd.Parameters.Add(SyncUtils.SyncAdapterTablePrimaryKeys[i], FbDbType.Integer);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowTimestamp, FbDbType.Integer);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, FbDbType.Integer);
                delMetadataOrdersCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
                adapter.DeleteMetadataCommand = delMetadataOrdersCmd;


                // get tombstones for cleanup
                FbCommand selTombstonesOrdersCmd = new FbCommand();
                selTombstonesOrdersCmd.CommandType = CommandType.StoredProcedure;
                selTombstonesOrdersCmd.CommandText = "sp_" + SyncUtils.SyncAdapterTables[i] + "_select_ts";
                selTombstonesOrdersCmd.Parameters.Add("tombstone_aging_in_hours", FbDbType.Integer).Value = SyncUtils.TombstoneAgingInHours;
                selTombstonesOrdersCmd.Parameters.Add("sync_scope_local_id", FbDbType.Integer);
                adapter.SelectMetadataForCleanupCommand = selTombstonesOrdersCmd;

                SyncAdapters.Add(adapter);
            }

            // 2. Setup provider wide commands
            // There are few commands on the provider itself and not on a table sync adapter:
            // SelectNewTimestampCommand: Returns the new high watermark for current sync
            // SelectScopeInfoCommand: Returns sync knowledge, cleanup knowledge and scope version (timestamp)
            // UpdateScopeInfoCommand: Sets the new values for sync knowledge and cleanup knowledge
            //

            FbCommand anchorCmd = new FbCommand();

            anchorCmd.CommandType = CommandType.StoredProcedure;
            anchorCmd.CommandText = "SP_GET_TIMESTAMP";                                                                       // for SQL Server 2005 SP2, use "min_active_rowversion() - 1"
            anchorCmd.Parameters.Add(DbSyncSession.SyncNewTimestamp, FbDbType.Integer).Direction = ParameterDirection.Output; // TODO: Should this be BigInt

            SelectNewTimestampCommand = anchorCmd;

            //
            // Select local replica info
            //
            RecreateSelectScope();

            //
            // Update local replica info
            //
            FbCommand updReplicaInfoCmd = new FbCommand();

            updReplicaInfoCmd.CommandType = CommandType.StoredProcedure;
            updReplicaInfoCmd.CommandText = "SP_UPDATE_SCOPE_INFO";
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeKnowledge, FbDbType.Binary, 10000);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeId, FbDbType.Guid);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeCleanupKnowledge, FbDbType.Binary, 10000);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeName, FbDbType.VarChar, 100);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncCheckConcurrency, FbDbType.Integer);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncScopeTimestamp, FbDbType.Integer);
            updReplicaInfoCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
            UpdateScopeInfoCommand = updReplicaInfoCmd;

            //
            // Select overlapping scopes
            //
            // get tombstones for cleanup
            FbCommand overlappingScopesCmd = new FbCommand();

            overlappingScopesCmd.CommandType = CommandType.StoredProcedure;
            overlappingScopesCmd.CommandText = "sp_select_shared_scopes";
            overlappingScopesCmd.Parameters.Add(DbSyncSession.SyncScopeName, FbDbType.VarChar, 100);
            SelectOverlappingScopesCommand = overlappingScopesCmd;

            //
            // Update table cleanup info
            //
            FbCommand updScopeCleanupInfoCmd = new FbCommand();

            updScopeCleanupInfoCmd.CommandType = CommandType.Text;
            updScopeCleanupInfoCmd.CommandText = "update  scope_info set " +
                                                 "scope_cleanup_timestamp = @" + DbSyncSession.SyncScopeCleanupTimestamp + " " +
                                                 "where scope_name = @" + DbSyncSession.SyncScopeName + " and " +
                                                 "(scope_cleanup_timestamp is null or scope_cleanup_timestamp <  @" + DbSyncSession.SyncScopeCleanupTimestamp + "2);" +
                                                 "@" + DbSyncSession.SyncRowCount + " = ROW_COUNT;";
            updScopeCleanupInfoCmd.Parameters.Add(DbSyncSession.SyncScopeCleanupTimestamp, FbDbType.Integer);
            updScopeCleanupInfoCmd.Parameters.Add(DbSyncSession.SyncScopeName, FbDbType.VarChar, 100);
            updScopeCleanupInfoCmd.Parameters.Add(DbSyncSession.SyncScopeCleanupTimestamp + "2", FbDbType.Integer);
            updScopeCleanupInfoCmd.Parameters.Add(DbSyncSession.SyncRowCount, FbDbType.Integer).Direction = ParameterDirection.Output;
            UpdateScopeCleanupTimestampCommand = updScopeCleanupInfoCmd;
        }
Пример #14
0
        /// <summary>
        /// Get changes from
        /// </summary>
        internal async Task <HttpMessageSendChangesResponse> ApplyThenGetChangesAsync(HttpMessageSendChangesRequest httpMessage, SessionCache sessionCache,
                                                                                      int clientBatchSize, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            // Overriding batch size options value, coming from client
            // having changes from server in batch size or not is decided by the client.
            // Basically this options is not used on the server, since it's always overriden by the client
            this.Options.BatchSize = clientBatchSize;

            // Get if we need to serialize data or making everything in memory
            var clientWorkInMemory = clientBatchSize == 0;

            // Get context from request message
            var ctx = httpMessage.SyncContext;

            // Set the context coming from the client
            this.SetContext(ctx);

            // Check schema.
            // If client has stored the schema, the EnsureScope will not be called on server.
            if (this.Schema == null || !this.Schema.HasTables || !this.Schema.HasColumns)
            {
                var serverScopeInfo = await this.EnsureSchemaAsync(cancellationToken, progress).ConfigureAwait(false);

                this.Schema = serverScopeInfo.Schema;
                this.Schema.EnsureSchema();
            }

            // ------------------------------------------------------------
            // FIRST STEP : receive client changes
            // ------------------------------------------------------------

            // We are receiving changes from client
            // BatchInfo containing all BatchPartInfo objects
            // Retrieve batchinfo instance if exists

            // Get batch info from session cache if exists, otherwise create it
            if (sessionCache.ClientBatchInfo == null)
            {
                sessionCache.ClientBatchInfo = new BatchInfo(clientWorkInMemory, Schema, this.Options.BatchDirectory);
            }

            // create the in memory changes set
            var changesSet = new SyncSet();

            foreach (var table in httpMessage.Changes.Tables)
            {
                DbSyncAdapter.CreateChangesTable(Schema.Tables[table.TableName, table.SchemaName], changesSet);
            }

            changesSet.ImportContainerSet(httpMessage.Changes, false);

            // If client has made a conversion on each line, apply the reverse side of it
            if (this.ClientConverter != null && changesSet.HasRows)
            {
                AfterDeserializedRows(changesSet, this.ClientConverter);
            }

            // add changes to the batch info
            await sessionCache.ClientBatchInfo.AddChangesAsync(changesSet, httpMessage.BatchIndex, httpMessage.IsLastBatch, this);


            // Clear the httpMessage set
            if (!clientWorkInMemory && httpMessage.Changes != null)
            {
                httpMessage.Changes.Clear();
            }

            // Until we don't have received all the batches, wait for more
            if (!httpMessage.IsLastBatch)
            {
                return(new HttpMessageSendChangesResponse(httpMessage.SyncContext)
                {
                    ServerStep = HttpStep.SendChangesInProgress
                });
            }

            // ------------------------------------------------------------
            // SECOND STEP : apply then return server changes
            // ------------------------------------------------------------


            // get changes
            var(remoteClientTimestamp, serverBatchInfo, _, clientChangesApplied, serverChangesSelected) =
                await this.ApplyThenGetChangesAsync(httpMessage.Scope, sessionCache.ClientBatchInfo, cancellationToken, progress).ConfigureAwait(false);


            // Save the server batch info object to cache if not working in memory
            if (!clientWorkInMemory)
            {
                sessionCache.RemoteClientTimestamp = remoteClientTimestamp;
                sessionCache.ServerBatchInfo       = serverBatchInfo;
                sessionCache.ServerChangesSelected = serverChangesSelected;
                sessionCache.ClientChangesApplied  = clientChangesApplied;
            }

            // Get the firt response to send back to client
            return(await GetChangesResponseAsync(ctx, remoteClientTimestamp, serverBatchInfo, clientChangesApplied, serverChangesSelected, 0));
        }