Beispiel #1
0
        /// <summary>
        /// Get the conflict item
        /// </summary>
        /// <returns>Conflict item</returns>
        public virtual Conflict GetConflict()
        {
            if (!HasConflict())
            {
                return(null);
            }

            Conflict conflict;

            if (currentEntryWrapper.IsConflict)
            {
                conflict = new SyncConflict
                {
                    LiveEntity   = liveEntity,
                    LosingEntity = ReflectionUtility.GetObjectForType(currentEntryWrapper.ConflictWrapper, knownTypes),
                    Resolution   =
                        (SyncConflictResolution)
                        Enum.Parse(FormatterConstants.SyncConflictResolutionType,
                                   currentEntryWrapper.ConflictDesc, true)
                };
            }
            else
            {
                conflict = new SyncError
                {
                    LiveEntity  = liveEntity,
                    ErrorEntity =
                        ReflectionUtility.GetObjectForType(currentEntryWrapper.ConflictWrapper,
                                                           knownTypes),
                    Description = currentEntryWrapper.ConflictDesc
                };
            }

            return(conflict);
        }
Beispiel #2
0
 public ApplyChangeFailedEventArgs(SyncConflict dbSyncConflict, ApplyAction action, DbConnection connection, DbTransaction transaction)
 {
     this._syncConflict = dbSyncConflict;
     this._connection   = connection;
     this._transaction  = transaction;
     this._applyAction  = action;
 }
Beispiel #3
0
        /// <summary>
        /// Get the conflict item
        /// </summary>
        /// <returns>Conflict item</returns>
        public virtual Conflict GetConflict()
        {
            if (!HasConflict())
            {
                return(null);
            }

            Conflict conflict = null;

            if (_currentEntryWrapper.IsConflict)
            {
                conflict = new SyncConflict
                {
                    LiveEntity   = _liveEntity,
                    LosingEntity = CreateEntity(_currentEntryWrapper.ConflictWrapper, _knownTypesDict),
                    Resolution   = (SyncConflictResolution)Enum.Parse(FormatterConstants.SyncConflictResolutionType, _currentEntryWrapper.ConflictDesc, true)
                };
            }
            else
            {
                conflict = new SyncError
                {
                    LiveEntity  = _liveEntity,
                    ErrorEntity = CreateEntity(_currentEntryWrapper.ConflictWrapper, _knownTypesDict),
                    Description = _currentEntryWrapper.ConflictDesc
                };
            }

            return(conflict);
        }
Beispiel #4
0
        /// <summary>
        ///  Sets the sync error to null. This method is not thread safe.
        /// </summary>
        internal void UnsafeClearSyncError()
        {
            syncConflict = null;

            OnPropertyChanged("SyncConflict");
            OnPropertyChanged("HasSyncConflict");
        }
 /// <summary>
 /// Called by SyncErrorInfo from the ClearSyncConflict method.  Used to remove the conflict
 /// from the collection and from the disk
 /// </summary>
 /// <param name="conflict">conflict to clear</param>
 internal void ClearSyncConflict(SyncConflict conflict)
 {
     using (_saveSyncLock.LockObject())
     {
         _cacheData.RemoveSyncConflict(conflict);
         _storageHandler.ClearSyncConflict((IsolatedStorageSyncConflict)conflict);
     }
 }
Beispiel #6
0
        /// <summary>
        /// Clears the sync conflict and removes it from the conflict list on the c.
        /// </summary>
        public async Task ClearSyncConflict()
        {
            if (syncConflict != null)
            {
                await context.ClearSyncConflict(syncConflict);

                syncConflict = null;

                OnPropertyChanged("SyncConflict");
                OnPropertyChanged("HasSyncConflict");
            }
        }
