/// <summary> /// OfflineSyncProvider method implementation called when a change set returned from GetChangeSet has been /// successfully uploaded. /// </summary> /// <param name="state">The unique identifier passed in to the GetChangeSet call.</param> /// <param name="response">ChangeSetResponse that contains an updated server blob and any conflicts or errors that /// happened on the service.</param> public override async Task OnChangeSetUploaded(Guid state, ChangeSetResponse response) { ThrowIfDisposed(); if (response == null) { throw new ArgumentNullException("response"); } if (!syncActive) { throw new InvalidOperationException("OnChangeSetUploaded cannot be called without calling BeginSession"); } if (response.Error == null) { IEnumerable <OfflineEntity> updatedItems = response.UpdatedItems.Cast <OfflineEntity>(); // Notify the disk management that changes uploaded successfully. IEnumerable <Conflict> conflicts = await StorageHandler.UploadSucceeded(state, response.ServerBlob, response.Conflicts, updatedItems); // Update the in-memory representation. cacheData.AddUploadChanges(response.ServerBlob, conflicts, updatedItems, this); } else { StorageHandler.UploadFailed(state); } }
public async Task <bool> Handle(ChangeSetRequest message, IOutputPort <ChangeSetResponse> outputPort) { ChangeSetResponse response = new ChangeSetResponse(message); try { CRUDContext <TService> context = new CRUDContext <TService>(message, response, (TService)_service, _serviceContainer); context.Properties.Add(CRUDContext <TService> .CHANGE_METHODS_KEY, _serviceMethods); await _pipeline(context); } catch (Exception ex) { if (ex is TargetInvocationException) { ex = ex.InnerException; } string err = _serviceMethods.OnError(ex); response.error = new ErrorInfo(err, ex.GetType().Name); } outputPort.Handle(response); return(response.error == null); }
public CRUDContext( ChangeSetRequest request, ChangeSetResponse response, TService service, IServiceContainer <TService> serviceContainer) { _ExceptionInfo = null; Request = request; Response = response; Service = service; ServiceContainer = serviceContainer; Properties = new Dictionary <string, object>(); }
/// <summary> /// Retrieve the status of a change set. /// </summary> /// <param name="changeSetID">The Id of the change set.</param> /// <returns>The change set status response.</returns> private async Task <ChangeSetResponse> GetChangeSetStatus(string changeSetID) { var getChangeSetStatusRequest = new GetChangeSetStatusRequest { ChangeSetId = changeSetID, }; Console.Write($"Getting status of change set {changeSetID}... "); ChangeSetResponse changeSetStatusResponse = await this.orgClient.GetChangeSetStatusAsync(getChangeSetStatusRequest).ConfigureAwait(false); Console.WriteLine(changeSetStatusResponse.Status); return(changeSetStatusResponse); }
public CacheRequestResult(Guid id, ChangeSetResponse response, int uploadCount, Exception error, object state) { this.ChangeSetResponse = response; this.Error = error; this.State = state; this.Id = id; this.BatchUploadCount = (uint)uploadCount; // Check that error is carried over to the response if (this.Error == null) return; if (this.ChangeSetResponse == null) this.ChangeSetResponse = new ChangeSetResponse(); this.ChangeSetResponse.Error = this.Error; }
/// <summary> /// OfflineSyncProvider method implementation called when a change set returned from GetChangeSet has been /// successfully uploaded. /// </summary> /// <param name="state">The unique identifier passed in to the GetChangeSet call.</param> /// <param name="response">ChangeSetResponse that contains an updated server blob and any conflicts or errors that /// happened on the service.</param> public override void OnChangeSetUploaded(Guid state, ChangeSetResponse response) { ThrowIfDisposed(); if (response == null) { throw new ArgumentNullException("response"); } if (!_syncActive) { throw new InvalidOperationException("OnChangeSetUploaded cannot be called without calling BeginSession"); } if (response.Error == null) { //Sergei Polevikov //IEnumerable<IsolatedStorageOfflineEntity> updatedItems = response.UpdatedItems.Cast<IsolatedStorageOfflineEntity>(); List <IsolatedStorageOfflineEntity> updatedItems = new List <IsolatedStorageOfflineEntity>(); foreach (IsolatedStorageOfflineEntity updatedItem in response.UpdatedItems) { updatedItems.Add(updatedItem); } //end // Notify the disk management that changes uploaded successfully. IEnumerable <Conflict> conflicts = _storageHandler.UploadSucceeded(state, response.ServerBlob, response.Conflicts, updatedItems); // Update the in-memory representation. _cacheData.AddUploadChanges(response.ServerBlob, conflicts, updatedItems, this); } else { _storageHandler.UploadFailed(state); } }
public async Task <ActionResult> Save([FromBody] ChangeSetRequest changeSet) { ChangeSetResponse res = await DomainService.ServiceApplyChangeSet(changeSet); return(new ChunkedResult <ChangeSetResponse>(res, DomainService.Serializer)); }
/// <summary> /// OnChangeSetUploaded, fired when changeset is uploaded /// </summary> /// <param name="state"></param> /// <param name="response"></param> public abstract Task OnChangeSetUploaded(Guid state, ChangeSetResponse response);
public override void OnChangeSetUploaded(Guid state, ChangeSetResponse response) { if (response == null) { throw new ArgumentNullException("response"); } if (response.Error != null) { throw response.Error; } this.OnSyncProgress(new SyncProgressEventArgs("Upload finished,mark local entities as uploaded...")); siaqodb.StartBulkInsert(CacheController.ControllerBehavior.KnownTypes.ToArray()); try { if (null != response.UpdatedItems && 0 != response.UpdatedItems.Count) { List <string> IdsWithError = new List <string>(); foreach (Conflict cf in response.Conflicts) { if (cf is SyncError) { IdsWithError.Add(cf.LiveEntity.ServiceMetadata.Id); } } foreach (var item in response.UpdatedItems) { if (IdsWithError.Contains(item.ServiceMetadata.Id)) { continue; } var offlineEntity = (SiaqodbOfflineEntity)item; offlineEntity.IsDirty = false; offlineEntity.IsTombstone = false; this.SaveEntityByPK(offlineEntity); } } if (response.Conflicts != null && response.Conflicts.Count > 0) { ConflictsEventArgs ceArgs = new ConflictsEventArgs(response.Conflicts); this.OnConflictOccur(ceArgs); if (!ceArgs.CancelResolvingConflicts) { foreach (var conflict in response.Conflicts) { if (conflict is SyncError) { continue; } var offlineEntity = (SiaqodbOfflineEntity)conflict.LiveEntity; offlineEntity.IsDirty = false; offlineEntity.IsTombstone = false; this.SaveEntity(offlineEntity); } } } ICollection <IOfflineEntity> changesJustUploaded = this.currentChanges[state]; foreach (IOfflineEntity enI in changesJustUploaded) { SiaqodbOfflineEntity en = enI as SiaqodbOfflineEntity; //check if we did not updated above if (null != response.UpdatedItems && 0 != response.UpdatedItems.Count) { bool existsUpdated = false; foreach (var item in response.UpdatedItems) { var offlineEntity = (SiaqodbOfflineEntity)item; if (EntitiesEqualByPK(offlineEntity, en)) { existsUpdated = true; } } if (existsUpdated) { continue; } } if (response.Conflicts != null && response.Conflicts.Count > 0) { bool existsUpdated = false; foreach (var conflict in response.Conflicts) { var offlineEntity = (SiaqodbOfflineEntity)conflict.LiveEntity; if (EntitiesEqualByPK(offlineEntity, en)) { existsUpdated = true; } } if (existsUpdated) { continue; } } if (en.IsTombstone) { en.IsDirty = false; en.IsTombstone = false; //reset flags first siaqodb.StoreObjectBase(en); siaqodb.DeleteBase(en); } else { en.IsDirty = false; siaqodb.StoreObjectBase(en); } } } finally { siaqodb.EndBulkInsert(CacheController.ControllerBehavior.KnownTypes.ToArray()); siaqodb.Flush(); } this.SaveAnchor(response.ServerBlob); this.OnSyncProgress(new SyncProgressEventArgs("Downloading changes from server...")); currentChanges.Remove(state); }
/// <summary> /// OfflineSyncProvider method implementation called when a change set returned from GetChangeSet has been /// successfully uploaded. /// </summary> /// <param name="state">The unique identifier passed in to the GetChangeSet call.</param> /// <param name="response">ChangeSetResponse that contains an updated server blob and any conflicts or errors that /// happened on the service.</param> public override void OnChangeSetUploaded(Guid state, ChangeSetResponse response) { if (null == response) { throw new ArgumentNullException("response"); } if (null != response.Error) { throw new Exception("Exception during sync!"); } var storageHandler = new SqlCeStorageHandler(); if (null != response.UpdatedItems && 0 != response.UpdatedItems.Count) { foreach (var item in response.UpdatedItems) { var offlineEntity = (SqlCeOfflineEntity)item; storageHandler.ApplyItem(offlineEntity); } } if (null != response.Conflicts && 0 != response.Conflicts.Count) { foreach (var conflict in response.Conflicts) { // We have an conflict so apply the LiveEntity var liveEntity = (SqlCeOfflineEntity)conflict.LiveEntity; // For a SyncError, which resulted from a client insert, the winning item may be a tombstone version // of the client entity. In this case, the ServiceMetadata.Id property of the LiveEntity will be null. // We need to lookup the item using primary keys in order to update it. if (conflict.GetType() == typeof(SyncError)) { var errorEntity = ((SyncError)conflict).ErrorEntity; if (!liveEntity.ServiceMetadata.IsTombstone) { // If the live entity is not a tombstone, then we just need to update the entity. storageHandler.ApplyItem(liveEntity); } else { // At this point, the LiveEntity is a tombstone and does not have primary key info. // If the live entity is a tombstone, then delete the item by looking up the primary key // from the error entity. // The error entity in this case will have both Id and the primary keys. errorEntity.ServiceMetadata.IsTombstone = true; errorEntity.ServiceMetadata.Id = null; storageHandler.ApplyItem((SqlCeOfflineEntity)errorEntity); } } else { storageHandler.ApplyItem(liveEntity); } } } // Clear all the isdirty flags and delete all rows with IsTombstone = true storageHandler.ResetDirtyAndDeleteTombstones(); storageHandler.SaveAnchor(response.ServerBlob); }
/// <summary> /// Demonstrate the full asynchronous change set workflow (create change set, upload contents, check results). /// </summary> /// <param name="parentTree">The tree to which to apply the change set.</param> /// <returns>Does not return anything.</returns> private async Task RunAsyncChangeSetWorkflow(Tree parentTree) { if (parentTree != null) { // Create an asynchronous change set (declare intention to upload changes) ChangeSetResponseInitiated createAsyncChangesSetResponse = await this.CreateAsyncChangeset(parentTree).ConfigureAwait(false); if (string.Compare(createAsyncChangesSetResponse.Status, "WaitingUpload", StringComparison.OrdinalIgnoreCase) == 0) { // Upload the change set contents asynchronously await this.UploadChangeSetContents(createAsyncChangesSetResponse.UploadURL).ConfigureAwait(false); // Wait until the change set has finished processing. // Use a change set status checking algorithm with exponential backoff an jitter // to reduce the load on the server and increase chance of success. const int CheckhangeSetStatusInitialInterval = 2000; const int CheckChangeSetstatusMaxInterval = 30000; const double CheckChangeSetIntervalIncreaseFactor = 2.0; const int CheckChangeSetMaxJitter = 1000; const int ChangeSetTimeOut = 90000; int checkChangeSetStatusInterval = CheckhangeSetStatusInitialInterval; Random random = new Random(); ChangeSetResponse getChangeSetStatusResponse = null; Stopwatch watch = Stopwatch.StartNew(); do { Console.WriteLine($"Waiting {(int)checkChangeSetStatusInterval/1000.0} sec..."); Thread.Sleep(checkChangeSetStatusInterval); getChangeSetStatusResponse = await this.GetChangeSetStatus(createAsyncChangesSetResponse.Id).ConfigureAwait(false); checkChangeSetStatusInterval = Math.Min((int)(checkChangeSetStatusInterval * CheckChangeSetIntervalIncreaseFactor), CheckChangeSetstatusMaxInterval) + random.Next(CheckChangeSetMaxJitter); }while ((string.Compare(getChangeSetStatusResponse.Status, "WaitingUpload", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(getChangeSetStatusResponse.Status, "Queued", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(getChangeSetStatusResponse.Status, "Processing", StringComparison.OrdinalIgnoreCase) == 0) && watch.ElapsedMilliseconds <= ChangeSetTimeOut); Console.WriteLine($"Change set status: {getChangeSetStatusResponse.Status}"); if (string.Compare(getChangeSetStatusResponse.Status, "Done", StringComparison.OrdinalIgnoreCase) == 0) { // Get the change set results if (!string.IsNullOrEmpty(getChangeSetStatusResponse.ResultsURL)) { Console.WriteLine("Change set results: "); await this.changeSetClient.GetNDJsonAsync <Node>(getChangeSetStatusResponse.ResultsURL, this.PrintNodes).ConfigureAwait(false); } // Get the change set errors if (!string.IsNullOrEmpty(getChangeSetStatusResponse.ErrorsURL)) { Console.WriteLine("Change set errors: "); await this.changeSetClient.GetNDJsonAsync( getChangeSetStatusResponse.ErrorsURL, (IEnumerable <UnprocessedNode> unprocessedNodesChunk) => { if (unprocessedNodesChunk != null) { foreach (var unprocessedNode in unprocessedNodesChunk) { Console.Write($" {unprocessedNode.Code} {unprocessedNode.Message}"); if (unprocessedNode.Item != null) { Console.Write($" {unprocessedNode.Item.Id} {unprocessedNode.Item.Name}"); } } Console.WriteLine(); } }).ConfigureAwait(false); } } } Console.WriteLine(); } }
/// <summary> /// SQLiteContext method implementation called when a change set returned from GetChangeSet has been /// successfully uploaded. /// </summary> /// <param name="state">The unique identifier passed in to the GetChangeSet call.</param> /// <param name="response">ChangeSetResponse that contains an updated server blob and any conflicts or errors that /// happened on the service.</param> public override async Task OnChangeSetUploaded(Guid state, ChangeSetResponse response) { await Task.Run(() => { ThrowIfDisposed(); if (response == null) { throw new ArgumentNullException("response"); } if (!syncActive) { throw new InvalidOperationException("OnChangeSetUploaded cannot be called without calling BeginSession"); } if (response.Error == null) { IEnumerable <SQLiteOfflineEntity> conflictEntities = null; IEnumerable <SQLiteOfflineEntity> updatedItems = null; // Get conflicts and notify user if (response.Conflicts != null) { // This approach assumes that there are not duplicates between the conflicts and the updated entities (there shouldn't be) conflictEntities = (from c in response.Conflicts select(SQLiteOfflineEntity) c.LiveEntity); this.Conflicts = response.Conflicts; } // Get the Updated Items from Server // A read only collection of Insert entities uploaded by clients that have been issued // permanent Id's by the service if (response.UpdatedItems != null && response.UpdatedItems.Count > 0) { updatedItems = response.UpdatedItems.Cast <SQLiteOfflineEntity>(); } IEnumerable <SQLiteOfflineEntity> allItems = updatedItems ?? new List <SQLiteOfflineEntity>(); // Add conflict entities if (conflictEntities != null) { allItems = allItems.Concat(conflictEntities); } this.Manager.SaveDownloadedChanges(allItems); // Notify the disk management that changes uploaded successfully. // Update all Entities and Set IsDirty = 0 // Remvove tarcking tombstone Manager.UploadSucceeded(state); // Set the new Anchor this.Configuration.AnchorBlob = response.ServerBlob; } }); }
/// <summary> /// OfflineSyncProvider method implementation called when a change set returned from GetChangeSet has been /// successfully uploaded. /// </summary> /// <param name="state">The unique identifier passed in to the GetChangeSet call.</param> /// <param name="response">ChangeSetResponse that contains an updated server blob and any conflicts or errors that /// happened on the service.</param> public override void OnChangeSetUploaded(Guid state, ChangeSetResponse response) { ThrowIfDisposed(); if (response == null) { throw new ArgumentNullException("response"); } if (!_syncActive) { throw new InvalidOperationException("OnChangeSetUploaded cannot be called without calling BeginSession"); } if (response.Error == null) { //Sergei Polevikov //IEnumerable<IsolatedStorageOfflineEntity> updatedItems = response.UpdatedItems.Cast<IsolatedStorageOfflineEntity>(); List<IsolatedStorageOfflineEntity> updatedItems = new List<IsolatedStorageOfflineEntity>(); foreach (IsolatedStorageOfflineEntity updatedItem in response.UpdatedItems) { updatedItems.Add(updatedItem); } //end // Notify the disk management that changes uploaded successfully. IEnumerable<Conflict> conflicts = _storageHandler.UploadSucceeded(state, response.ServerBlob, response.Conflicts, updatedItems); // Update the in-memory representation. _cacheData.AddUploadChanges(response.ServerBlob, conflicts, updatedItems, this); } else { _storageHandler.UploadFailed(state); } }
public async Task <ActionResult> Save([ServiceParamsBinder] ChangeSetRequest changeSet) { ChangeSetResponse response = await DomainService.ServiceApplyChangeSet(changeSet); return(new ChunkedResult <ChangeSetResponse>(response, Serializer)); }