/// <summary> /// Try to apply changes on the server. /// Internally will call ApplyUpdate or ApplyDelete and will return /// </summary> /// <param name="changes">Changes</param> /// <returns>every lines not updated / deleted in the destination data source</returns> internal async Task <int> ApplyChangesAsync(Guid localScopeId, Guid senderScopeId, SyncTable changesTable, long lastTimestamp, List <SyncConflict> conflicts) { int appliedRows = 0; foreach (var row in changesTable.Rows) { bool operationComplete = false; try { if (ApplyType == DataRowState.Modified) { operationComplete = await this.ApplyUpdateAsync(row, lastTimestamp, senderScopeId, false).ConfigureAwait(false); } else if (ApplyType == DataRowState.Deleted) { operationComplete = await this.ApplyDeleteAsync(row, lastTimestamp, senderScopeId, false).ConfigureAwait(false); } if (operationComplete) { appliedRows++; } else { conflicts.Add(GetConflict(row, await GetRowAsync(localScopeId, row, changesTable).ConfigureAwait(false))); } } catch (Exception ex) { if (this.IsUniqueKeyViolation(ex)) { // Generate the conflict var conflict = new SyncConflict(ConflictType.UniqueKeyConstraint); // Add the row as Remote row conflict.AddRemoteRow(row); // Get the local row var localRow = await GetRowAsync(localScopeId, row, changesTable).ConfigureAwait(false); if (localRow != null) { conflict.AddLocalRow(localRow); } conflicts.Add(conflict); } else { throw; } } } return(appliedRows); }
/// <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); }
/// <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); }
/// <summary> /// We have a conflict, try to get the source row and generate a conflict /// </summary> private SyncConflict GetConflict(DmRow dmRow) { DmRow localRow = null; // Problem during operation // Getting the row involved in the conflict var localTable = GetRow(dmRow); ConflictType dbConflictType = ConflictType.ErrorsOccurred; // Can't find the row on the server datastore if (localTable.Rows.Count == 0) { if (ApplyType == DmRowState.Added) { dbConflictType = ConflictType.RemoteInsertLocalNoRow; } else if (ApplyType == DmRowState.Modified) { dbConflictType = ConflictType.RemoteUpdateLocalNoRow; } else if (ApplyType == DmRowState.Deleted) { dbConflictType = ConflictType.RemoteDeleteLocalNoRow; } } else { // We have a problem and found the row on the server side localRow = localTable.Rows[0]; var isTombstone = Convert.ToBoolean(localRow["sync_row_is_tombstone"]); // the row on local is deleted if (isTombstone) { if (ApplyType == DmRowState.Added) { dbConflictType = ConflictType.RemoteInsertLocalDelete; } else if (ApplyType == DmRowState.Modified) { dbConflictType = ConflictType.RemoteUpdateLocalDelete; } else if (ApplyType == DmRowState.Deleted) { dbConflictType = ConflictType.RemoteDeleteLocalDelete; } } else { var createTimestamp = localRow["create_timestamp"] != DBNull.Value ? (long)localRow["create_timestamp"] : 0L; var updateTimestamp = localRow["update_timestamp"] != DBNull.Value ? (long)localRow["update_timestamp"] : 0L; switch (ApplyType) { case DmRowState.Added: dbConflictType = updateTimestamp == 0 ? ConflictType.RemoteInsertLocalInsert : ConflictType.RemoteInsertLocalUpdate; break; case DmRowState.Modified: dbConflictType = updateTimestamp == 0 ? ConflictType.RemoteUpdateLocalInsert : ConflictType.RemoteUpdateLocalUpdate; break; case DmRowState.Deleted: dbConflictType = updateTimestamp == 0 ? ConflictType.RemoteDeleteLocalInsert : ConflictType.RemoteDeleteLocalUpdate; break; } } } // Generate the conflict var conflict = new SyncConflict(dbConflictType); conflict.AddRemoteRow(dmRow); if (localRow != null) { conflict.AddLocalRow(localRow); } localTable.Clear(); return(conflict); }
/// <summary> /// Try to apply changes on the server. /// Internally will call ApplyInsert / ApplyUpdate or ApplyDelete /// </summary> /// <param name="dmChanges">Changes from remote</param> /// <returns>every lines not updated on the server side</returns> internal int ApplyChanges(DmView dmChanges, ScopeInfo scope, List <SyncConflict> conflicts) { int appliedRows = 0; foreach (var dmRow in dmChanges) { bool operationComplete = false; try { if (ApplyType == DmRowState.Added) { operationComplete = this.ApplyInsert(dmRow, scope, false); if (operationComplete) { UpdateMetadatas(DbCommandType.InsertMetadata, dmRow, scope); } } else if (ApplyType == DmRowState.Modified) { operationComplete = this.ApplyUpdate(dmRow, scope, false); if (operationComplete) { UpdateMetadatas(DbCommandType.UpdateMetadata, dmRow, scope); } } else if (ApplyType == DmRowState.Deleted) { operationComplete = this.ApplyDelete(dmRow, scope, false); if (operationComplete) { UpdateMetadatas(DbCommandType.UpdateMetadata, dmRow, scope); } } if (operationComplete) { // if no pb, increment then go to next row appliedRows++; } else { // Generate a conflict and add it conflicts.Add(GetConflict(dmRow)); } } catch (Exception ex) { if (this.IsUniqueKeyViolation(ex)) { // Generate the conflict var conflict = new SyncConflict(ConflictType.UniqueKeyConstraint); // Add the row as Remote row conflict.AddRemoteRow(dmRow); // Get the local row var localTable = GetRow(dmRow); if (localTable.Rows.Count > 0) { conflict.AddLocalRow(localTable.Rows[0]); } conflicts.Add(conflict); localTable.Clear(); } else { throw; } } } return(appliedRows); }