/// <summary> /// Handle a conflict /// The int returned is the conflict count I need /// </summary> /// changeApplicationAction, conflictCount, resolvedRow, conflictApplyInt internal async Task <(int conflictResolvedCount, SyncRow resolvedRow, int rowAppliedCount)> HandleConflictAsync( Guid localScopeId, Guid senderScopeId, DbSyncAdapter syncAdapter, SyncContext context, SyncConflict conflict, ConflictResolutionPolicy policy, long lastTimestamp, DbConnection connection, DbTransaction transaction) { SyncRow finalRow; ApplyAction conflictApplyAction; int rowAppliedCount = 0; (conflictApplyAction, finalRow) = await this.GetConflictActionAsync(context, conflict, policy, 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 : conflict.LocalRow; // Conflict on a line that is not present on the datasource if (row == null) { return(0, finalRow, 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 syncAdapter.ApplyUpdateAsync(row, lastTimestamp, null, true); // 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 : conflict.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 (conflict.RemoteRow == null) { return(0, finalRow, 0); } bool operationComplete = false; switch (conflict.Type) { // Remote source has row, Local don't have the row, so insert it case ConflictType.RemoteExistsLocalExists: case ConflictType.RemoteExistsLocalNotExists: case ConflictType.RemoteExistsLocalIsDeleted: case ConflictType.UniqueKeyConstraint: operationComplete = await syncAdapter.ApplyUpdateAsync(conflict.RemoteRow, lastTimestamp, senderScopeId, true); rowAppliedCount = 1; break; // Conflict, but both have delete the row, so nothing to do case ConflictType.RemoteIsDeletedLocalIsDeleted: 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 syncAdapter.ApplyDeleteAsync(conflict.RemoteRow, lastTimestamp, senderScopeId, true); rowAppliedCount = 1; break; case ConflictType.RemoteCleanedupDeleteLocalUpdate: case ConflictType.ErrorsOccurred: return(0, finalRow, 0); } finalRow = conflict.RemoteRow; //After a force update, there is a problem, so raise exception if (!operationComplete) { finalRow = null; return(0, finalRow, rowAppliedCount); } return(1, finalRow, rowAppliedCount); } return(0, finalRow, 0); }