Beispiel #7
0
        /// <summary>
        /// Adds a conflict to the list of in-memory
        /// </summary>
        /// <param name="conflict">Conflict to add</param>
        /// <param name="context">Context for which the conflict is being added</param>
        public void AddSyncConflict(SyncConflict conflict, WinEightContext context)
        {
            OfflineEntity entity      = Collections[conflict.LiveEntity.GetType()].AddOrUpdateSyncEntity((OfflineEntity)conflict.LiveEntity);
            SyncConflict  oldConflict = Collections[conflict.LiveEntity.GetType()].MapSyncConflict(entity, conflict, context);

            SyncConflicts.Add(conflict);

            if (oldConflict != null)
            {
                ClearSyncConflict(oldConflict, context);
            }
        }
Beispiel #8
0
        public void AddSerializedConflict(SyncConflict conflict, IsolatedStorageOfflineContext context)
        {
            Type entityType = conflict.LiveEntity.GetType();
            IsolatedStorageOfflineEntity entity      = Collections[entityType].AddOrUpdateSyncEntity((IsolatedStorageOfflineEntity)conflict.LiveEntity);
            IsolatedStorageSyncConflict  oldConflict = (IsolatedStorageSyncConflict)Collections[entityType].MapSyncConflict(entity, conflict, context);

            SyncConflicts.Add(conflict);

            if (oldConflict != null)
            {
                ClearSyncConflict(oldConflict, context);
            }
        }
Beispiel #9
0
        /// <summary>
        /// Sets the sync conflict, providing the cache data so that the conflict can be removed if ClearSyncConflict
        /// is called.
        /// </summary>
        ///<param name="c">IsolatedStorageOfflineContext</param>
        /// <param name="sc">conflict to set.</param>
        internal void SetSyncConflict(WinEightContext c, SyncConflict sc)
        {
            SyncConflict oldConflict = this.syncConflict;

            this.context      = c;
            this.syncConflict = sc;

            OnPropertyChanged("SyncConflict");

            if (oldConflict == null)
            {
                OnPropertyChanged("HasSyncConflict");
            }
        }
Beispiel #10
0
        /// <summary>
        /// Called by SyncErrorInfo from the ClearSyncConflict method.  Used to remove the conflict
        /// from the collection and from the disk
        /// </summary>
        /// <param name="conflict">conflict to clear</param>
        internal async Task ClearSyncConflict(SyncConflict conflict)
        {
            using (saveSyncLock.LockObject())
            {
                WinEightSyncConflict winEightSyncConflict = conflict as WinEightSyncConflict;

                if (winEightSyncConflict == null)
                {
                    return;
                }

                cacheData.RemoveSyncConflict(conflict);
                await StorageHandler.ClearSyncConflict(winEightSyncConflict);
            }
        }
Beispiel #11
0
        /// <summary>
        /// Sets the sync conflict, providing the cache data so that the conflict can be removed if ClearSyncConflict
        /// is called.
        /// </summary>
        ///<param name="context">IsolatedStorageOfflineContext</param>
        /// <param name="syncConflict">conflict to set.</param>
        internal void SetSyncConflict(
            IsolatedStorageOfflineContext context,
            SyncConflict syncConflict)
        {
            SyncConflict oldConflict = this._syncConflict;

            this._context      = context;
            this._syncConflict = syncConflict;

            OnPropertyChanged("SyncConflict");

            if (oldConflict == null)
            {
                OnPropertyChanged("HasSyncConflict");
            }
        }
Beispiel #12
0
 private void ClearSyncConflict(SyncConflict syncConflict, IsolatedStorageOfflineContext context)
 {
 }
Beispiel #13
0
 public void RemoveSyncConflict(SyncConflict conflict)
 {
 }
 /// <summary>
 /// Removes the specified sync conflict
 /// </summary>
 /// <param name="conflict">Conflict to remove</param>
 public void RemoveSyncConflict(SyncConflict conflict)
 {
     SyncConflicts.Remove(conflict);
 }
Beispiel #15
0
 private void ClearSyncConflict(SyncConflict syncConflict, OfflineContext context)
 {
 }
