/// <summary> /// Method that synchronize the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> internal async Task <CacheRefreshStatistics> SynchronizeAsync(CancellationToken cancellationToken) { CacheRefreshStatistics statistics = new CacheRefreshStatistics(); try { // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // set start time statistics.StartTime = DateTime.Now; // First create the CacheRequestHandler this.cacheRequestHandler = new HttpCacheRequestHandler(this.serviceUri, this.controllerBehavior); // Then fire the BeginSession call on the local provider. this.localProvider.BeginSession(); // Set the flag to indicate BeginSession was successful this.beginSessionComplete = true; // Do uploads first statistics = await this.EnqueueUploadRequest(statistics, cancellationToken); // Set end time statistics.EndTime = DateTime.Now; // Call EndSession only if BeginSession was successful. if (this.beginSessionComplete) { this.localProvider.EndSession(); } } catch (OperationCanceledException ex) { statistics.EndTime = DateTime.Now; statistics.Cancelled = true; statistics.Error = ex; this.localProvider.EndSession(); } catch (Exception ex) { statistics.EndTime = DateTime.Now; statistics.Error = ex; this.localProvider.EndSession(); } finally { // Reset the state this.ResetAsyncWorkerManager(); } return(statistics); }
/// <summary> /// Method that performs an upload. It gets the ChangeSet from the local provider and then creates an /// CacheRequest object for that ChangeSet and then passed the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task <CacheRefreshStatistics> EnqueueUploadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken) { this.changeSetId = Guid.NewGuid(); try { // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } ChangeSet changeSet = this.localProvider.GetChangeSet(this.changeSetId); if (changeSet == null || changeSet.Data == null || changeSet.Data.Count == 0) { // No data to upload. Skip upload phase. statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken); } else { // Create a SyncRequest out of this. CacheRequest request = new CacheRequest { RequestId = this.changeSetId, Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.UploadChanges, Changes = changeSet.Data, KnowledgeBlob = changeSet.ServerBlob, IsLastBatch = changeSet.IsLastBatch }; var args = await this.cacheRequestHandler.ProcessCacheRequestAsync(request, changeSet.IsLastBatch, cancellationToken); statistics = await this.ProcessCacheRequestResults(statistics, args, cancellationToken); } } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) { throw; } statistics.Error = e; } return(statistics); }
/// <summary> /// Method that performs a download. It gets the server blob anchor from the local provider and then creates an /// CacheRequest object for that download request. It then passes the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task<CacheRefreshStatistics> EnqueueDownloadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress = null) { try { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); Boolean isLastBatch = false; while (!isLastBatch) { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // Create a SyncRequest for download. CacheRequest request = new CacheRequest { Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.DownloadChanges, KnowledgeBlob = this.localProvider.GetServerBlob() }; // Get Changes DateTime durationStartDate = DateTime.Now; var requestResult = await this.cacheRequestHandler.ProcessCacheRequestAsync( request, null, cancellationToken); statistics = await this.ProcessCacheRequestResults(statistics, requestResult, cancellationToken); // Check if we are at the end if (requestResult.ChangeSet == null || requestResult.ChangeSet.IsLastBatch) isLastBatch = true; // Reporting progress after get changes from local store if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.DownloadingChanges, DateTime.Now.Subtract(durationStartDate), true, (requestResult.ChangeSet != null ? requestResult.ChangeSet.Data : null))); } } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) throw; statistics.Error = e; } return statistics; }
/// <summary> /// Internal ctor to prevent public instantiation. /// </summary> /// <param name="stats">Statistics parameter</param> /// <param name="error">Refresh exception.</param> /// <param name="cancelled">Cancellation flag</param> internal RefreshCompletedEventArgs(CacheRefreshStatistics stats, Exception error, bool cancelled) { if (stats == null) { throw new ArgumentNullException("stats"); } this.Statistics = stats; this.Error = error; this.Cancelled = cancelled; }
/// <summary> /// Method that refreshes the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> public void RefreshAsync() { if (this.IsBusy) { throw CacheControllerException.CreateCacheBusyException(); } this.refreshStats = new CacheRefreshStatistics(); this.StartAsyncWorkerManager(); // Enqueue an async operation this._asyncWorkManager.AddWorkRequest(new AsyncWorkRequest(RefreshWorker, RefreshWorkerCompleted, null)); }
/// <summary> /// Method that performs a download. It gets the server blob anchor from the local provider and then creates an /// CacheRequest object for that download request. It then passes the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task <CacheRefreshStatistics> EnqueueDownloadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken) { try { if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // Create a SyncRequest for download. CacheRequest request = new CacheRequest { Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.DownloadChanges, KnowledgeBlob = this.localProvider.GetServerBlob() }; var args = await this.cacheRequestHandler.ProcessCacheRequestAsync(request, null, cancellationToken); statistics = await this.ProcessCacheRequestResults(statistics, args, cancellationToken); } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) { throw; } statistics.Error = e; } return(statistics); }
/// <summary> /// Method that performs an upload. It gets the ChangeSet from the local provider and then creates an /// CacheRequest object for that ChangeSet and then passed the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task <CacheRefreshStatistics> EnqueueUploadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken, IProgress <SyncProgressEvent> progress = null) { this.changeSetId = Guid.NewGuid(); try { // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // Get Changes DateTime durationStartDate = DateTime.Now; ChangeSet changeSet = await this.localProvider.GetChangeSet(this.changeSetId); // Reporting progress after get changes from local store if (progress != null) { progress.Report(new SyncProgressEvent(SyncStage.GetChanges, DateTime.Now.Subtract(durationStartDate), true, (changeSet != null ? changeSet.Data : null))); } // No data to upload. Skip upload phase. if (changeSet == null || changeSet.Data == null || changeSet.Data.Count == 0) { return(statistics); } // Create a SyncRequest out of this. CacheRequest request = new CacheRequest { RequestId = this.changeSetId, Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.UploadChanges, Changes = changeSet.Data, KnowledgeBlob = changeSet.ServerBlob, IsLastBatch = changeSet.IsLastBatch }; // Upload changes to server durationStartDate = DateTime.Now; var requestResult = await this.cacheRequestHandler.ProcessCacheRequestAsync( request, changeSet.IsLastBatch, cancellationToken); // Get response from server if mb any conflicts or updated items statistics = await this.ProcessCacheRequestResults(statistics, requestResult, cancellationToken); // Reporting progress after uploading changes, and mb get back Conflicts and new Id from insterted items if (progress != null) { progress.Report(new SyncProgressEvent(SyncStage.UploadingChanges, DateTime.Now.Subtract(durationStartDate), true, changeSet.Data, requestResult.ChangeSetResponse.Conflicts, requestResult.ChangeSetResponse.UpdatedItems)); } } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) { throw; } statistics.Error = e; } return(statistics); }
/// <summary> /// Method that refreshes the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> /// <returns>A CacheRefreshStatistics object denoting the statistics from the Refresh call</returns> public CacheRefreshStatistics Refresh() { this._controllerBehavior.Locked = true; try { // First create the CacheRequestHandler this._cacheRequestHandler = CacheRequestHandler.CreateRequestHandler(this._serviceUri, this._controllerBehavior); CacheRefreshStatistics refreshStats = new CacheRefreshStatistics(); refreshStats.StartTime = DateTime.Now; bool uploadComplete = false; bool downloadComplete = false; // Start sync by executin an Upload request while (!uploadComplete || !downloadComplete) { if (!uploadComplete) { Guid changeSetId = Guid.NewGuid(); ChangeSet changeSet = this._localProvider.GetChangeSet(changeSetId); if (changeSet.Data == null || changeSet.Data.Count == 0) { // No data to upload. Skip upload phase. uploadComplete = true; } else { // Create a SyncRequest out of this. CacheRequest request = new CacheRequest() { RequestId = changeSetId, RequestType = CacheRequestType.UploadChanges, Changes = changeSet.Data, KnowledgeBlob = changeSet.ServerBlob, IsLastBatch = changeSet.IsLastBatch }; // Increment the stats refreshStats.TotalChangeSetsUploaded++; ChangeSetResponse response = (ChangeSetResponse)this._cacheRequestHandler.ProcessCacheRequest(request); // Increment the stats refreshStats.TotalUploads += (uint)request.Changes.Count; response.ConflictsInternal.ForEach((e1) => { if (e1 is SyncConflict) { refreshStats.TotalSyncConflicts++; } else { refreshStats.TotalSyncErrors++; } }); // Send the response to the local provider this._localProvider.OnChangeSetUploaded(changeSetId, response); uploadComplete = request.IsLastBatch; } } else if (!downloadComplete) { // Create a SyncRequest for download. CacheRequest request = new CacheRequest() { RequestType = CacheRequestType.DownloadChanges, KnowledgeBlob = this.LocalProvider.GetServerBlob() }; ChangeSet changeSet = (ChangeSet)this._cacheRequestHandler.ProcessCacheRequest(request); // Increment the refresh stats refreshStats.TotalChangeSetsDownloaded++; refreshStats.TotalDownloads += (uint)changeSet.Data.Count; // Call the SaveChangeSet method on local provider. this.LocalProvider.SaveChangeSet(changeSet); downloadComplete = changeSet.IsLastBatch; } } refreshStats.EndTime = DateTime.Now; // Finally return the statistics object return(refreshStats); } finally { // Unlock the ControllerBehavior object this._controllerBehavior.Locked = false; } }
/// <summary> /// Method that synchronize the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> internal async Task<CacheRefreshStatistics> SynchronizeAsync(CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress = null) { CacheRefreshStatistics statistics = new CacheRefreshStatistics(); // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // set start time statistics.StartTime = DateTime.Now; // Reporting progress if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.StartingSync, TimeSpan.Zero)); try { // First create the CacheRequestHandler this.cacheRequestHandler = new HttpCacheRequestHandler(this.serviceUri, this.controllerBehavior); // Then fire the BeginSession call on the local provider. await this.localProvider.BeginSession(); // Set the flag to indicate BeginSession was successful this.beginSessionComplete = true; // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // Do uploads first (no batch mode on Upload Request) statistics = await this.EnqueueUploadRequest(statistics, cancellationToken, progress); // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // then Download (be careful, could be in batch mode) statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken, progress); // Set end time statistics.EndTime = DateTime.Now; // Call EndSession only if BeginSession was successful. if (this.beginSessionComplete) this.localProvider.EndSession(); } catch (OperationCanceledException ex) { statistics.EndTime = DateTime.Now; statistics.Cancelled = true; statistics.Error = ex; this.localProvider.EndSession(); } catch (Exception ex) { statistics.EndTime = DateTime.Now; statistics.Error = ex; this.localProvider.EndSession(); } finally { // Reset the state this.ResetAsyncWorkerManager(); } // Reporting progress if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.EndingSync, statistics.EndTime.Subtract(statistics.StartTime))); return statistics; }
/// <summary> /// Method that performs a download. It gets the server blob anchor from the local provider and then creates an /// CacheRequest object for that download request. It then passes the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task<CacheRefreshStatistics> EnqueueDownloadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress = null) { try { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); Boolean isLastBatch = false; while (!isLastBatch) { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // Create a SyncRequest for download. CacheRequest request = new CacheRequest { Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.DownloadChanges, KnowledgeBlob = this.localProvider.GetServerBlob() }; // Get Changes DateTime durationStartDate = DateTime.Now; var args = await this.cacheRequestHandler.ProcessCacheRequestAsync(request, null, cancellationToken); statistics = await this.ProcessCacheRequestResults(statistics, args, cancellationToken); // Check if we are at the end if (args.ChangeSet == null || args.ChangeSet.IsLastBatch) isLastBatch = true; // Reporting progress after get changes from local store if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.DownloadingChanges, DateTime.Now.Subtract(durationStartDate), true, (args.ChangeSet != null ? args.ChangeSet.Data : null))); } } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) throw; statistics.Error = e; } return statistics; }
/// <summary> /// Method that performs an upload. It gets the ChangeSet from the local provider and then creates an /// CacheRequest object for that ChangeSet and then passed the processing asynchronously to the underlying /// CacheRequestHandler. /// </summary> private async Task<CacheRefreshStatistics> EnqueueUploadRequest(CacheRefreshStatistics statistics, CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress = null) { this.changeSetId = Guid.NewGuid(); try { // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // Get Changes DateTime durationStartDate = DateTime.Now; ChangeSet changeSet = await this.localProvider.GetChangeSet(this.changeSetId); // Reporting progress after get changes from local store if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.GetChanges, DateTime.Now.Subtract(durationStartDate), true, (changeSet != null ? changeSet.Data : null))); // No data to upload. Skip upload phase. if (changeSet == null || changeSet.Data == null || changeSet.Data.Count == 0) return statistics; // Create a SyncRequest out of this. CacheRequest request = new CacheRequest { RequestId = this.changeSetId, Format = this.ControllerBehavior.SerializationFormat, RequestType = CacheRequestType.UploadChanges, Changes = changeSet.Data, KnowledgeBlob = changeSet.ServerBlob, IsLastBatch = changeSet.IsLastBatch }; // Upload changes to server durationStartDate = DateTime.Now; var args = await this.cacheRequestHandler.ProcessCacheRequestAsync(request, changeSet.IsLastBatch, cancellationToken); // Get response from server if mb any conflicts or updated items statistics = await this.ProcessCacheRequestResults(statistics, args, cancellationToken); // Reporting progress after uploading changes, and mb get back Conflicts and new Id from insterted items if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.UploadingChanges, DateTime.Now.Subtract(durationStartDate), true, changeSet.Data, args.ChangeSetResponse.Conflicts, args.ChangeSetResponse.UpdatedItems)); } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception e) { if (ExceptionUtility.IsFatal(e)) throw; statistics.Error = e; } return statistics; }
/// <summary> /// Method that synchronize the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> internal async Task<CacheRefreshStatistics> SynchronizeAsync(CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress = null) { CacheRefreshStatistics statistics = new CacheRefreshStatistics(); // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // set start time statistics.StartTime = DateTime.Now; // Reporting progress if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.StartingSync, TimeSpan.Zero)); try { // First create the CacheRequestHandler this.cacheRequestHandler = new HttpCacheRequestHandler(this.serviceUri, this.controllerBehavior); // Then fire the BeginSession call on the local provider. await this.localProvider.BeginSession(); // Set the flag to indicate BeginSession was successful this.beginSessionComplete = true; // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // Do uploads first (no batch mode on Upload Request) statistics = await this.EnqueueUploadRequest(statistics, cancellationToken, progress); // If there is an error during Upload request, dont want to donwload if (statistics.Error != null) throw new Exception("Error occured during Upload request.", statistics.Error); // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); // When there are records records that are not uploading successfully // a successfull download 1-N records will cause anchor to be updated. // if anchor is updated then the records on the device will not be attempted for upload again // on subsequent syncs. therefore check for null Error if (statistics.Error == null) { // then Download (be careful, could be in batch mode) statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken, progress); } // Set end time statistics.EndTime = DateTime.Now; // Call EndSession only if BeginSession was successful. if (this.beginSessionComplete) this.localProvider.EndSession(); } catch (OperationCanceledException ex) { statistics.EndTime = DateTime.Now; statistics.Cancelled = true; statistics.Error = ex; if (this.beginSessionComplete) this.localProvider.EndSession(); } catch (Exception ex) { statistics.EndTime = DateTime.Now; statistics.Error = ex; if (this.beginSessionComplete) this.localProvider.EndSession(); } finally { // Reset the state this.ResetAsyncWorkerManager(); } // Reporting progress if (progress != null) progress.Report(new SyncProgressEvent(SyncStage.EndingSync, statistics.EndTime.Subtract(statistics.StartTime))); return statistics; }
/// <summary> /// Called whenever the CacheRequestHandler proceeses an upload/download request. It is also responsible for /// issuing another request if it wasnt the last batch. In case of receiving an Upload response it calls the /// underlying provider with the status of the upload. In case of Download it notifies the local provider of the /// changes that it needs to save. /// </summary> private async Task <CacheRefreshStatistics> ProcessCacheRequestResults(CacheRefreshStatistics statistics, CacheRequestResult e, CancellationToken cancellationToken) { try { if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } if (e.Error != null) { // Check to see if it was a UploadRequest in which case we will have to call OnChangeSetUploaded // with error to reset the dirty bits. if (e.ChangeSetResponse != null) { // its an response to a upload this.localProvider.OnChangeSetUploaded(e.Id, e.ChangeSetResponse); } // Finally complete Refresh with error. statistics.Error = e.Error; } else if (e.ChangeSetResponse != null) { // its an response to a upload this.localProvider.OnChangeSetUploaded(e.Id, e.ChangeSetResponse); if (e.ChangeSetResponse.Error != null) { statistics.Error = e.ChangeSetResponse.Error; return(statistics); } // Increment the ChangeSets uploaded count statistics.TotalChangeSetsUploaded++; statistics.TotalUploads += e.BatchUploadCount; // Update refresh stats foreach (var e1 in e.ChangeSetResponse.ConflictsInternal) { if (e1 is SyncConflict) { statistics.TotalSyncConflicts++; } else { statistics.TotalSyncErrors++; } } // Dont enqueue another request if its been cancelled if (!cancellationToken.IsCancellationRequested) { if (!((bool)e.State)) { // Check to see if this was the last batch or else enqueue another pending Upload request statistics = await this.EnqueueUploadRequest(statistics, cancellationToken); } else { // That was the last batch. Issue an Download request statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken); } } else { cancellationToken.ThrowIfCancellationRequested(); } } else // It means its an Download response { Debug.Assert(e.ChangeSet != null, "Completion is not for a download request."); // Increment the refresh stats if (e.ChangeSet != null) { statistics.TotalChangeSetsDownloaded++; statistics.TotalDownloads += (uint)e.ChangeSet.Data.Count; await this.localProvider.SaveChangeSet(e.ChangeSet); // Dont enqueue another request if its been cancelled if (!cancellationToken.IsCancellationRequested) { if (!e.ChangeSet.IsLastBatch) { // Enqueue the next download statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken); } } else { cancellationToken.ThrowIfCancellationRequested(); } } } } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception exp) { if (ExceptionUtility.IsFatal(exp)) { throw; } statistics.Error = exp; } return(statistics); }
/// <summary> /// Called whenever the CacheRequestHandler proceeses an upload/download request. It is also responsible for /// issuing another request if it wasnt the last batch. In case of receiving an Upload response it calls the /// underlying provider with the status of the upload. In case of Download it notifies the local provider of the /// changes that it needs to save. /// </summary> private async Task <CacheRefreshStatistics> ProcessCacheRequestResults( CacheRefreshStatistics statistics, CacheRequestResult cacheRequestResult, CancellationToken cancellationToken) { try { if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } #region Error if (cacheRequestResult.Error != null) { // We have an error but we have a ChangeSetResponse with reading the upload respose // So we can serialize results and update dirty bits if (cacheRequestResult.ChangeSetResponse != null && cacheRequestResult.HttpStep == HttpState.End) { await this.localProvider.OnChangeSetUploaded(cacheRequestResult.Id, cacheRequestResult.ChangeSetResponse); } // Finally complete Refresh with error. statistics.Error = cacheRequestResult.Error; return(statistics); } #endregion #region Upload response if (cacheRequestResult.ChangeSetResponse != null) { if (cacheRequestResult.ChangeSetResponse.Error == null && cacheRequestResult.HttpStep == HttpState.End) { await this.localProvider.OnChangeSetUploaded(cacheRequestResult.Id, cacheRequestResult.ChangeSetResponse); } if (cacheRequestResult.ChangeSetResponse.Error != null) { statistics.Error = cacheRequestResult.ChangeSetResponse.Error; return(statistics); } // Increment the ChangeSets uploaded count statistics.TotalChangeSetsUploaded++; statistics.TotalUploads += cacheRequestResult.BatchUploadCount; // Update refresh stats foreach (var e1 in cacheRequestResult.ChangeSetResponse.ConflictsInternal) { if (e1 is SyncConflict) { statistics.TotalSyncConflicts++; } else { statistics.TotalSyncErrors++; } } return(statistics); } #endregion #region Download Response // it's a response to download Debug.Assert(cacheRequestResult.ChangeSet != null, "Completion is not for a download request."); // Increment the refresh stats if (cacheRequestResult.ChangeSet != null && cacheRequestResult.ChangeSet.Data != null && cacheRequestResult.ChangeSet.Data.Count > 0) { statistics.TotalChangeSetsDownloaded++; statistics.TotalDownloads += (uint)cacheRequestResult.ChangeSet.Data.Count; await this.localProvider.SaveChangeSet(cacheRequestResult.ChangeSet); } return(statistics); #endregion } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception exp) { if (ExceptionUtility.IsFatal(exp)) { throw; } statistics.Error = exp; } return(statistics); }
/// <summary> /// Method that synchronize the Cache by uploading all modified changes and then downloading the /// server changes. /// </summary> internal async Task <CacheRefreshStatistics> SynchronizeAsync(CancellationToken cancellationToken, IProgress <SyncProgressEvent> progress = null) { CacheRefreshStatistics statistics = new CacheRefreshStatistics(); // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // set start time statistics.StartTime = DateTime.Now; // Reporting progress if (progress != null) { progress.Report(new SyncProgressEvent(SyncStage.StartingSync, TimeSpan.Zero)); } try { // First create the CacheRequestHandler this.cacheRequestHandler = new HttpCacheRequestHandler(this.serviceUri, this.controllerBehavior); // Then fire the BeginSession call on the local provider. await this.localProvider.BeginSession(); // Set the flag to indicate BeginSession was successful this.beginSessionComplete = true; // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // Do uploads first (no batch mode on Upload Request) statistics = await this.EnqueueUploadRequest(statistics, cancellationToken, progress); // If there is an error during Upload request, dont want to donwload if (statistics.Error != null) { throw new Exception("Error occured during Upload request.", statistics.Error); } // Check if cancellation has occured if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // When there are records records that are not uploading successfully // a successfull download 1-N records will cause anchor to be updated. // if anchor is updated then the records on the device will not be attempted for upload again // on subsequent syncs. therefore check for null Error if (statistics.Error == null) { // then Download (be careful, could be in batch mode) statistics = await this.EnqueueDownloadRequest(statistics, cancellationToken, progress); } // Set end time statistics.EndTime = DateTime.Now; // Call EndSession only if BeginSession was successful. if (this.beginSessionComplete) { this.localProvider.EndSession(); } } catch (OperationCanceledException ex) { statistics.EndTime = DateTime.Now; statistics.Cancelled = true; statistics.Error = ex; if (this.beginSessionComplete) { this.localProvider.EndSession(); } } catch (Exception ex) { statistics.EndTime = DateTime.Now; statistics.Error = ex; if (this.beginSessionComplete) { this.localProvider.EndSession(); } } finally { // Reset the state this.ResetAsyncWorkerManager(); } // Reporting progress if (progress != null) { progress.Report(new SyncProgressEvent(SyncStage.EndingSync, statistics.EndTime.Subtract(statistics.StartTime))); } return(statistics); }
/// <summary> /// Called whenever the CacheRequestHandler proceeses an upload/download request. It is also responsible for /// issuing another request if it wasnt the last batch. In case of receiving an Upload response it calls the /// underlying provider with the status of the upload. In case of Download it notifies the local provider of the /// changes that it needs to save. /// </summary> private async Task<CacheRefreshStatistics> ProcessCacheRequestResults(CacheRefreshStatistics statistics, CacheRequestResult cacheRequestResult, CancellationToken cancellationToken) { try { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); #region Error if (cacheRequestResult.Error != null) { // Check to see if it was a UploadRequest in which case we will have to call OnChangeSetUploaded // with error to reset the dirty bits. if (cacheRequestResult.ChangeSetResponse != null) { // its an response to a upload await this.localProvider.OnChangeSetUploaded(cacheRequestResult.Id, cacheRequestResult.ChangeSetResponse); } // Finally complete Refresh with error. statistics.Error = cacheRequestResult.Error; return statistics; } #endregion #region Upload response if (cacheRequestResult.ChangeSetResponse != null) { // its an response to a upload await this.localProvider.OnChangeSetUploaded(cacheRequestResult.Id, cacheRequestResult.ChangeSetResponse); if (cacheRequestResult.ChangeSetResponse.Error != null) { statistics.Error = cacheRequestResult.ChangeSetResponse.Error; return statistics; } // Increment the ChangeSets uploaded count statistics.TotalChangeSetsUploaded++; statistics.TotalUploads += cacheRequestResult.BatchUploadCount; // Update refresh stats foreach (var e1 in cacheRequestResult.ChangeSetResponse.ConflictsInternal) { if (e1 is SyncConflict) { statistics.TotalSyncConflicts++; } else { statistics.TotalSyncErrors++; } } return statistics; } #endregion #region Download Response // it's a response to download Debug.Assert(cacheRequestResult.ChangeSet != null, "Completion is not for a download request."); // Increment the refresh stats if (cacheRequestResult.ChangeSet != null && cacheRequestResult.ChangeSet.Data != null && cacheRequestResult.ChangeSet.Data.Count > 0) { statistics.TotalChangeSetsDownloaded++; statistics.TotalDownloads += (uint)cacheRequestResult.ChangeSet.Data.Count; await this.localProvider.SaveChangeSet(cacheRequestResult.ChangeSet); } return statistics; #endregion } catch (OperationCanceledException) { // Re throw the operation cancelled throw; } catch (Exception exp) { if (ExceptionUtility.IsFatal(exp)) throw; statistics.Error = exp; } return statistics; }