/// <summary> /// A conflict has occured, we try to ask for the solution to the user /// </summary> internal async Task <(ApplyAction, SyncRow)> GetConflictActionAsync(SyncContext context, SyncConflict conflict, ConflictResolutionPolicy policy, DbConnection connection, DbTransaction transaction = null, CancellationToken cancellationToken = default) { var conflictAction = ConflictResolution.ServerWins; if (policy == ConflictResolutionPolicy.ClientWins) { conflictAction = ConflictResolution.ClientWins; } // Interceptor var arg = new ApplyChangesFailedArgs(context, conflict, conflictAction, connection, transaction); this.Orchestrator.logger.LogDebug(SyncEventsId.ResolveConflicts, arg); await this.Orchestrator.InterceptAsync(arg, cancellationToken).ConfigureAwait(false); // if ConflictAction is ServerWins or MergeRow it's Ok to set to Continue var action = ApplyAction.Continue; // Change action only if we choose ClientWins or Rollback. // for ServerWins or MergeRow, action is Continue if (arg.Resolution == ConflictResolution.ClientWins) { action = ApplyAction.RetryWithForceWrite; } else if (arg.Resolution == ConflictResolution.Rollback) { action = ApplyAction.Rollback; } var finalRow = arg.Resolution == ConflictResolution.MergeRow ? arg.FinalRow : null; // returning the action to take, and actually the finalRow if action is set to Merge return(action, finalRow); }
/// <summary> /// A conflict has occured, we try to ask for the solution to the user /// </summary> internal async Task <(ApplyAction, DmRow)> GetConflictActionAsync(SyncContext context, SyncConflict conflict, ConflictResolutionPolicy policy, DbConnection connection, DbTransaction transaction = null) { var conflictAction = ConflictResolution.ServerWins; if (policy == ConflictResolutionPolicy.ClientWins) { conflictAction = ConflictResolution.ClientWins; } // Interceptor var arg = new ApplyChangesFailedArgs(context, conflict, conflictAction, connection, transaction); await this.InterceptAsync(arg); // if ConflictAction is ServerWins or MergeRow it's Ok to set to Continue var action = ApplyAction.Continue; if (arg.Resolution == ConflictResolution.ClientWins) { action = ApplyAction.RetryWithForceWrite; } var finalRow = arg.Resolution == ConflictResolution.MergeRow ? arg.FinalRow : null; // returning the action to take, and actually the finalRow if action is set to Merge return(action, finalRow); }
/// <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); }
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); }