Beispiel #16
0
        private SyncConflict CreateSyncError(UpdateResult r, DataRow clientRow)
        {
            Microsoft.Synchronization.SyncStage syncStage = Microsoft.Synchronization.SyncStage.UploadingChanges;

            switch (r.Command)
            {
            case "New":
                syncStage = Microsoft.Synchronization.SyncStage.ApplyingInserts;
                break;

            case "Update":
                syncStage = Microsoft.Synchronization.SyncStage.ApplyingUpdates;
                break;

            case "Delete":
                syncStage = Microsoft.Synchronization.SyncStage.ApplyingDeletes;
                break;
            }

            SyncConflict conflict;

            if (r.ErrorCode == UpdateResult.VersionConflict)
            {
                if (r.Command == "Update")
                {
                    conflict = new SyncConflict(ConflictType.ClientUpdateServerUpdate, syncStage);
                }
                else if (r.Command == "Delete")
                {
                    conflict = new SyncConflict(ConflictType.ClientDeleteServerUpdate, syncStage);
                }
                else
                {
                    conflict = new SyncConflict(ConflictType.Unknown, syncStage);
                }

                if (r.ItemData != null)
                {
                    Exception e;
                    DataRow   serverRow = clientRow.Table.NewRow();
                    MapListItemToDataRow(r.ItemData, serverRow, out e);
                    if (e == null)
                    {
                        conflict.ServerChange           = serverRow.Table.Clone();
                        conflict.ServerChange.TableName = this.TableName;
                        conflict.ServerChange.Rows.Add(serverRow);
                    }
                }
            }
            else if (r.ErrorCode == UpdateResult.ItemDeleted)
            {
                conflict = new SyncConflict(ConflictType.ClientUpdateServerDelete, syncStage);
            }
            else
            {
                conflict = new SyncConflict(ConflictType.ErrorsOccurred, syncStage);
            }

            if (conflict.ClientChange == null)
            {
                conflict.ClientChange           = clientRow.Table.Clone();
                conflict.ClientChange.TableName = clientRow.Table.TableName;
            }

            conflict.ClientChange.ImportRow(clientRow);
            conflict.ErrorMessage = r.ErrorMessage;

            return(conflict);
        }
Beispiel #17
0
        /// #UPLOAD 2
        private void ApplyChangesInternal(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession, SyncContext syncContext)
        {
            SyncStage syncStage = SyncStage.UploadingChanges;

            foreach (SyncTableMetadata tableMetadata in groupMetadata.TablesMetadata)
            {
                SpSyncAdapter adapter = null;

                if (this.SyncAdapters.Contains(tableMetadata.TableName))
                {
                    adapter = this.SyncAdapters[tableMetadata.TableName];
                }

                if (adapter == null)
                {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
                                                              Messages.InvalidTableName, tableMetadata.TableName));
                }


                // SpSyncAnchor anchor
                if (!dataSet.Tables.Contains(tableMetadata.TableName))
                {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
                                                              Messages.TableNotInSchema, tableMetadata.TableName));
                }

                SyncTableProgress tableProgress = syncContext.GroupProgress.FindTableProgress(tableMetadata.TableName);

                DataTable dataTable = dataSet.Tables[tableMetadata.TableName];

                try
                {
                    Collection <SyncConflict> conflicts;
                    int changesCount = dataTable.Rows.Count;
                    adapter.Update(dataTable, Connection, out conflicts);

                    if (conflicts != null)
                    {
                        foreach (SyncConflict conflict in conflicts)
                        {
                            ApplyChangeFailedEventArgs failureArgs = new ApplyChangeFailedEventArgs(tableMetadata, conflict, null, syncSession, syncContext, Connection, null);
                            OnApplyChangeFailed(failureArgs);

                            if (failureArgs.Action == ApplyAction.Continue)
                            {
                                if (conflict != null)
                                {
                                    tableProgress.ChangesFailed++;
                                    tableProgress.Conflicts.Add(conflict);
                                }
                            }
                        }
                    }
                    tableProgress.ChangesApplied = changesCount - tableProgress.ChangesFailed;
                }
                catch (Exception e)
                {
                    SyncConflict conflict = new SyncConflict(ConflictType.ErrorsOccurred, SyncStage.UploadingChanges)
                    {
                        ErrorMessage = e.Message + ", InnerException:" + e.InnerException.ToString(), ServerChange = dataTable, ClientChange = dataTable
                    };
                    ApplyChangeFailedEventArgs failureArgs = new ApplyChangeFailedEventArgs(tableMetadata, conflict, null, syncSession, syncContext, Connection, null);
                    OnApplyChangeFailed(failureArgs);
                    // handle errors?
                    if (SyncTracer.IsErrorEnabled())
                    {
                        SyncTracer.Error(e.ToString());
                    }
                }

                SyncProgressEventArgs args = new SyncProgressEventArgs(tableMetadata, tableProgress, groupMetadata, syncContext.GroupProgress, syncStage);
                OnSyncProgress(args);
            }
        }
