/// <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;
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Apply a single update in the current datasource. if forceWrite, override conflict situation and force the update
        /// </summary>
        internal async Task <bool> ApplyUpdateAsync(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 this.PrepareCommandAsync(DbCommandType.UpdateRow, connection, transaction);

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

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

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

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

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

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

            var schemaTable = row.Table;

            foreach (DbParameter parameter in command.Parameters)
            {
                // foreach parameter, check if we have a column
                if (!string.IsNullOrEmpty(parameter.SourceColumn))
                {
                    var column = schemaTable.Columns.FirstOrDefault(sc => sc.ColumnName.Equals(parameter.SourceColumn, SyncGlobalization.DataSourceStringComparison));
                    if (column != null)
                    {
                        object value = row[column] ?? DBNull.Value;
                        DbTableManagerFactory.SetParameterValue(command, parameter.ParameterName, value);
                    }
                }
            }

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

            if (syncRowCountParam != null)
            {
                syncRowCountParam.Direction = ParameterDirection.Output;
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Create a new row
        /// </summary>
        public SyncRow NewRow(DataRowState state = DataRowState.Unchanged)
        {
            var row = new SyncRow(this.Columns.Count)
            {
                RowState = state,
                Table    = this
            };

            return(row);
        }
        /// <summary>
        /// We have a conflict, try to get the source row and generate a conflict
        /// </summary>
        private SyncConflict GetConflict(SyncRow remoteConflictRow, SyncRow localConflictRow)
        {
            var dbConflictType = ConflictType.ErrorsOccurred;

            if (remoteConflictRow == null)
            {
                throw new UnknownException("THAT can't happen...");
            }


            // local row is null
            if (localConflictRow == null && remoteConflictRow.RowState == DataRowState.Modified)
            {
                dbConflictType = ConflictType.RemoteExistsLocalNotExists;
            }
            else if (localConflictRow == null && remoteConflictRow.RowState == DataRowState.Deleted)
            {
                dbConflictType = ConflictType.RemoteIsDeletedLocalNotExists;
            }

            //// remote row is null. Can't happen
            //else if (remoteConflictRow == null && localConflictRow.RowState == DataRowState.Modified)
            //    dbConflictType = ConflictType.RemoteNotExistsLocalExists;
            //else if (remoteConflictRow == null && localConflictRow.RowState == DataRowState.Deleted)
            //    dbConflictType = ConflictType.RemoteNotExistsLocalIsDeleted;

            else if (remoteConflictRow.RowState == DataRowState.Deleted && localConflictRow.RowState == DataRowState.Deleted)
            {
                dbConflictType = ConflictType.RemoteIsDeletedLocalIsDeleted;
            }
            else if (remoteConflictRow.RowState == DataRowState.Modified && localConflictRow.RowState == DataRowState.Deleted)
            {
                dbConflictType = ConflictType.RemoteExistsLocalIsDeleted;
            }
            else if (remoteConflictRow.RowState == DataRowState.Deleted && localConflictRow.RowState == DataRowState.Modified)
            {
                dbConflictType = ConflictType.RemoteIsDeletedLocalExists;
            }
            else if (remoteConflictRow.RowState == DataRowState.Modified && localConflictRow.RowState == DataRowState.Modified)
            {
                dbConflictType = ConflictType.RemoteExistsLocalExists;
            }

            // Generate the conflict
            var conflict = new SyncConflict(dbConflictType);

            conflict.AddRemoteRow(remoteConflictRow);

            if (localConflictRow != null)
            {
                conflict.AddLocalRow(localConflictRow);
            }

            return(conflict);
        }
Esempio n. 6
0
        /// <summary>
        /// Apply a delete on a row
        /// </summary>
        internal async Task <bool> ApplyDeleteAsync(SyncRow row, long lastTimestamp, Guid?senderScopeId, bool forceWrite)
        {
            if (row.Table == null)
            {
                throw new ArgumentException("Schema table is not present in the row");
            }

            using (var command = this.GetCommand(DbCommandType.DeleteRow))
            {
                if (command == null)
                {
                    throw new MissingCommandException(DbCommandType.DeleteRow.ToString());
                }

                // Deriving Parameters
                await this.SetCommandParametersAsync(DbCommandType.DeleteRow, command).ConfigureAwait(false);

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

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

                var alreadyOpened = Connection.State == ConnectionState.Open;

                // OPen Connection
                if (!alreadyOpened)
                {
                    await this.Connection.OpenAsync().ConfigureAwait(false);
                }

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

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

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

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

                if (!alreadyOpened)
                {
                    Connection.Close();
                }

                return(rowDeletedCount > 0);
            }
        }
        /// <summary>
        /// A conflict has occured, we try to ask for the solution to the user
        /// </summary>
        private async Task <(ApplyAction, ConflictType, SyncRow, SyncRow, Guid?)> GetConflictActionAsync(SyncContext context, Guid localScopeId, DbSyncAdapter syncAdapter, SyncRow conflictRow,
                                                                                                         SyncTable schemaChangesTable, ConflictResolutionPolicy policy, Guid senderScopeId, DbConnection connection, DbTransaction transaction = null, CancellationToken cancellationToken = default)
        {
            // default action
            var resolution = policy == ConflictResolutionPolicy.ClientWins ? ConflictResolution.ClientWins : ConflictResolution.ServerWins;

            // if ConflictAction is ServerWins or MergeRow it's Ok to set to Continue
            var action = ApplyAction.Continue;

            // check the interceptor
            var interceptor = this.interceptors.GetInterceptor <ApplyChangesFailedArgs>();

            SyncRow finalRow           = null;
            SyncRow localRow           = null;
            Guid?   finalSenderScopeId = senderScopeId;

            // default conflict type
            ConflictType conflictType = conflictRow.RowState == DataRowState.Deleted ? ConflictType.RemoteIsDeletedLocalExists : ConflictType.RemoteExistsLocalExists;

            // if is not empty, get the conflict and intercept
            if (!interceptor.IsEmpty)
            {
                // Get the localRow
                localRow = await this.InternalGetConflictRowAsync(context, syncAdapter, localScopeId, conflictRow, schemaChangesTable, connection, transaction).ConfigureAwait(false);

                // Get the conflict
                var conflict = this.GetConflict(conflictRow, localRow);

                // Interceptor
                var arg = new ApplyChangesFailedArgs(context, conflict, resolution, senderScopeId, connection, transaction);
                await this.InterceptAsync(arg, cancellationToken).ConfigureAwait(false);

                resolution         = arg.Resolution;
                finalRow           = arg.Resolution == ConflictResolution.MergeRow ? arg.FinalRow : null;
                finalSenderScopeId = arg.SenderScopeId;
                conflictType       = arg.Conflict.Type;
            }

            // Change action only if we choose ClientWins or Rollback.
            // for ServerWins or MergeRow, action is Continue
            if (resolution == ConflictResolution.ClientWins)
            {
                action = ApplyAction.RetryWithForceWrite;
            }
            else if (resolution == ConflictResolution.Rollback)
            {
                action = ApplyAction.Rollback;
            }

            // returning the action to take, and actually the finalRow if action is set to Merge
            return(action, conflictType, localRow, finalRow, finalSenderScopeId);
        }
Esempio n. 8
0
        /// <summary>
        /// We have a conflict, try to get the source row and generate a conflict
        /// </summary>
        private SyncConflict GetConflict(SyncRow remoteConflictRow, SyncRow localConflictRow)
        {
            var dbConflictType = ConflictType.ErrorsOccurred;

            // Can't find the row on the server datastore
            if (localConflictRow == null)
            {
                if (ApplyType == DataRowState.Modified)
                {
                    dbConflictType = ConflictType.RemoteExistsLocalNotExists;
                }
                else if (ApplyType == DataRowState.Deleted)
                {
                    dbConflictType = ConflictType.RemoteIsDeletedLocalNotExists;
                }
            }
            else
            {
                // the row on local is deleted
                if (localConflictRow.RowState == DataRowState.Deleted)
                {
                    if (ApplyType == DataRowState.Modified)
                    {
                        dbConflictType = ConflictType.RemoteExistsLocalIsDeleted;
                    }
                    else if (ApplyType == DataRowState.Deleted)
                    {
                        dbConflictType = ConflictType.RemoteIsDeletedLocalIsDeleted;
                    }
                }
                else
                {
                    dbConflictType = ConflictType.RemoteExistsLocalExists;
                }
            }
            // Generate the conflict
            var conflict = new SyncConflict(dbConflictType);

            conflict.AddRemoteRow(remoteConflictRow);

            if (localConflictRow != null)
            {
                conflict.AddLocalRow(localConflictRow);
            }

            return(conflict);
        }
Esempio n. 9
0
        /// <summary>
        /// Update a metadata row
        /// </summary>
        internal async Task <bool> UpdateMetadatasAsync(SyncRow row, Guid?senderScopeId, bool forceWrite, DbConnection connection, DbTransaction transaction)
        {
            var command = await this.PrepareCommandAsync(DbCommandType.UpdateMetadata, connection, transaction);

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

            // Set the special parameters for update
            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 = DbTableManagerFactory.GetParameter(command, "sync_row_count");

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

            return(metadataUpdatedRowsCount > 0);
        }
Esempio n. 10
0
        GetConflictActionAsync(IScopeInfo scopeInfo, SyncContext context, Guid localScopeId, DbSyncAdapter syncAdapter, SyncRow conflictRow,
                               SyncTable schemaChangesTable, ConflictResolutionPolicy policy, Guid senderScopeId, DbConnection connection, DbTransaction transaction,
                               CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            // default action
            var resolution = policy == ConflictResolutionPolicy.ClientWins ? ConflictResolution.ClientWins : ConflictResolution.ServerWins;

            // if ConflictAction is ServerWins or MergeRow it's Ok to set to Continue
            var action = ApplyAction.Continue;

            // check the interceptor
            var interceptors = this.interceptors.GetInterceptors <ApplyChangesFailedArgs>();

            SyncRow finalRow           = null;
            SyncRow localRow           = null;
            Guid?   finalSenderScopeId = senderScopeId;

            // default conflict type
            ConflictType conflictType = conflictRow.RowState == DataRowState.Deleted ? ConflictType.RemoteIsDeletedLocalExists : ConflictType.RemoteExistsLocalExists;

            // if is not empty, get the conflict and intercept
            // We don't get the conflict on automatic conflict resolution
            // Since it's an automatic resolution, we don't need to get the local conflict row
            // So far we get the conflict only if an interceptor exists
            if (interceptors.Count > 0)
            {
                // Get the localRow
                (context, localRow) = await this.InternalGetConflictRowAsync(scopeInfo, context, syncAdapter, localScopeId, conflictRow, schemaChangesTable, connection, transaction).ConfigureAwait(false);

                // Get the conflict
                var conflict = this.GetConflict(conflictRow, localRow);

                // Interceptor
                var arg = new ApplyChangesFailedArgs(context, conflict, resolution, senderScopeId, connection, transaction);
                await this.InterceptAsync(arg, progress, cancellationToken).ConfigureAwait(false);

                resolution         = arg.Resolution;
                finalRow           = arg.Resolution == ConflictResolution.MergeRow ? arg.FinalRow : null;
                finalSenderScopeId = arg.SenderScopeId;
                conflictType       = arg.Conflict.Type;
            }
            else
            {
                // Check logger, because we make some reflection here
                if (this.Logger.IsEnabled(LogLevel.Debug))
                {
                    var args = new { Row = conflictRow, Resolution = resolution, Connection = connection, Transaction = transaction };
                    this.Logger.LogDebug(new EventId(SyncEventsId.ApplyChangesFailed.Id, "ApplyChangesFailed"), args);
                }
            }

            // Change action only if we choose ClientWins or Rollback.
            // for ServerWins or MergeRow, action is Continue
            if (resolution == ConflictResolution.ClientWins)
            {
                action = ApplyAction.RetryWithForceWrite;
            }
            else if (resolution == ConflictResolution.Rollback)
            {
                action = ApplyAction.Rollback;
            }

            // returning the action to take, and actually the finalRow if action is set to Merge
            return(context, action, conflictType, localRow, finalRow, finalSenderScopeId);
        }
Esempio n. 11
0
        /// <summary>
        /// Try to get a source row
        /// </summary>
        /// <returns></returns>
        internal async Task <SyncRow> GetRowAsync(Guid localScopeId, SyncRow primaryKeyRow, SyncTable schema)
        {
            // Get the row in the local repository
            using (var selectCommand = GetCommand(DbCommandType.SelectRow))
            {
                if (selectCommand == null)
                {
                    throw new MissingCommandException(DbCommandType.SelectRow.ToString());
                }

                // Deriving Parameters
                await this.SetCommandParametersAsync(DbCommandType.SelectRow, selectCommand).ConfigureAwait(false);

                // set the primary keys columns as parameters
                this.SetColumnParametersValues(selectCommand, primaryKeyRow);

                var alreadyOpened = Connection.State == ConnectionState.Open;

                // Open Connection
                if (!alreadyOpened)
                {
                    Connection.Open();
                }

                if (Transaction != null)
                {
                    selectCommand.Transaction = Transaction;
                }

                // Create a select table based on the schema in parameter + scope columns
                var     changesSet  = schema.Schema.Clone(false);
                var     selectTable = CreateChangesTable(schema, changesSet);
                SyncRow syncRow     = null;
                using (var dataReader = await selectCommand.ExecuteReaderAsync().ConfigureAwait(false))
                {
                    while (dataReader.Read())
                    {
                        // Create a new empty row
                        syncRow = selectTable.NewRow();

                        for (var i = 0; i < dataReader.FieldCount; i++)
                        {
                            var columnName = dataReader.GetName(i);

                            // if we have the tombstone value, do not add it to the table
                            if (columnName == "sync_row_is_tombstone")
                            {
                                var isTombstone = Convert.ToInt64(dataReader.GetValue(i)) > 0;
                                syncRow.RowState = isTombstone ? DataRowState.Deleted : DataRowState.Unchanged;
                                continue;
                            }

                            if (columnName == "update_scope_id")
                            {
                                var readerScopeId = dataReader.GetValue(i);

                                //// if update_scope_id is null, so the row owner is the local database
                                //// if update_scope_id is not null, the row owner is someone else
                                //if (readerScopeId == DBNull.Value || readerScopeId == null)
                                //    syncRow.UpdateScopeId = localScopeId;
                                //else if (SyncTypeConverter.TryConvertTo<Guid>(readerScopeId, out var updateScopeIdObject))
                                //    syncRow.UpdateScopeId = (Guid)updateScopeIdObject;
                                //else
                                //    throw new Exception("Impossible to parse row['update_scope_id']");

                                continue;
                            }

                            var columnValueObject = dataReader.GetValue(i);
                            var columnValue       = columnValueObject == DBNull.Value ? null : columnValueObject;
                            syncRow[columnName] = columnValue;
                        }
                    }
                }

                // Close Connection
                if (!alreadyOpened)
                {
                    Connection.Close();
                }


                if (syncRow != null)
                {
                    syncRow.RowState = primaryKeyRow.RowState;
                }

                return(syncRow);
            }
        }
 public RowsChangesSelectedArgs(SyncContext context, SyncRow syncRow, SyncTable schemaTable, DbConnection connection, DbTransaction transaction)
     : base(context, connection, transaction)
 {
     this.SyncRow     = syncRow;
     this.SchemaTable = schemaTable;
 }
        /// <summary>
        /// Handle a conflict
        /// The int returned is the conflict count I need
        /// </summary>
        private async Task <(int conflictResolvedCount, SyncRow resolvedRow, int rowAppliedCount)> HandleConflictAsync(
            Guid localScopeId, Guid senderScopeId, DbSyncAdapter syncAdapter, SyncContext context, SyncRow conflictRow, SyncTable schemaChangesTable,
            ConflictResolutionPolicy policy, long?lastTimestamp, DbConnection connection, DbTransaction transaction)
        {
            SyncRow      finalRow;
            SyncRow      localRow;
            ConflictType conflictType;
            ApplyAction  conflictApplyAction;
            int          rowAppliedCount = 0;
            Guid?        nullableSenderScopeId;

            (conflictApplyAction, conflictType, localRow, finalRow, nullableSenderScopeId) = await this.GetConflictActionAsync(context, localScopeId, syncAdapter, conflictRow, schemaChangesTable,
                                                                                                                               policy, senderScopeId, connection, transaction).ConfigureAwait(false);

            // Conflict rollbacked by user
            if (conflictApplyAction == ApplyAction.Rollback)
            {
                throw new RollbackException("Rollback action taken on conflict");
            }

            // Local provider wins, update metadata
            if (conflictApplyAction == ApplyAction.Continue)
            {
                var isMergeAction = finalRow != null;
                var row           = isMergeAction ? finalRow : localRow;

                // Conflict on a line that is not present on the datasource
                if (row == null)
                {
                    return(conflictResolvedCount : 1, finalRow, rowAppliedCount : 0);
                }

                // if we have a merge action, we apply the row on the server
                if (isMergeAction)
                {
                    // if merge, we update locally the row and let the update_scope_id set to null
                    var isUpdated = await this.InternalApplyConflictUpdateAsync(context, syncAdapter, row, lastTimestamp, null, true, connection, transaction).ConfigureAwait(false);

                    // We don't update metadatas so the row is updated (on server side)
                    // and is mark as updated locally.
                    // and will be returned back to sender, since it's a merge, and we need it on the client

                    if (!isUpdated)
                    {
                        throw new Exception("Can't update the merge row.");
                    }
                }

                finalRow = isMergeAction ? row : localRow;

                // We don't do anything, since we let the original row. so we resolved one conflict but applied no rows
                return(conflictResolvedCount : 1, finalRow, rowAppliedCount : 0);
            }

            // We gonna apply with force the line
            if (conflictApplyAction == ApplyAction.RetryWithForceWrite)
            {
                // TODO : Should Raise an error ?
                if (conflictRow == null)
                {
                    return(0, finalRow, 0);
                }

                bool operationComplete = false;

                switch (conflictType)
                {
                // Remote source has row, Local don't have the row, so insert it
                case ConflictType.RemoteExistsLocalExists:
                    operationComplete = await this.InternalApplyConflictUpdateAsync(context, syncAdapter, conflictRow, lastTimestamp, nullableSenderScopeId, true, connection, transaction).ConfigureAwait(false);

                    rowAppliedCount = 1;
                    break;

                case ConflictType.RemoteExistsLocalNotExists:
                case ConflictType.RemoteExistsLocalIsDeleted:
                case ConflictType.UniqueKeyConstraint:
                    operationComplete = await this.InternalApplyConflictUpdateAsync(context, syncAdapter, conflictRow, lastTimestamp, nullableSenderScopeId, true, connection, transaction).ConfigureAwait(false);

                    rowAppliedCount = 1;
                    break;

                // Conflict, but both have delete the row, so just update the metadata to the right winner
                case ConflictType.RemoteIsDeletedLocalIsDeleted:
                    operationComplete = await this.InternalUpdateMetadatasAsync(context, syncAdapter, conflictRow, nullableSenderScopeId, true, connection, transaction).ConfigureAwait(false);

                    rowAppliedCount = 0;
                    break;

                // The row does not exists locally, and since it's coming from a deleted state, we can forget it
                case ConflictType.RemoteIsDeletedLocalNotExists:
                    operationComplete = true;
                    rowAppliedCount   = 0;
                    break;

                // The remote has delete the row, and local has insert or update it
                // So delete the local row
                case ConflictType.RemoteIsDeletedLocalExists:
                    operationComplete = await this.InternalApplyConflictDeleteAsync(context, syncAdapter, conflictRow, lastTimestamp, nullableSenderScopeId, true, connection, transaction);

                    // Conflict, but both have delete the row, so just update the metadata to the right winner
                    if (!operationComplete)
                    {
                        operationComplete = await this.InternalUpdateMetadatasAsync(context, syncAdapter, conflictRow, nullableSenderScopeId, true, connection, transaction);

                        rowAppliedCount = 0;
                    }
                    else
                    {
                        rowAppliedCount = 1;
                    }

                    break;

                case ConflictType.ErrorsOccurred:
                    return(0, finalRow, 0);
                }

                finalRow = conflictRow;

                //After a force update, there is a problem, so raise exception
                if (!operationComplete)
                {
                    throw new UnknownException("Force update should always work.. contact the author :)");
                }

                return(1, finalRow, rowAppliedCount);
            }

            return(0, finalRow, 0);
        }
        /// <summary>
        /// Try to get a source row
        /// </summary>
        private async Task <SyncRow> InternalGetConflictRowAsync(SyncContext context, DbSyncAdapter syncAdapter, Guid localScopeId, SyncRow primaryKeyRow, SyncTable schema, DbConnection connection, DbTransaction transaction)
        {
            // Get the row in the local repository
            var command = await syncAdapter.GetCommandAsync(DbCommandType.SelectRow, connection, transaction);

            // set the primary keys columns as parameters
            syncAdapter.SetColumnParametersValues(command, primaryKeyRow);

            // Create a select table based on the schema in parameter + scope columns
            var changesSet  = schema.Schema.Clone(false);
            var selectTable = DbSyncAdapter.CreateChangesTable(schema, changesSet);

            using var dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false);

            if (!dataReader.Read())
            {
                dataReader.Close();
                return(null);
            }

            // Create a new empty row
            var syncRow = selectTable.NewRow();

            for (var i = 0; i < dataReader.FieldCount; i++)
            {
                var columnName = dataReader.GetName(i);

                // if we have the tombstone value, do not add it to the table
                if (columnName == "sync_row_is_tombstone")
                {
                    var isTombstone = Convert.ToInt64(dataReader.GetValue(i)) > 0;
                    syncRow.RowState = isTombstone ? DataRowState.Deleted : DataRowState.Modified;
                    continue;
                }
                if (columnName == "update_scope_id")
                {
                    // var readerScopeId = dataReader.GetValue(i);
                    continue;
                }

                var columnValueObject = dataReader.GetValue(i);
                var columnValue       = columnValueObject == DBNull.Value ? null : columnValueObject;
                syncRow[columnName] = columnValue;
            }


            // if syncRow is not a deleted row, we can check for which kind of row it is.
            if (syncRow != null && syncRow.RowState == DataRowState.Unchanged)
            {
                syncRow.RowState = DataRowState.Modified;
            }

            dataReader.Close();

            return(syncRow);
        }
 /// <summary>
 /// add a local row
 /// </summary>
 internal void AddLocalRow(SyncRow row) => this.LocalRow = row;
Esempio n. 16
0
        /// <summary>
        /// Try to get a source row
        /// </summary>
        /// <returns></returns>
        internal async Task <SyncRow> GetRowAsync(Guid localScopeId, SyncRow primaryKeyRow, SyncTable schema, DbConnection connection, DbTransaction transaction)
        {
            // Get the row in the local repository
            var command = await this.PrepareCommandAsync(DbCommandType.SelectRow, connection, transaction);

            // set the primary keys columns as parameters
            this.SetColumnParametersValues(command, primaryKeyRow);

            // Create a select table based on the schema in parameter + scope columns
            var     changesSet  = schema.Schema.Clone(false);
            var     selectTable = CreateChangesTable(schema, changesSet);
            SyncRow syncRow     = null;

            using (var dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false))
            {
                while (dataReader.Read())
                {
                    // Create a new empty row
                    syncRow = selectTable.NewRow();

                    for (var i = 0; i < dataReader.FieldCount; i++)
                    {
                        var columnName = dataReader.GetName(i);

                        // if we have the tombstone value, do not add it to the table
                        if (columnName == "sync_row_is_tombstone")
                        {
                            var isTombstone = Convert.ToInt64(dataReader.GetValue(i)) > 0;
                            syncRow.RowState = isTombstone ? DataRowState.Deleted : DataRowState.Modified;
                            continue;
                        }

                        if (columnName == "update_scope_id")
                        {
                            var readerScopeId = dataReader.GetValue(i);

                            //// if update_scope_id is null, so the row owner is the local database
                            //// if update_scope_id is not null, the row owner is someone else
                            //if (readerScopeId == DBNull.Value || readerScopeId == null)
                            //    syncRow.UpdateScopeId = localScopeId;
                            //else if (SyncTypeConverter.TryConvertTo<Guid>(readerScopeId, out var updateScopeIdObject))
                            //    syncRow.UpdateScopeId = (Guid)updateScopeIdObject;
                            //else
                            //    throw new Exception("Impossible to parse row['update_scope_id']");

                            continue;
                        }

                        var columnValueObject = dataReader.GetValue(i);
                        var columnValue       = columnValueObject == DBNull.Value ? null : columnValueObject;
                        syncRow[columnName] = columnValue;
                    }
                }
            }

            // if syncRow is not a deleted row, we can check for which kind of row it is.
            if (syncRow != null && syncRow.RowState == DataRowState.Unchanged)
            {
                syncRow.RowState = DataRowState.Modified;
            }

            return(syncRow);
        }
 /// <summary>
 /// add a remote row
 /// </summary>
 internal void AddRemoteRow(SyncRow row) => this.RemoteRow = row;
Esempio n. 18
0
        /// <summary>
        /// Apply a single update in the current datasource. if forceWrite, override conflict situation and force the update
        /// </summary>
        private async Task <(SyncContext, bool)> InternalApplyConflictUpdateAsync(IScopeInfo scopeInfo, SyncContext context, DbSyncAdapter syncAdapter, SyncRow row, long?lastTimestamp, Guid?senderScopeId, bool forceWrite, DbConnection connection, DbTransaction transaction)
        {
            if (row.SchemaTable == null)
            {
                throw new ArgumentException("Schema table is not present in the row");
            }

            var(command, _) = await syncAdapter.GetCommandAsync(DbCommandType.UpdateRow, 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, lastTimestamp, false, forceWrite);

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

            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;
            }

            command.Dispose();

            return(context, rowUpdatedCount > 0);
        }
Esempio n. 19
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);
        }