protected virtual void OnApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs ev) { if (ApplyChangeFailed != null) { ApplyChangeFailed(sender, ev); } }
public void SynchronizeServer_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { if (e.Error != null) { Log.Error("[DistributedDb] Synchronize Server ApplyChangeFailed Error", e.Error, this); } }
public void ShowFailures(object syncOrchestrator, DbApplyChangeFailedEventArgs args) { ConflictForm form = new ConflictForm(); form.HandleConflict(fromPeer, toPeer, args); form.ShowDialog(); }
public void HandleConflict(string fromPeer, string toPeer, DbApplyChangeFailedEventArgs args) { _conflictArgs = args; this.Text = "Conflict Detected on " + toPeer; richTextBoxHelp.Text = "Conflict Resolution Guide: \n" + "Continue: Ignore the remote change [local change wins] \n " + "Force Write: Overwrite existing row [remote change wins]. For this option to work the sync commands need to use @sync_force_write parameter \n" + "Retry Next Sync: Record the conflict in the sync metadata and fetch it again in the next sync \n" + "Retry: Disabled, only used when custom code is written to make changes to the database to fix the conflict (i.e. constraint conflict) \n"; buttonRetry.Enabled = false; textBoxSyncStage.Text = _conflictArgs.Conflict.Stage.ToString(); textBoxConflictType.Text = _conflictArgs.Conflict.Type.ToString(); textBoxError.Text = _conflictArgs.Conflict.ErrorMessage; groupBoxRemoteChange.Text = "Remote Change from " + fromPeer; groupBoxLocalChange.Text = "Local Change on " + toPeer; if (_conflictArgs.Conflict.RemoteChange!= null) { dataGridServerChange.DataSource = _conflictArgs.Conflict.RemoteChange; } if (_conflictArgs.Conflict.LocalChange != null) { dataGridClientChange.DataSource = _conflictArgs.Conflict.LocalChange; } Application.DoEvents(); }
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { MessageBox.Show(e.Conflict.Type.ToString()); MessageBox.Show(e.Error.ToString()); }
void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { string strmsg = "Error Type: " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + e.Conflict.Type + "\n"; strmsg += "Error Message: " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + e.Error + "\n"; WriteLogFile(strmsg); }
private void Provider_ApplyingChanges(object sender, DbApplyChangeFailedEventArgs e) { if (e.Conflict.Type == DbConflictType.LocalUpdateRemoteUpdate) { int i = e.Conflict.LocalChange.Rows.Count; } else if (e.Conflict.Type == DbConflictType.LocalCleanedupDeleteRemoteUpdate) { e.Action = ApplyAction.Continue; } else if (e.Conflict.Type == DbConflictType.LocalDeleteRemoteUpdate) { e.Action = ApplyAction.RetryWithForceWrite; } else if (e.Conflict.Type == DbConflictType.LocalInsertRemoteInsert) { e.Action = ApplyAction.Continue; } else if (e.Conflict.Type == DbConflictType.LocalUpdateRemoteDelete) { e.Action = ApplyAction.Continue; } else if (e.Conflict.Type == DbConflictType.ErrorsOccurred) { Console.WriteLine(e.Error.Message); } else { Console.WriteLine(e.Error.Message); } }
public void HandleConflict(string fromPeer, string toPeer, DbApplyChangeFailedEventArgs args) { _conflictArgs = args; this.Text = "Conflict Detected on " + toPeer; richTextBoxHelp.Text = "Conflict Resolution Guide: \n" + "Continue: Ignore the remote change [local change wins] \n " + "Force Write: Overwrite existing row [remote change wins]. For this option to work the sync commands need to use @sync_force_write parameter \n" + "Retry Next Sync: Record the conflict in the sync metadata and fetch it again in the next sync \n" + "Retry: Disabled, only used when custom code is written to make changes to the database to fix the conflict (i.e. constraint conflict) \n"; buttonRetry.Enabled = false; textBoxSyncStage.Text = _conflictArgs.Conflict.Stage.ToString(); textBoxConflictType.Text = _conflictArgs.Conflict.Type.ToString(); textBoxError.Text = _conflictArgs.Conflict.ErrorMessage; groupBoxRemoteChange.Text = "Remote Change from " + fromPeer; groupBoxLocalChange.Text = "Local Change on " + toPeer; if (_conflictArgs.Conflict.RemoteChange != null) { dataGridServerChange.DataSource = _conflictArgs.Conflict.RemoteChange; } if (_conflictArgs.Conflict.LocalChange != null) { dataGridClientChange.DataSource = _conflictArgs.Conflict.LocalChange; } Application.DoEvents(); }
private void to_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { var toProvider = sender as RelationalSyncProvider; #if DEBUG if (toProvider.ScopeName == Scope.Icd.ToScopeString()) { return; } #endif if (e.Conflict.Type == DbConflictType.ErrorsOccurred) { Poster.PostMessage("ApplyChangeFailed. Error: {0}", e.Error); } else if (SyncTracer.IsVerboseEnabled() == false) { SyncTracer.Warning(1, "CONFLICT DETECTED FOR CLIENT {0}", toProvider.Connection); SyncTracer.Warning(2, "** Local change **"); SyncTracer.Warning(2, TableToStr(e.Conflict.LocalChange)); SyncTracer.Warning(2, "** Remote change **"); SyncTracer.Warning(2, TableToStr(e.Conflict.RemoteChange)); } if (!ConflictsCounter.Keys.Contains(e.Conflict.Type)) { ConflictsCounter[e.Conflict.Type] = 0; } ConflictsCounter[e.Conflict.Type]++; if (e.Conflict.Type != DbConflictType.ErrorsOccurred) { e.Action = ApplyAction.RetryWithForceWrite; } }
private void karPaypal_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { // display conflict type Console.WriteLine(e.Conflict.Type); // display error message Console.WriteLine(e.Error); }
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { // display conflict type Console.WriteLine(e.Conflict.Type); // display error message Console.WriteLine(e.Error); }
private void PrintChangeFailedEventArgs(object sender, DbApplyChangeFailedEventArgs e) { // display conflict type Console.WriteLine(e.Conflict.Type); // display error message Console.WriteLine(e.Error); }
private void ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { if (e.Conflict != null) { Log(e.Conflict.Type.ToString()); } if (e.Error != null) { Log(e.Error.ToString()); } }
public void SynchronizeClient_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { if (e.Conflict != null) { Log.Error("[DistributedDb] Synchronize Client ApplyChangeFailed [" + e.Conflict.RemoteChange.TableName + "] Conflict", new Exception(e.Conflict.Type.ToString()), this); } if (e.Error != null) { Log.Error("[DistributedDb] Synchronize Client ApplyChangeFailed [" + e.Conflict.RemoteChange.TableName + "] Error", e.Error, this); } }
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { if (e.Conflict.Type == DbConflictType.LocalInsertRemoteInsert) { //e.Action = ApplyAction.RetryWithForceWrite; e.Action = ApplyAction.Continue; } else { throw e.Error; } }
private static void sync_error_event_handler_local(object sender, DbApplyChangeFailedEventArgs e) { try { Debug.WriteLine("=========SYNC ERROR=========="); // display conflict type Debug.WriteLine(e.Conflict.Type); // display error message Debug.WriteLine(e.Error); Debug.WriteLine("=========END SYNC ERROR=========="); } catch (Exception ex) { Debug.WriteLine(ex); } }
protected void proveedor_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { String mensaje = string.Format( "{0}:{1}\n\r{2}:{3}\n\r{4}:{5}\n\r{6}:{7}\n\r{8}:{9}\n\r{10}:{11}\n\r", "\tSource Database :", e.Connection.Database, "\tContexto :", e.Context, "\tAction :", e.Action.ToString(), "\tSession Id :", e.Session.SessionId, "\tTransaccion :", e.Transaction.ToString(), "\tTipo de conflicto: ", e.Conflict.Type ); e.Action = ApplyAction.RetryWithForceWrite; //Aplicar cambios del lado del local this.loguear("proveedor_ApplyChangeFailed", mensaje); }
private void ServerApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { PrintChangeFailedEventArgs(sender, e); }
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { Console.WriteLine(e.Conflict.Type); Console.WriteLine(e.Error); }
private void dbProvider_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { //For conflict detection, the "local" database is the one at which the //ApplyChangeFailed event occurs. We determine at which database the event //fired and then compare the name of that database to the names of //the databases specified as the LocalProvider and RemoteProvider. string DbConflictDetected = e.Connection.Database.ToString(); string DbOther; DbOther = DbConflictDetected == _localProviderDatabase ? _remoteProviderDatabase : _localProviderDatabase; Console.WriteLine(String.Empty); Console.WriteLine("Conflict of type " + e.Conflict.Type + " was detected at " + DbConflictDetected + "."); //<snippetOCSv2_CS_Peer_Conflicts_LocalUpdateRemoteUpdate> if (e.Conflict.Type == DbConflictType.LocalUpdateRemoteUpdate) { //Get the conflicting changes from the Conflict object //and display them. The Conflict object holds a copy //of the changes; updates to this object will not be //applied. To make changes, use the Context object. DataTable conflictingRemoteChange = e.Conflict.RemoteChange; DataTable conflictingLocalChange = e.Conflict.LocalChange; int remoteColumnCount = conflictingRemoteChange.Columns.Count; int localColumnCount = conflictingLocalChange.Columns.Count; Console.WriteLine(String.Empty); Console.WriteLine(String.Empty); Console.WriteLine("Row from database " + DbConflictDetected); Console.Write(" | "); //Display the local row. As mentioned above, this is the row //from the database at which the conflict was detected. for (int i = 0; i < localColumnCount; i++) { Console.Write(conflictingLocalChange.Rows[0][i] + " | "); } Console.WriteLine(String.Empty); Console.WriteLine(String.Empty); Console.WriteLine(String.Empty); Console.WriteLine("Row from database " + DbOther); Console.Write(" | "); //Display the remote row. for (int i = 0; i < remoteColumnCount; i++) { Console.Write(conflictingRemoteChange.Rows[0][i] + " | "); } //Ask for a conflict resolution option. Console.WriteLine(String.Empty); Console.WriteLine(String.Empty); Console.WriteLine("Enter a resolution option for this conflict:"); Console.WriteLine("A = change from " + DbConflictDetected + " wins."); Console.WriteLine("B = change from " + DbOther + " wins."); string conflictResolution = Console.ReadLine(); conflictResolution.ToUpper(); if (conflictResolution == "A") { e.Action = ApplyAction.Continue; } else if (conflictResolution == "B") { e.Action = ApplyAction.RetryWithForceWrite; } else { Console.WriteLine(String.Empty); Console.WriteLine("Not a valid resolution option."); } } //</snippetOCSv2_CS_Peer_Conflicts_LocalUpdateRemoteUpdate> //Write any errors to a log file. //<snippetOCSv2_CS_Peer_Conflicts_ErrorsOccurred> else if (e.Conflict.Type == DbConflictType.ErrorsOccurred) { string logFile = @"C:\SyncErrorLog.txt"; Console.WriteLine(String.Empty); Console.WriteLine("An error occurred during synchronization."); Console.WriteLine("This error has been logged to " + logFile + "."); StreamWriter streamWriter = File.AppendText(logFile); StringBuilder outputText = new StringBuilder(); outputText.AppendLine("** APPLY CHANGE FAILURE AT " + DbConflictDetected.ToUpper() + " **"); outputText.AppendLine("Error source: " + e.Error.Source); outputText.AppendLine("Error message: " + e.Error.Message); streamWriter.WriteLine(DateTime.Now.ToShortTimeString() + " | " + outputText.ToString()); streamWriter.Flush(); streamWriter.Dispose(); } //</snippetOCSv2_CS_Peer_Conflicts_ErrorsOccurred> }
private void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { File.WriteAllText("Errors.txt", DateTime.Now.ToString() + '\n' + e.Conflict.Type.ToString() + ": " + e.Error.ToString()); }
private static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { // Both the server and the client have the same update on the same row, by default client (remote) wins if ((e.Conflict.Type == DbConflictType.LocalUpdateRemoteUpdate) && (e.Conflict.Stage == DbSyncStage.ApplyingUpdates)) { // If we use "ApplyAction.RetryWithForceWrite" the server will win the conflict... e.Action = ApplyAction.RetryWithForceWrite; // Otherwise (default behaviour), the client change will be written unto server (client wins) } else if ((e.Conflict.Type == DbConflictType.LocalInsertRemoteInsert) && (e.Conflict.Stage == DbSyncStage.ApplyingInserts)) { // If we use "ApplyAction.RetryWithForceWrite" the server will win the conflict... e.Action = ApplyAction.RetryWithForceWrite; // Otherwise (default behaviour), the client change will be written unto server (client wins) } else if ((e.Conflict.Type == DbConflictType.LocalDeleteRemoteDelete) && (e.Conflict.Stage == DbSyncStage.ApplyingDeletes)) { // If we use "ApplyAction.RetryWithForceWrite" the server will win the conflict... e.Action = ApplyAction.RetryWithForceWrite; // Otherwise (default behaviour), the client change will be written unto server (client wins) } else if ((e.Conflict.Type == DbConflictType.ErrorsOccurred) && (e.Conflict.Stage == DbSyncStage.ApplyingInserts)) { #region Custom logic for proof of concept // Custom logic proof of concept (unique constraint) // Cannot insert duplicate key row in object 'dbo.Region' with unique index 'IX_RegionDescription'. The duplicate key value is (UniqueConstraintRegionDescription). // The statement has been terminated. if (e.Conflict.ErrorMessage.Contains("Cannot insert duplicate key row in object") && (e.Conflict.ErrorMessage.Contains("with unique index"))) { // For table named "Region" we want [server] to always win when the type of conflict is: unique name constraint... // Additionally, all child relations from the [server] need to be written to the [client] database as well // Additionally, client side conflicting row needs to be deleted BUT its child relations need to be updated to the // [server] value if (e.Conflict.RemoteChange.TableName.Equals("Region")) { try { var conflictingUniqueConstraint = string.Empty; var serverRegionId = Guid.Empty; if (e.Connection.State == ConnectionState.Open) { // 1. get the conflicting unique constraint from the [server] if (e.Context.DataSet.Tables.Contains("Region")) { var dataTable = e.Context.DataSet.Tables["Region"]; for (var j = 0; j < dataTable.Rows.Count; j++) { var row = dataTable.Rows[j]; // check if the status is Completed if (!string.IsNullOrEmpty(row["RegionDescription"].ToString())) { conflictingUniqueConstraint = row["RegionDescription"].ToString(); } // get the unique Id of the conflicting row [server] serverRegionId = new Guid(row["RegionID"].ToString()); } } // 2. Get the [client] entity id (Guid) for the conflicting row (which will be different from the [server]s) var clientRegionId = string.Empty; var sqlTextSel = "select * from dbo.Region where RegionDescription=@description "; var myCommandSel = new SqlCommand(sqlTextSel, (SqlConnection)e.Connection, (SqlTransaction)e.Transaction); myCommandSel.Parameters.AddWithValue("@description", conflictingUniqueConstraint.TrimEnd()); var dr = myCommandSel.ExecuteReader(); while (dr.Read()) { clientRegionId = dr["RegionID"].ToString(); } // This should allow the [server] to insert its region data into the [client] database var sqlTextDeleteReg = "update dbo.Region set RegionDescription=@regionDescriptionConflicting where RegionID=@clientRegionId "; var myCommandDeleteReg = new SqlCommand(sqlTextDeleteReg, (SqlConnection)e.Connection, (SqlTransaction)e.Transaction); myCommandDeleteReg.Parameters.AddWithValue("@clientRegionId", clientRegionId); myCommandDeleteReg.Parameters.AddWithValue("@regionDescriptionConflicting", conflictingUniqueConstraint.TrimEnd() + "-Conflict-" + DateTime.Now); myCommandDeleteReg.ExecuteNonQuery(); //var sqlTextDeleteTer = // "update dbo.Territories set TerritoryDescription=territoryDescriptionConflicting where RegionID=@clientRegionId "; //var myCommandDeleteTer = new SqlCommand(sqlTextDeleteTer, (SqlConnection)e.Connection, // (SqlTransaction)e.Transaction); //myCommandDeleteTer.Parameters.AddWithValue("@clientRegionId", clientRegionId); //myCommandDeleteTer.Parameters.AddWithValue("@territoryDescriptionConflicting", conflictingClientTerritory.TrimEnd() + "-Conflict-" + DateTime.Now); //myCommandDeleteTer.ExecuteNonQuery(); //// var tempDataTable = new DataTable(); ////// create data adapter //// var da = new SqlDataAdapter(myCommandSel); ////// this will query your database and return the result to your datatable //// var res = da.Fill(tempDataTable); ////// var drc = tempDataTable.ParentRelations; //// var x = tempDataTable.Rows[0].GetChildRows("RegionID"); //// 3. Every [client] side child entity related via the said guid, needs to be **updated** to [server] side guid... //// need to start from the "childmost" and work our way up, in this extremely elaborate case, we only have the //// table named [Territory] that has a one to many relation to the [Region] table...no further tables are related //// making this a simple sample... // var sqlTextDeleteTer = // "update dbo.Territory set RegionID=@serverRegionID where RegionID=@clientRegionId "; // var myCommandDeleteTer = new SqlCommand(sqlTextDeleteTer, (SqlConnection) e.Connection, // (SqlTransaction) e.Transaction); // myCommandDeleteTer.Parameters.AddWithValue("@clientRegionId", clientRegionId); // myCommandDeleteTer.Parameters.AddWithValue("@serverRegionID", serverRegionId); // myCommandDeleteTer.ExecuteNonQuery(); //// 4. delete the row that has the conflicting unique constraint - from the [local] database //var sqlText = "delete from Region where RegionDescription=@description "; //var myCommand = new SqlCommand(sqlText, (SqlConnection) e.Connection, // (SqlTransaction) e.Transaction); //myCommand.Parameters.AddWithValue("@description", conflictingUniqueConstraint); //myCommand.ExecuteNonQuery(); } // 3. Let it sync (overwrite the client row with the server row // If we use "ApplyAction.RetryWithForceWrite" the server will win the conflict... e.Action = ApplyAction.RetryWithForceWrite; // However the server is powerless against unique key constraint validation scenario... e.Transaction.Commit(); // Problem: will client side entities that were just updated to feature server side guid be synced to the server? } catch (Exception regionExc) { e.Transaction.Rollback(); } } } #endregion // Otherwise (default behaviour), the client change will be written unto server (client wins) if (e.Conflict.ErrorMessage.Contains("The INSERT statement conflicted with the FOREIGN KEY constraint")) { } } }
private static void dbProvider_SyncProcessFailed(object sender, DbApplyChangeFailedEventArgs e) { //Write your code here }
/// <summary> /// On the client (remote) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Program_ChangeApplyFail(object sender, DbApplyChangeFailedEventArgs e) { // Summary: // The peer database threw an exception while applying a change. // ErrorsOccurred = 0, // // Summary: // The local and remote peers both updated the same row. // LocalUpdateRemoteUpdate = 1, // // Summary: // The local peer updated a row that the remote peer deleted. // LocalUpdateRemoteDelete = 2, // // Summary: // The local peer deleted a row that the remote peer updated. // LocalDeleteRemoteUpdate = 3, // // Summary: // The local and remote peers both inserted a row that has the same primary // key value. This caused a primary key violation. // LocalInsertRemoteInsert = 4, // // Summary: // The local and remote peers both deleted the same row. // LocalDeleteRemoteDelete = 5, // // Summary: // The local peer deleted a row that the remote peer updated, and the metadata // for that row was cleaned up. // LocalCleanedupDeleteRemoteUpdate = 6, switch (e.Conflict.Type) { case DbConflictType.ErrorsOccurred: var serverConflict = true; var stage = e.Conflict.Stage; if (e.Conflict.LocalChange != null) { serverConflict = true; } else if (e.Conflict.RemoteChange != null) { serverConflict = false; } // If we encounter an conflict while inserting server data into client database... if (!serverConflict && stage == DbSyncStage.ApplyingInserts) { // since we want server to win insert conflicts... if (e.Conflict.ErrorMessage.Contains("Violation of UNIQUE KEY constraint")) { // e.Action = ApplyAction.RetryWithForceWrite; // This wont work, constraint is a constraint } // In this case we need to forego changing/updating the root entity, BUT we want to be able to insert/update its children!!! // Root Entity on Master remains the same, but the children taken from the client must be inserted and related to the root entity... } break; case DbConflictType.LocalCleanedupDeleteRemoteUpdate: break; case DbConflictType.LocalDeleteRemoteDelete: break; case DbConflictType.LocalDeleteRemoteUpdate: break; case DbConflictType.LocalInsertRemoteInsert: e.Action = ApplyAction.Continue; break; case DbConflictType.LocalUpdateRemoteDelete: break; case DbConflictType.LocalUpdateRemoteUpdate: break; } // display conflict type Console.WriteLine(e.Conflict.ErrorMessage); }
protected abstract void SyncProviderOnApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs dbApplyChangeFailedEventArgs);
protected override void SyncProviderOnApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs dbApplyChangeFailedEventArgs) { ShowMessage("Local"); }
/// <summary> /// Handler for the ApplyChangedFailed event of the SqlSyncProvider class. This is used to record /// conflict information and apply the service conflict resolution policy. /// </summary> /// <param name="sender">Sender object</param> /// <param name="e">Event args</param> private void SqlSyncProviderApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { ApplyAction applyAction = ApplyAction.Continue; // Note: LocalChange table name may be null if the record does not exist on the server. So use the remote table name. string tableName = e.Conflict.RemoteChange.TableName; Type entityType = _configuration.TableGlobalNameToTypeMapping[tableName]; ConstructorInfo constructorInfo = entityType.GetConstructor(Type.EmptyTypes); // Handle Errors first if (null != e.Error) { var syncError = new SyncError { LiveEntity = (IOfflineEntity)constructorInfo.Invoke(null), ErrorEntity = (IOfflineEntity)constructorInfo.Invoke(null) }; //Note: When Error is not null, the conflict type should be ErrorsOccurred. Assert, just to make sure this is always correct. Debug.Assert(e.Conflict.Type == DbConflictType.ErrorsOccurred, "Conflict.Type is not ErrorsOccurred."); syncError.Description = e.Error.Message; // Fill in the error entity. This is the value of the client entity. _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], syncError.ErrorEntity); // Mark the error entity as a tombstone, if the client sent a delete. // Note: The DataRow.RowState property is not marked as deleted, so we cannot use that // to determine if the client change was a delete. if (e.Conflict.Stage == DbSyncStage.ApplyingDeletes) { syncError.ErrorEntity.ServiceMetadata.IsTombstone = true; } // Get the current version from the server. IOfflineEntity serverVersion = GetCurrentServerVersionForEntities( new List<IOfflineEntity> { syncError.ErrorEntity }, (SqlConnection)e.Connection, (SqlTransaction)e.Transaction) .FirstOrDefault(); // There is no item corresponding to the item sent from the client. // Example is an INSERT which caused an RI error and server does not have the record. if (null == serverVersion) { // If there is no server record and the client changes is not a tombstone, then // set the LiveEntity as a compensating action which is a tombstone. // This means that the client has to apply the delete locally // for data convergence. // If there is no server record and the client is a tombstone, then we ideally should // just ackowledge the action and don't send any response but // for now we will keep the sync error as is. _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], syncError.LiveEntity); syncError.LiveEntity.ServiceMetadata.IsTombstone = true; } else { syncError.LiveEntity = serverVersion; } if (this.ApplyClientChangeFailed != null) { this.ApplyClientChangeFailed(syncError.ErrorEntity); } // This will add the item as an exception to the server knowledge and will also send the exception to the client. // However the exception will be cleared the next time the client uploads changes, since we increment tickcounts always. applyAction = ApplyAction.Continue; // The ApplyChangesFailed event is fired when a conflict is detected. During resolution, // if change application fails due to some errors (such as RI, connectivity issues etc), // the provider fires the ApplyChangesFailed event again // to report an error. If we save the error entity as is, then both the original conflict and this new error // will be sent back to the client in the response. Since this is not desirable, we first need to remove the // corresponding conflict entity from the _conflicts collection before recording the error. // Note: item versions are not bumped since the conflict has not yet been resolved. RemoveEntityFromConflictCollection(tableName, syncError.LiveEntity); _syncErrors.Add(syncError); e.Action = applyAction; return; } // Create instances of OfflineCapableEntities and initialize the WinningChange and LosingChange properties. var c = new SyncConflict { LiveEntity = (IOfflineEntity)constructorInfo.Invoke(null), LosingEntity = (IOfflineEntity)constructorInfo.Invoke(null) }; ConflictResolutionPolicy policyToUse = _conflictResolutionPolicy; SyncConflictResolution? userResolution = null; // Check and fire any Conflict interceptors if (_configuration.HasConflictInterceptors(this._scopeName) || _configuration.HasTypedConflictInterceptor(this._scopeName, entityType)) { userResolution = GetUserConflictResolution(e, constructorInfo, entityType); if (userResolution != null && userResolution == SyncConflictResolution.ServerWins) { policyToUse = ConflictResolutionPolicy.ServerWins; } else if (userResolution != null && (userResolution == SyncConflictResolution.ClientWins || userResolution == SyncConflictResolution.Merge)) { // If resolution is Merge or ClientWins, set the resolution to ClientWins so the runtime will // retry with force write and save the merged values back policyToUse = ConflictResolutionPolicy.ClientWins; } } // If there were no Errors, then act based on the service conflict resolution policy. switch (policyToUse) { // ServerWins policy... case ConflictResolutionPolicy.ServerWins: // For OCS, ApplyAction.Continue means ServerWins (local change will be maintained). applyAction = ApplyAction.Continue; // If the local change exists, then save it in the WinningChange property if (null != e.Conflict.LocalChange && 1 == e.Conflict.LocalChange.Rows.Count) { _converter.GetEntityFromDataRow(e.Conflict.LocalChange.Columns, e.Conflict.LocalChange.Rows[0], c.LiveEntity); } // If local change does not exist else { _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], c.LiveEntity); c.LiveEntity.ServiceMetadata.IsTombstone = true; } // Save the remote change in the LosingChange property. if (1 == e.Conflict.RemoteChange.Rows.Count) { _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], c.LosingEntity); } // Save the conflict resolution policy. c.Resolution = WebUtil.GetSyncConflictResolution(ConflictResolutionPolicy.ServerWins); // Set the tombstone flag based on the type of the conflict. switch (e.Conflict.Type) { case DbConflictType.LocalDeleteRemoteDelete: c.LosingEntity.ServiceMetadata.IsTombstone = true; c.LiveEntity.ServiceMetadata.IsTombstone = true; break; case DbConflictType.LocalDeleteRemoteUpdate: c.LiveEntity.ServiceMetadata.IsTombstone = true; break; case DbConflictType.LocalUpdateRemoteDelete: c.LosingEntity.ServiceMetadata.IsTombstone = true; break; // No changes to the tombstone flag for other cases. default: break; } if (this.ApplyClientChangeFailed != null) { this.ApplyClientChangeFailed(c.LosingEntity); } break; // ClientWins policy... case ConflictResolutionPolicy.ClientWins: // For OCS, client change can be kept by using ApplyAction.RetryWithForceWrite. applyAction = ApplyAction.RetryWithForceWrite; if (1 == e.Conflict.RemoteChange.Rows.Count) { _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], c.LiveEntity); } // If the local change exists, then save it in the WinningChange property if (1 == e.Conflict.LocalChange.Rows.Count) { _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.LocalChange.Rows[0], c.LosingEntity); } // Save the conflict resolution policy. c.Resolution = userResolution ?? WebUtil.GetSyncConflictResolution(ConflictResolutionPolicy.ClientWins); // Set the tombstone flag based on the type of the conflict. switch (e.Conflict.Type) { case DbConflictType.LocalDeleteRemoteDelete: c.LosingEntity.ServiceMetadata.IsTombstone = true; c.LiveEntity.ServiceMetadata.IsTombstone = true; break; case DbConflictType.LocalDeleteRemoteUpdate: c.LosingEntity.ServiceMetadata.IsTombstone = true; break; case DbConflictType.LocalUpdateRemoteDelete: c.LiveEntity.ServiceMetadata.IsTombstone = true; break; // No changes to the tombstone flag for other cases. default: break; } if (this.ApplyClientChangeFailed != null) { this.ApplyClientChangeFailed(c.LiveEntity); } break; } // After deciding on the Live and the Losing entities for the conflict, we need to generate and save the SyncId // of the LiveEntity. This value is used later after all changes are applied to project on the latest // server knowledge and add positive exceptions to the updated client knowledge that is sent in the response. SyncId rowId = GenerateSyncIdForConflictingEntity(tableName, c.LiveEntity); if (!_conflictToSyncEntityIdMapping.ContainsKey(c)) { _conflictToSyncEntityIdMapping.Add(c, rowId); // Note: SyncId's are unique for each entity. Debug.Assert(!_syncEntityIdToConflictMapping.ContainsKey(rowId), "!_syncEntityIdToConflictMapping.ContainsKey(rowId)"); // Also fill the reverse mapping of syncId to the conflict entity. _syncEntityIdToConflictMapping.Add(rowId, c); } _conflicts.Add(c); e.Action = applyAction; }
private void ApplyLocalChangesFailedFn(object sender, DbApplyChangeFailedEventArgs e) { Console.WriteLine(e.Conflict.Type); Console.WriteLine(e.Error); }
private static void sync_error_event_handler_remote(object sender, DbApplyChangeFailedEventArgs e) { try { Debug.WriteLine("=========SYNC ERROR=========="); // display conflict type Debug.WriteLine(e.Conflict.Type); // display error message Debug.WriteLine(e.Error); Debug.WriteLine("=========END SYNC ERROR=========="); } catch (Exception ex) { Debug.WriteLine(ex); } }
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { // display conflict type Console.WriteLine(e.Conflict.Type); if (e.Conflict.Type == DbConflictType.LocalUpdateRemoteUpdate) { Console.WriteLine("diadeem"); } // display error message Console.WriteLine(e.Error); }
public static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e) { WriteErrorLog(string.Format("SCP Conflict.Type Error : {0}", e.Conflict.Type)); WriteErrorLog(string.Format("SCP Error : {0}", e.Error)); }
/// <summary> /// Invokes the users Conflict Interceptor and returns back with a resolution. /// </summary> /// <param name="e">Actual event args</param> /// <param name="constructorInfo">ConstructorInfo object</param> /// <param name="entityType">Entity type of the conflict</param> private SyncConflictResolution? GetUserConflictResolution(DbApplyChangeFailedEventArgs e, ConstructorInfo constructorInfo, Type entityType) { // Create the Client and Server entities IOfflineEntity clientVersion = (IOfflineEntity)constructorInfo.Invoke(null); IOfflineEntity serverVersion = null; // Read RemoteChange as client version _converter.GetEntityFromDataRow(e.Conflict.RemoteChange.Columns, e.Conflict.RemoteChange.Rows[0], clientVersion); // Set tombstone based on DbSyncConflict as the DataRow is always marked unchanged clientVersion.ServiceMetadata.IsTombstone = e.Conflict.Type == DbConflictType.LocalDeleteRemoteDelete || e.Conflict.Type == DbConflictType.LocalUpdateRemoteDelete; if (e.Conflict.LocalChange != null && e.Conflict.LocalChange.Rows.Count > 0) { // Server row exists. Create memory for row. serverVersion = (IOfflineEntity)constructorInfo.Invoke(null); _converter.GetEntityFromDataRow(e.Conflict.LocalChange.Columns, e.Conflict.LocalChange.Rows[0], serverVersion); // Set tombstone based on DbSyncConflict as the DataRow is always marked unchanged serverVersion.ServiceMetadata.IsTombstone = e.Conflict.Type == DbConflictType.LocalDeleteRemoteDelete || e.Conflict.Type == DbConflictType.LocalDeleteRemoteUpdate; } IOfflineEntity mergedVersion = null; this._conflictContext.ClientChange = clientVersion; this._conflictContext.ServerChange = serverVersion; SyncConflictResolution? userResolution = this._configuration.InvokeConflictInterceptor( this._conflictContext, entityType, out mergedVersion); // Check to see the resolution. if (userResolution != null && userResolution == SyncConflictResolution.Merge) { // Check that mergedVersion is not null and is of the expected type if (mergedVersion == null) { throw new InvalidOperationException("User SyncConflictInterceptor returned a conflict resolution of 'Merge' but did not specify a merged version."); } if (mergedVersion.GetType() != clientVersion.GetType()) { throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "User SyncConflictInterceptor returned merged version entity type '{0} does not match required type '{1}'.", mergedVersion.GetType().Name, clientVersion.GetType().Name)); } // Merge is required // If merge is requested then the server version is always the losing version as changes are overwritten. In this case // set the policy to clientWins and copy the mergedVersion values in to the Args.Conflict.RemoteChange object[] rowValues = _converter.CopyEntityToDataRow(mergedVersion, e.Conflict.RemoteChange); // Now add this row back to the DataSet to we can retry applying this. _converter.MergeChangeInToDataSet(e.Context.DataSet.Tables[e.Conflict.RemoteChange.TableName], e.Conflict.RemoteChange.Rows[0], rowValues, mergedVersion.GetType()); } return userResolution; }
public void ShowFailures(object syncOrchestrator, DbApplyChangeFailedEventArgs args) { args.Action = ApplyAction.Continue; }
private static void dbProvider_SyncProcessFailed (object sender, DbApplyChangeFailedEventArgs e) { //Common.WriteLog(System.DateTime.Now.ToString() + e.Context.ToString()); }