Beispiel #18
0
 private async void ClearSyncConflict(SyncConflict syncConflict, WinEightContext context)
 {
     RemoveSyncConflict(syncConflict);
     await context.ClearSyncConflict(syncConflict);
 }
Beispiel #19
0
 /// <summary>
 /// Removes the specified sync conflict
 /// </summary>
 /// <param name="conflict">Conflict to remove</param>
 public void RemoveSyncConflict(SyncConflict conflict)
 {
     SyncConflicts.Remove(conflict);
 }
Beispiel #20
0
 private void ClearSyncConflict(SyncConflict syncConflict, IsolatedStorageOfflineContext context)
 {
     RemoveSyncConflict(syncConflict);
     context.StorageHandler.ClearSyncConflict((IsolatedStorageSyncConflict)syncConflict);
 }
Beispiel #21
0
        private SyncConflict CreateSyncError(UpdateResult r, DataRow clientRow)
        {
            Microsoft.Synchronization.SyncStage syncStage = Microsoft.Synchronization.SyncStage.UploadingChanges;

            switch (r.Command)
            {
                case "New":
                    syncStage = Microsoft.Synchronization.SyncStage.ApplyingInserts;
                    break;
                case "Update":
                    syncStage = Microsoft.Synchronization.SyncStage.ApplyingUpdates;
                    break;
                case "Delete":
                    syncStage = Microsoft.Synchronization.SyncStage.ApplyingDeletes;
                    break;
            }

            SyncConflict conflict;

            if (r.ErrorCode == UpdateResult.VersionConflict)
            {
                if (r.Command == "Update")
                    conflict = new SyncConflict(ConflictType.ClientUpdateServerUpdate, syncStage);
                else if (r.Command == "Delete")
                    conflict = new SyncConflict(ConflictType.ClientDeleteServerUpdate, syncStage);
                else
                    conflict = new SyncConflict(ConflictType.Unknown, syncStage);

                if (r.ItemData != null)
                {
                    Exception e;
                    DataRow serverRow = clientRow.Table.NewRow();
                    MapListItemToDataRow(r.ItemData, serverRow, out e);
                    if (e == null)
                    {
                        conflict.ServerChange = serverRow.Table.Clone();
                        conflict.ServerChange.TableName = this.TableName;
                        conflict.ServerChange.Rows.Add(serverRow);
                    }
                }
            }
            else if (r.ErrorCode == UpdateResult.ItemDeleted)
            {
                conflict = new SyncConflict(ConflictType.ClientUpdateServerDelete, syncStage);
            }
            else
            {
                conflict = new SyncConflict(ConflictType.ErrorsOccurred, syncStage);
            }

            if (conflict.ClientChange == null)
            {
                conflict.ClientChange = clientRow.Table.Clone();
                conflict.ClientChange.TableName = clientRow.Table.TableName;
            }

            conflict.ClientChange.ImportRow(clientRow);
            conflict.ErrorMessage = r.ErrorMessage;

            return conflict;
        }
 public abstract SyncConflict MapSyncConflict(IsolatedStorageOfflineEntity entity, SyncConflict conflict, IsolatedStorageOfflineContext context);
 public IsolatedStorageSyncConflict(SyncConflict conflict)
 {
     this.LiveEntity = conflict.LiveEntity;
     this.LosingEntity = conflict.LosingEntity;
     this.Resolution = conflict.Resolution;
 }
 public IsolatedStorageSyncConflict(SyncConflict conflict)
 {
     this.LiveEntity   = conflict.LiveEntity;
     this.LosingEntity = conflict.LosingEntity;
     this.Resolution   = conflict.Resolution;
 }
 private void ClearSyncConflict(SyncConflict syncConflict, IsolatedStorageOfflineContext context)
 {
     RemoveSyncConflict(syncConflict);
     context.StorageHandler.ClearSyncConflict((IsolatedStorageSyncConflict)syncConflict);
 }
 public abstract SyncConflict MapSyncConflict(IsolatedStorageOfflineEntity entity, SyncConflict conflict, IsolatedStorageOfflineContext context);
