/// <summary>
        /// Set command parameters value mapped to Row
        /// </summary>
        internal void SetColumnParametersValues(DbCommand command, SyncRow row)
        {
            if (row.SchemaTable == null)
            {
                throw new ArgumentException("Schema table columns does not correspond to row values");
            }

            var schemaTable = row.SchemaTable;

            foreach (DbParameter parameter in command.Parameters)
            {
                if (!string.IsNullOrEmpty(parameter.SourceColumn))
                {
                    // foreach parameter, check if we have a column
                    var column = schemaTable.Columns[parameter.SourceColumn];

                    if (column != null)
                    {
                        object value = row[column] ?? DBNull.Value;
                        DbSyncAdapter.SetParameterValue(command, parameter.ParameterName, value);
                    }
                }
            }

            // return value
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                syncRowCountParam.Direction = ParameterDirection.Output;
                syncRowCountParam.Value     = DBNull.Value;
            }
        }
        /// <summary>
        /// Apply a single update in the current datasource. if forceWrite, override conflict situation and force the update
        /// </summary>
        private async Task <bool> InternalApplyConflictUpdateAsync(SyncContext context, DbSyncAdapter syncAdapter, SyncRow row, long?lastTimestamp, Guid?senderScopeId, bool forceWrite, DbConnection connection, DbTransaction transaction)
        {
            if (row.Table == null)
            {
                throw new ArgumentException("Schema table is not present in the row");
            }

            var command = await syncAdapter.GetCommandAsync(DbCommandType.UpdateRow, connection, transaction);

            // Set the parameters value from row
            syncAdapter.SetColumnParametersValues(command, row);

            // Set the special parameters for update
            syncAdapter.AddScopeParametersValues(command, senderScopeId, lastTimestamp, false, forceWrite);

            var rowUpdatedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

            // Check if we have a return value instead
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                rowUpdatedCount = (int)syncRowCountParam.Value;
            }

            return(rowUpdatedCount > 0);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Update a metadata row
        /// </summary>
        internal async Task <(SyncContext context, bool metadataUpdated)> InternalUpdateMetadatasAsync(IScopeInfo scopeInfo, SyncContext context, DbSyncAdapter syncAdapter, SyncRow row, Guid?senderScopeId, bool forceWrite, DbConnection connection, DbTransaction transaction)
        {
            context.SyncStage = SyncStage.ChangesApplying;

            var(command, _) = await syncAdapter.GetCommandAsync(DbCommandType.UpdateMetadata, connection, transaction);

            if (command == null)
            {
                return(context, false);
            }

            // Set the parameters value from row
            syncAdapter.SetColumnParametersValues(command, row);

            // Set the special parameters for update
            syncAdapter.AddScopeParametersValues(command, senderScopeId, 0, row.RowState == DataRowState.Deleted, forceWrite);

            await this.InterceptAsync(new DbCommandArgs(context, command, connection, transaction)).ConfigureAwait(false);

            var metadataUpdatedRowsCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

            // Check if we have a return value instead
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                metadataUpdatedRowsCount = (int)syncRowCountParam.Value;
            }

            command.Dispose();

            return(context, metadataUpdatedRowsCount > 0);
        }
        internal virtual async Task <DatabaseMetadatasCleaned> InternalDeleteMetadatasAsync(SyncContext context, SyncSet schema, SyncSetup setup, long timestampLimit,
                                                                                            DbConnection connection, DbTransaction transaction,
                                                                                            CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            await this.InterceptAsync(new MetadataCleaningArgs(context, this.Setup, timestampLimit, connection, transaction), cancellationToken).ConfigureAwait(false);

            DatabaseMetadatasCleaned databaseMetadatasCleaned = new DatabaseMetadatasCleaned {
                TimestampLimit = timestampLimit
            };

            foreach (var syncTable in schema.Tables)
            {
                // Create sync adapter
                var syncAdapter = this.GetSyncAdapter(syncTable, setup);

                var command = await syncAdapter.GetCommandAsync(DbCommandType.DeleteMetadata, connection, transaction);

                // Set the special parameters for delete metadata
                DbSyncAdapter.SetParameterValue(command, "sync_row_timestamp", timestampLimit);

                var rowsCleanedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

                // Check if we have a return value instead
                var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

                if (syncRowCountParam != null)
                {
                    rowsCleanedCount = (int)syncRowCountParam.Value;
                }

                // Only add a new table metadata stats object, if we have, at least, purged 1 or more rows
                if (rowsCleanedCount > 0)
                {
                    var tableMetadatasCleaned = new TableMetadatasCleaned(syncTable.TableName, syncTable.SchemaName)
                    {
                        RowsCleanedCount = rowsCleanedCount,
                        TimestampLimit   = timestampLimit
                    };

                    databaseMetadatasCleaned.Tables.Add(tableMetadatasCleaned);
                }
            }

            await this.InterceptAsync(new MetadataCleanedArgs(context, databaseMetadatasCleaned, connection), cancellationToken).ConfigureAwait(false);

            return(databaseMetadatasCleaned);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Internal update untracked rows routine
        /// </summary>
        internal async Task <int> InternalUpdateUntrackedRowsAsync(SyncContext ctx, DbSyncAdapter syncAdapter, DbConnection connection, DbTransaction transaction)
        {
            // Get correct Select incremental changes command
            var command = await syncAdapter.GetCommandAsync(DbCommandType.UpdateUntrackedRows, connection, transaction);

            // Execute
            var rowAffected = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

            // Check if we have a return value instead
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                rowAffected = (int)syncRowCountParam.Value;
            }

            return(rowAffected);
        }
        /// <summary>
        /// Internal update untracked rows routine
        /// </summary>
        internal async Task <(SyncContext context, int updated)> InternalUpdateUntrackedRowsAsync(IScopeInfo scopeInfo, SyncContext context, DbSyncAdapter syncAdapter, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            // Get table builder
            var tableBuilder = this.GetTableBuilder(syncAdapter.TableDescription, scopeInfo);

            // Check if tracking table exists
            bool trackingTableExists;

            (context, trackingTableExists) = await this.InternalExistsTrackingTableAsync(scopeInfo, context, tableBuilder, connection, transaction, CancellationToken.None, null).ConfigureAwait(false);

            if (!trackingTableExists)
            {
                throw new MissingTrackingTableException(tableBuilder.TableDescription.GetFullName());
            }

            // Get correct Select incremental changes command
            var(command, _) = await syncAdapter.GetCommandAsync(DbCommandType.UpdateUntrackedRows, connection, transaction);

            if (command == null)
            {
                return(context, 0);
            }

            await this.InterceptAsync(new DbCommandArgs(context, command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            // Execute
            var rowAffected = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

            // Check if we have a return value instead
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                rowAffected = (int)syncRowCountParam.Value;
            }
            command.Dispose();

            return(context, rowAffected);
        }
        /// <summary>
        /// Update a metadata row
        /// </summary>
        internal async Task <bool> InternalUpdateMetadatasAsync(SyncContext context, DbSyncAdapter syncAdapter, SyncRow row, Guid?senderScopeId, bool forceWrite, DbConnection connection, DbTransaction transaction)
        {
            var command = await syncAdapter.GetCommandAsync(DbCommandType.UpdateMetadata, connection, transaction);

            // Set the parameters value from row
            syncAdapter.SetColumnParametersValues(command, row);

            // Set the special parameters for update
            syncAdapter.AddScopeParametersValues(command, senderScopeId, 0, row.RowState == DataRowState.Deleted, forceWrite);

            var metadataUpdatedRowsCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

            // Check if we have a return value instead
            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

            if (syncRowCountParam != null)
            {
                metadataUpdatedRowsCount = (int)syncRowCountParam.Value;
            }

            return(metadataUpdatedRowsCount > 0);
        }
        /// <summary>
        /// Internally apply a batch changes from a table
        /// </summary>
        private async Task <(int AppliedRowsCount, int ConflictsResolvedCount)> InternalApplyChangesBatchAsync(SyncContext context, bool useBulkOperation,
                                                                                                               SyncTable changesTable, MessageApplyChanges message, DataRowState applyType,
                                                                                                               DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken)
        {
            // Conflicts occured when trying to apply rows
            var conflictRows = new List <SyncRow>();

            // get executioning adapter
            var syncAdapter = this.GetSyncAdapter(changesTable, message.Setup);

            syncAdapter.ApplyType = applyType;

            // Get correct command type
            var dbCommandType = applyType switch
            {
                DataRowState.Deleted => useBulkOperation ? DbCommandType.BulkDeleteRows : DbCommandType.DeleteRow,
                DataRowState.Modified => useBulkOperation ? DbCommandType.BulkUpdateRows : DbCommandType.UpdateRow,
                _ => throw new UnknownException("RowState not valid during ApplyBulkChanges operation"),
            };

            // Get command
            var command = await syncAdapter.GetCommandAsync(dbCommandType, connection, transaction);

            // Launch any interceptor if available
            var args = new TableChangesBatchApplyingArgs(context, changesTable, applyType, command, connection, transaction);

            await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false);

            if (args.Cancel || args.Command == null)
            {
                return(0, 0);
            }

            // get the correct pointer to the command from the interceptor in case user change the whole instance
            command = args.Command;

            // get the items count
            var itemsArrayCount = changesTable.Rows.Count;

            // Make some parts of BATCH_SIZE
            var appliedRows = await Task.Run(async() =>
            {
                int appliedRowsTmp = 0;

                for (int step = 0; step < itemsArrayCount; step += DbSyncAdapter.BATCH_SIZE)
                {
                    // get upper bound max value
                    var taken = step + DbSyncAdapter.BATCH_SIZE >= itemsArrayCount ? itemsArrayCount - step : DbSyncAdapter.BATCH_SIZE;

                    var arrayStepChanges = changesTable.Rows.Skip(step).Take(taken);

                    if (useBulkOperation)
                    {
                        var failedPrimaryKeysTable = changesTable.Schema.Clone().Tables[changesTable.TableName, changesTable.SchemaName];

                        // execute the batch, through the provider
                        await syncAdapter.ExecuteBatchCommandAsync(command, message.SenderScopeId, arrayStepChanges, changesTable, failedPrimaryKeysTable, message.LastTimestamp, connection, transaction).ConfigureAwait(false);

                        // Get local and remote row and create the conflict object
                        foreach (var failedRow in failedPrimaryKeysTable.Rows)
                        {
                            // Get the row that caused the problem, from the opposite side (usually client)
                            var remoteConflictRow = changesTable.Rows.GetRowByPrimaryKeys(failedRow);
                            conflictRows.Add(remoteConflictRow);
                        }

                        //rows minus failed rows
                        appliedRowsTmp += taken - failedPrimaryKeysTable.Rows.Count;
                    }
                    else
                    {
                        foreach (var row in arrayStepChanges)
                        {
                            // Set the parameters value from row
                            syncAdapter.SetColumnParametersValues(command, row);

                            // Set the special parameters for update
                            syncAdapter.AddScopeParametersValues(command, message.SenderScopeId, message.LastTimestamp, applyType == DataRowState.Deleted, false);

                            var rowAppliedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

                            // Check if we have a return value instead
                            var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

                            if (syncRowCountParam != null)
                            {
                                rowAppliedCount = (int)syncRowCountParam.Value;
                            }

                            if (rowAppliedCount > 0)
                            {
                                appliedRowsTmp++;
                            }
                            else
                            {
                                conflictRows.Add(row);
                            }
                        }
                    }
                }

                return(appliedRowsTmp);
            });

            // If conflicts occured
            if (conflictRows.Count <= 0)
            {
                return(appliedRows, 0);
            }

            // conflict rows applied
            int rowsAppliedCount = 0;
            // conflict resolved count
            int conflictsResolvedCount = 0;

            foreach (var conflictRow in conflictRows)
            {
                var fromScopeLocalTimeStamp = message.LastTimestamp;

                var(conflictResolvedCount, resolvedRow, rowAppliedCount) =
                    await this.HandleConflictAsync(message.LocalScopeId, message.SenderScopeId, syncAdapter, context, conflictRow, changesTable,
                                                   message.Policy, fromScopeLocalTimeStamp, connection, transaction).ConfigureAwait(false);

                conflictsResolvedCount += conflictResolvedCount;
                rowsAppliedCount       += rowAppliedCount;
            }

            appliedRows += rowsAppliedCount;

            return(appliedRows, conflictsResolvedCount);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Apply changes internal method for one type of query: Insert, Update or Delete for every batch from a table
        /// </summary>
        private async Task <SyncContext> InternalApplyTableChangesAsync(IScopeInfo scopeInfo, SyncContext context, SyncTable schemaTable, MessageApplyChanges message,
                                                                        DbConnection connection, DbTransaction transaction, DataRowState applyType, DatabaseChangesApplied changesApplied,
                                                                        CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (this.Provider == null)
            {
                return(context);
            }

            context.SyncStage = SyncStage.ChangesApplying;

            var setupTable = scopeInfo.Setup.Tables[schemaTable.TableName, schemaTable.SchemaName];

            if (setupTable == null)
            {
                return(context);
            }

            // Only table schema is replicated, no datas are applied
            if (setupTable.SyncDirection == SyncDirection.None)
            {
                return(context);
            }

            // if we are in upload stage, so check if table is not download only
            if (context.SyncWay == SyncWay.Upload && setupTable.SyncDirection == SyncDirection.DownloadOnly)
            {
                return(context);
            }

            // if we are in download stage, so check if table is not download only
            if (context.SyncWay == SyncWay.Download && setupTable.SyncDirection == SyncDirection.UploadOnly)
            {
                return(context);
            }

            var hasChanges = message.BatchInfo.HasData(schemaTable.TableName, schemaTable.SchemaName);

            // Each table in the messages contains scope columns. Don't forget it
            if (!hasChanges)
            {
                return(context);
            }

            // what kind of command to execute
            var           init          = message.IsNew || context.SyncType != SyncType.Normal;
            DbCommandType dbCommandType = applyType == DataRowState.Deleted ? DbCommandType.DeleteRows : (init ? DbCommandType.InsertRows : DbCommandType.UpdateRows);

            // tmp sync table with only writable columns
            var changesSet         = schemaTable.Schema.Clone(false);
            var schemaChangesTable = DbSyncAdapter.CreateChangesTable(schemaTable, changesSet);

            // get executioning adapter
            var syncAdapter = this.GetSyncAdapter(schemaChangesTable, scopeInfo);

            syncAdapter.ApplyType = applyType;

            // Get command
            var(command, isBatch) = await syncAdapter.GetCommandAsync(dbCommandType, connection, transaction);

            if (command == null)
            {
                return(context);
            }

            var bpiTables = message.BatchInfo.GetBatchPartsInfo(schemaTable);

            // launch interceptor if any
            var args = new TableChangesApplyingArgs(context, message.BatchInfo, bpiTables, schemaTable, applyType, command, connection, transaction);

            await this.InterceptAsync(args, progress, cancellationToken).ConfigureAwait(false);

            if (args.Cancel || args.Command == null)
            {
                return(context);
            }

            command = args.Command;
            var cmdText = command.CommandText;

            TableChangesApplied tableChangesApplied = null;

            // Conflicts occured when trying to apply rows
            var conflictRows = new List <SyncRow>();

            var localSerializer = new LocalJsonSerializer();

            // If someone has an interceptor on deserializing, we read the row and intercept
            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, progress, cancellationToken).ConfigureAwait(false);
                    return(args.Result);
                });
            }

            // I've got all files for my table
            // applied rows for this bpi
            foreach (var batchPartInfo in bpiTables)
            {
                // Applied row for this particular BPI
                var appliedRowsTmp = 0;
                // Rows fetch (either of the good state or not) from the BPI
                var rowsFetched = 0;

                // Get full path of my batchpartinfo
                var fullPath = message.BatchInfo.GetBatchPartInfoPath(batchPartInfo).FullPath;

                // accumulating rows
                var batchRows = new List <SyncRow>();

                if (isBatch)
                {
                    foreach (var syncRow in localSerializer.ReadRowsFromFile(fullPath, schemaChangesTable))
                    {
                        rowsFetched++;

                        // Adding rows to the batch rows
                        if (batchRows.Count < this.Provider.BulkBatchMaxLinesCount)
                        {
                            if (syncRow.RowState == applyType)
                            {
                                batchRows.Add(syncRow);
                            }

                            if (rowsFetched < batchPartInfo.RowsCount && batchRows.Count < this.Provider.BulkBatchMaxLinesCount)
                            {
                                continue;
                            }
                        }
                        if (batchRows.Count <= 0)
                        {
                            continue;
                        }

                        var failedRows = schemaChangesTable.Schema.Clone().Tables[schemaChangesTable.TableName, schemaChangesTable.SchemaName];

                        command.CommandText = cmdText;
                        var batchArgs = new RowsChangesApplyingArgs(context, message.BatchInfo, batchRows, schemaChangesTable, applyType, command, connection, transaction);
                        await this.InterceptAsync(batchArgs, progress, cancellationToken).ConfigureAwait(false);

                        if (batchArgs.Cancel || batchArgs.Command == null || batchArgs.SyncRows == null || batchArgs.SyncRows.Count <= 0)
                        {
                            continue;
                        }

                        // get the correct pointer to the command from the interceptor in case user change the whole instance
                        command = batchArgs.Command;

                        await this.InterceptAsync(new DbCommandArgs(context, command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

                        // execute the batch, through the provider
                        await syncAdapter.ExecuteBatchCommandAsync(command, message.SenderScopeId, batchArgs.SyncRows, schemaChangesTable, failedRows, message.LastTimestamp, connection, transaction).ConfigureAwait(false);

                        foreach (var failedRow in failedRows.Rows)
                        {
                            conflictRows.Add(failedRow);
                        }

                        //rows minus failed rows
                        appliedRowsTmp += batchRows.Count - failedRows.Rows.Count;
                        batchRows.Clear();
                    }
                }
                else
                {
                    foreach (var syncRow in localSerializer.ReadRowsFromFile(fullPath, schemaChangesTable))
                    {
                        rowsFetched++;

                        if (syncRow.RowState != applyType)
                        {
                            continue;
                        }

                        command.CommandText = cmdText;
                        var batchArgs = new RowsChangesApplyingArgs(context, message.BatchInfo, new List <SyncRow> {
                            syncRow
                        }, schemaChangesTable, applyType, command, connection, transaction);
                        await this.InterceptAsync(batchArgs, progress, cancellationToken).ConfigureAwait(false);

                        if (batchArgs.Cancel || batchArgs.Command == null || batchArgs.SyncRows == null || batchArgs.SyncRows.Count() <= 0)
                        {
                            continue;
                        }

                        // get the correct pointer to the command from the interceptor in case user change the whole instance
                        command = batchArgs.Command;

                        // Set the parameters value from row
                        syncAdapter.SetColumnParametersValues(command, batchArgs.SyncRows.First());

                        // Set the special parameters for update
                        syncAdapter.AddScopeParametersValues(command, message.SenderScopeId, message.LastTimestamp, applyType == DataRowState.Deleted, false);

                        await this.InterceptAsync(new DbCommandArgs(context, command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

                        var rowAppliedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

                        // Check if we have a return value instead
                        var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

                        if (syncRowCountParam != null)
                        {
                            rowAppliedCount = (int)syncRowCountParam.Value;
                        }

                        if (rowAppliedCount > 0)
                        {
                            appliedRowsTmp++;
                        }
                        else
                        {
                            conflictRows.Add(syncRow);
                        }
                    }
                }


                // conflict rows applied
                int rowsAppliedCount = 0;
                // conflict resolved count
                int conflictsResolvedCount = 0;

                // If conflicts occured
                if (conflictRows.Count > 0)
                {
                    foreach (var conflictRow in conflictRows)
                    {
                        int     conflictResolvedCount;
                        SyncRow resolvedRow;
                        int     rowAppliedCount;
                        (context, conflictResolvedCount, resolvedRow, rowAppliedCount) =
                            await this.HandleConflictAsync(scopeInfo, context, message.LocalScopeId, message.SenderScopeId, syncAdapter, conflictRow, schemaChangesTable,
                                                           message.Policy, message.LastTimestamp, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        conflictsResolvedCount += conflictResolvedCount;
                        rowsAppliedCount       += rowAppliedCount;
                    }

                    // add rows with resolved conflicts
                    appliedRowsTmp += rowsAppliedCount;
                }

                // Any failure ?
                var changedFailed = rowsFetched - conflictsResolvedCount - appliedRowsTmp;

                // Only Upsert DatabaseChangesApplied if we make an upsert/ delete from the batch or resolved any conflict
                if (appliedRowsTmp > 0 || conflictsResolvedCount > 0)
                {
                    // We may have multiple batch files, so we can have multipe sync tables with the same name
                    // We can say that a syncTable may be contained in several files
                    // That's why we should get an applied changes instance if already exists from a previous batch file
                    tableChangesApplied = changesApplied.TableChangesApplied.FirstOrDefault(tca =>
                    {
                        var sc = SyncGlobalization.DataSourceStringComparison;

                        var sn      = tca.SchemaName == null ? string.Empty : tca.SchemaName;
                        var otherSn = schemaTable.SchemaName == null ? string.Empty : schemaTable.SchemaName;

                        return(tca.TableName.Equals(schemaTable.TableName, sc) &&
                               sn.Equals(otherSn, sc) &&
                               tca.State == applyType);
                    });

                    if (tableChangesApplied == null)
                    {
                        tableChangesApplied = new TableChangesApplied
                        {
                            TableName         = schemaTable.TableName,
                            SchemaName        = schemaTable.SchemaName,
                            Applied           = appliedRowsTmp,
                            ResolvedConflicts = conflictsResolvedCount,
                            Failed            = changedFailed,
                            State             = applyType,
                            TotalRowsCount    = message.BatchInfo.RowsCount,
                            TotalAppliedCount = changesApplied.TotalAppliedChanges + appliedRowsTmp
                        };
                        changesApplied.TableChangesApplied.Add(tableChangesApplied);
                    }
                    else
                    {
                        tableChangesApplied.Applied           += appliedRowsTmp;
                        tableChangesApplied.TotalAppliedCount  = changesApplied.TotalAppliedChanges;
                        tableChangesApplied.ResolvedConflicts += conflictsResolvedCount;
                        tableChangesApplied.Failed            += changedFailed;
                    }

                    // we've got 0.25% to fill here
                    var progresspct = appliedRowsTmp * 0.25d / tableChangesApplied.TotalRowsCount;
                    context.ProgressPercentage += progresspct;
                }
            }

            schemaChangesTable.Dispose();
            schemaChangesTable = null;
            changesSet.Dispose();
            changesSet = null;

            // Report the overall changes applied for the current table
            if (tableChangesApplied != null)
            {
                var tableChangesAppliedArgs = new TableChangesAppliedArgs(context, tableChangesApplied, connection, transaction);
                // We don't report progress if we do not have applied any changes on the table, to limit verbosity of Progress
                await this.InterceptAsync(tableChangesAppliedArgs, progress, cancellationToken).ConfigureAwait(false);
            }


            if (command != null)
            {
                command.Dispose();
            }

            return(context);
        }
Exemplo n.º 10
0
        InternalDeleteMetadatasAsync(
            IEnumerable <IScopeInfo> scopeInfos, SyncContext context, long timestampLimit,
            DbConnection connection, DbTransaction transaction,
            CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            context.SyncStage = SyncStage.ChangesApplying;

            var databaseMetadatasCleaned = new DatabaseMetadatasCleaned {
                TimestampLimit = timestampLimit
            };

            await this.InterceptAsync(new MetadataCleaningArgs(context, scopeInfos, timestampLimit,
                                                               connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            // contains all tables already processed
            var doneList = new List <SetupTable>();

            foreach (var scopeInfo in scopeInfos)
            {
                if (scopeInfo.Setup?.Tables == null || scopeInfo.Setup.Tables.Count <= 0)
                {
                    continue;
                }

                foreach (var setupTable in scopeInfo.Setup.Tables)
                {
                    var isDone = doneList.Any(t => t.EqualsByName(setupTable));

                    if (isDone)
                    {
                        continue;
                    }

                    // create a fake syncTable
                    // Don't need anything else than table name to make a delete metadata clean up
                    var syncTable = new SyncTable(setupTable.TableName, setupTable.SchemaName);

                    // Create sync adapter
                    var syncAdapter = this.GetSyncAdapter(syncTable, scopeInfo);

                    var(command, _) = await syncAdapter.GetCommandAsync(DbCommandType.DeleteMetadata, connection, transaction);

                    if (command != null)
                    {
                        // Set the special parameters for delete metadata
                        DbSyncAdapter.SetParameterValue(command, "sync_row_timestamp", timestampLimit);

                        await this.InterceptAsync(new DbCommandArgs(context, command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

                        var rowsCleanedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

                        // Check if we have a return value instead
                        var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

                        if (syncRowCountParam != null)
                        {
                            rowsCleanedCount = (int)syncRowCountParam.Value;
                        }

                        // Only add a new table metadata stats object, if we have, at least, purged 1 or more rows
                        if (rowsCleanedCount > 0)
                        {
                            var tableMetadatasCleaned = new TableMetadatasCleaned(syncTable.TableName, syncTable.SchemaName)
                            {
                                RowsCleanedCount = rowsCleanedCount,
                                TimestampLimit   = timestampLimit
                            };

                            databaseMetadatasCleaned.Tables.Add(tableMetadatasCleaned);
                        }

                        command.Dispose();
                    }

                    doneList.Add(setupTable);
                }
            }

            await this.InterceptAsync(new MetadataCleanedArgs(context, databaseMetadatasCleaned, connection), progress, cancellationToken).ConfigureAwait(false);

            return(context, databaseMetadatasCleaned);
        }