Beispiel #27
0
        /// <summary>
        /// Get the conflict item
        /// </summary>
        /// <returns>Conflict item</returns>
        public virtual Conflict GetConflict()
        {
            if (!HasConflict())
            {
                return null;
            }

            Conflict conflict;

            if (currentEntryWrapper.IsConflict)
            {
                conflict = new SyncConflict
                {
                    LiveEntity = liveEntity,
                    LosingEntity = ReflectionUtility.GetObjectForType(currentEntryWrapper.ConflictWrapper, knownTypes),
                    Resolution =
                        (SyncConflictResolution)
                        Enum.Parse(FormatterConstants.SyncConflictResolutionType,
                                    currentEntryWrapper.ConflictDesc, true)
                };
            }
            else
            {
                conflict = new SyncError
                {
                    LiveEntity = liveEntity,
                    ErrorEntity =
                        ReflectionUtility.GetObjectForType(currentEntryWrapper.ConflictWrapper,
                                                            knownTypes),
                    Description = currentEntryWrapper.ConflictDesc
                };
            }

            return conflict;
        }
Beispiel #28
0
        /// <summary>
        /// Handle a conflict
        /// </summary>
        internal ChangeApplicationAction HandleConflict(SyncConflict conflict, ScopeInfo scope, long timestamp, out DmRow finalRow)
        {
            finalRow = null;

            // overwrite apply action if we handle it (ie : user wants to change the action)
            if (this.ConflictActionInvoker != null)
            {
                ConflictApplyAction = this.ConflictActionInvoker(conflict, Connection, Transaction);
            }

            // Default behavior and an error occured
            if (ConflictApplyAction == ApplyAction.Rollback)
            {
                Logger.Current.Info("Rollback all operation");

                return(ChangeApplicationAction.Rollback);
            }

            // Server wins
            if (ConflictApplyAction == ApplyAction.Continue)
            {
                Logger.Current.Info("Local Wins, update metadata");


                // COnflict on a line that is not present on the datasource
                if (conflict.LocalChange == null || conflict.LocalChange.Rows == null || conflict.LocalChange.Rows.Count == 0)
                {
                    return(ChangeApplicationAction.Continue);
                }

                if (conflict.LocalChange != null && conflict.LocalChange.Rows != null && conflict.LocalChange.Rows.Count > 0)
                {
                    var localRow = conflict.LocalChange.Rows[0];
                    // TODO : Différencier le timestamp de mise à jour ou de création
                    var updateMetadataCommand = GetCommand(DbObjectType.UpdateMetadataProcName);

                    // create a localscope to override values
                    var localScope = new ScopeInfo {
                        Name = null, LastTimestamp = timestamp
                    };

                    var rowsApplied = this.InsertOrUpdateMetadatas(updateMetadataCommand, localRow, localScope);

                    if (rowsApplied < 1)
                    {
                        throw new Exception("No metadatas rows found, can't update the server side");
                    }

                    finalRow = localRow;

                    return(ChangeApplicationAction.Continue);
                }

                // tableProgress.ChangesFailed += 1;
                return(ChangeApplicationAction.Rollback);
            }

            // We gonna apply with force the remote line
            if (ConflictApplyAction == ApplyAction.RetryWithForceWrite)
            {
                if (conflict.RemoteChange.Rows.Count == 0)
                {
                    Logger.Current.Error("Cant find a remote row");
                    return(ChangeApplicationAction.Rollback);
                }

                var  row = conflict.RemoteChange.Rows[0];
                bool operationComplete = false;

                // create a localscope to override values
                var localScope = new ScopeInfo {
                    Name = scope.Name, LastTimestamp = timestamp
                };


                if (conflict.Type == ConflictType.LocalNoRowRemoteUpdate || conflict.Type == ConflictType.LocalNoRowRemoteInsert)
                {
                    operationComplete = this.ApplyInsert(row);
                }
                else if (applyType == DmRowState.Added)
                {
                    operationComplete = this.ApplyInsert(row);
                }
                else if (applyType == DmRowState.Modified)
                {
                    operationComplete = this.ApplyUpdate(row, localScope, true);
                }
                else if (applyType == DmRowState.Deleted)
                {
                    operationComplete = this.ApplyDelete(row, localScope, true);
                }

                var insertMetadataCommand = GetCommand(DbObjectType.InsertMetadataProcName);
                var rowsApplied           = this.InsertOrUpdateMetadatas(insertMetadataCommand, row, localScope);
                if (rowsApplied < 1)
                {
                    throw new Exception("No metadatas rows found, can't update the server side");
                }

                finalRow = row;

                //After a force update, there is a problem, so raise exception
                if (!operationComplete)
                {
                    var ex = $"Can't force operation for applyType {applyType}";
                    Logger.Current.Error(ex);
                    finalRow = null;
                    return(ChangeApplicationAction.Rollback);
                }

                // tableProgress.ChangesApplied += 1;
                return(ChangeApplicationAction.Continue);
            }

            return(ChangeApplicationAction.Rollback);
        }
Beispiel #29
0
 public object Convert(object value, Type targetType, object parameter, string language)
 {
     return(SyncConflict.GetConflictMessage((ConflictType)value));
 }
Beispiel #30
0
 public void AddSerializedConflict(SyncConflict conflict, OfflineContext context)
 {
 }
Beispiel #31
0
 public ConflictEventArgs(Command client, Command server, SyncConflict conflict)
 {
     this.client = client;
     this.server = server;
     this.conflict = conflict;
 }
Beispiel #32
0
 public abstract SyncConflict MapSyncConflict(OfflineEntity entity, SyncConflict conflict,
                                              WinEightContext context);
Beispiel #33
0
 public WinEightSyncConflict(SyncConflict conflict)
 {
     this.LiveEntity   = conflict.LiveEntity;
     this.LosingEntity = conflict.LosingEntity;
     this.Resolution   = conflict.Resolution;
 }
        /// <summary>
        /// Sets the sync conflict, providing the cache data so that the conflict can be removed if ClearSyncConflict
        /// is called.
        /// </summary>
        ///<param name="context">IsolatedStorageOfflineContext</param>
        /// <param name="syncConflict">conflict to set.</param>
        internal void SetSyncConflict(
            IsolatedStorageOfflineContext context,
            SyncConflict syncConflict)
        {
            SyncConflict oldConflict = this._syncConflict;

            this._context = context;
            this._syncConflict = syncConflict;

            OnPropertyChanged("SyncConflict");

            if (oldConflict == null)
            {
                OnPropertyChanged("HasSyncConflict");
            }
        }
Beispiel #35
0
        /// <summary>
        /// We have a conflict, try to get the source (server) row and generate a conflict
        /// </summary>
        private SyncConflict GetConflict(DmRow dmRow)
        {
            DmRow destinationRow = null;

            // Problem during operation
            // Getting the row involved in the conflict
            var dmTableSelected = GetRow(dmRow);

            ConflictType dbConflictType = ConflictType.ErrorsOccurred;

            // Can't find the row on the local datastore
            if (dmTableSelected.Rows.Count == 0)
            {
                var errorMessage = "Change Application failed due to Row not Found on the server";

                Logger.Current.Error($"Conflict detected with error: {errorMessage}");

                if (applyType == DmRowState.Added)
                {
                    dbConflictType = ConflictType.LocalNoRowRemoteInsert;
                }
                else if (applyType == DmRowState.Modified)
                {
                    dbConflictType = ConflictType.LocalNoRowRemoteUpdate;
                }
                else if (applyType == DmRowState.Deleted)
                {
                    dbConflictType = ConflictType.LocalNoRowRemoteDelete;
                }
            }
            else
            {
                // We have a problem and found the row on the server side
                destinationRow = dmTableSelected.Rows[0];

                var isTombstone = (bool)destinationRow["sync_row_is_tombstone"];

                // the row on local is deleted
                if (isTombstone)
                {
                    if (applyType == DmRowState.Added)
                    {
                        dbConflictType = ConflictType.LocalDeleteRemoteInsert;
                    }
                    else if (applyType == DmRowState.Modified)
                    {
                        dbConflictType = ConflictType.LocalDeleteRemoteUpdate;
                    }
                    else if (applyType == DmRowState.Deleted)
                    {
                        dbConflictType = ConflictType.LocalDeleteRemoteDelete;
                    }
                }
                else
                {
                    var isLocallyCreated = destinationRow["create_scope_id"] == DBNull.Value;
                    var islocallyUpdated = destinationRow["update_scope_id"] == DBNull.Value;

                    if (applyType == DmRowState.Added && islocallyUpdated)
                    {
                        dbConflictType = ConflictType.LocalUpdateRemoteInsert;
                    }
                    else if (applyType == DmRowState.Added && isLocallyCreated)
                    {
                        dbConflictType = ConflictType.LocalInsertRemoteInsert;
                    }
                    else if (applyType == DmRowState.Modified && islocallyUpdated)
                    {
                        dbConflictType = ConflictType.LocalUpdateRemoteUpdate;
                    }
                    else if (applyType == DmRowState.Modified && isLocallyCreated)
                    {
                        dbConflictType = ConflictType.LocalInsertRemoteUpdate;
                    }
                    else if (applyType == DmRowState.Deleted && islocallyUpdated)
                    {
                        dbConflictType = ConflictType.LocalUpdateRemoteDelete;
                    }
                    else if (applyType == DmRowState.Deleted && isLocallyCreated)
                    {
                        dbConflictType = ConflictType.LocalInsertRemoteDelete;
                    }
                }
            }


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

            conflict.AddRemoteRow(dmRow);
            if (destinationRow != null)
            {
                conflict.AddLocalRow(destinationRow);
            }

            dmTableSelected.Clear();

            return(conflict);
        }
Beispiel #36
0
 public void AddSerializedConflict(SyncConflict conflict, IsolatedStorageOfflineContext context)
 {
 }
        /// <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;
        }
        public void AddSerializedConflict(SyncConflict conflict, IsolatedStorageOfflineContext context)
        {
            Type entityType = conflict.LiveEntity.GetType();
            IsolatedStorageOfflineEntity entity = Collections[entityType].AddOrUpdateSyncEntity((IsolatedStorageOfflineEntity)conflict.LiveEntity);
            IsolatedStorageSyncConflict oldConflict = (IsolatedStorageSyncConflict)Collections[entityType].MapSyncConflict(entity, conflict, context);

            SyncConflicts.Add(conflict);

            if (oldConflict != null)
            {
                ClearSyncConflict(oldConflict,context);
            }
        }