/// <summary>
        /// Called by the CacheController when it wants this CacheRequest to be processed asynchronously.
        /// </summary>
        /// <param name="request">CacheRequest to be processed</param>
        /// <param name="state">User state object</param>
        /// <param name="cancellationToken"> </param>
        public async Task<CacheRequestResult> ProcessCacheRequestAsync(CacheRequest request, object state,
                                                                       CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
                cancellationToken.ThrowIfCancellationRequested();

            var wrapper = new AsyncArgsWrapper
            {
                UserPassedState = state,
                CacheRequest = request,
                Step = HttpState.Start
            };

            wrapper = await ProcessRequest(wrapper, cancellationToken);

            CacheRequestResult cacheRequestResult;

            if (wrapper.CacheRequest.RequestType == CacheRequestType.UploadChanges)
            {
                cacheRequestResult =
                    new CacheRequestResult(
                        wrapper.CacheRequest.RequestId,
                        wrapper.UploadResponse,
                        wrapper.CacheRequest.Changes.Count,
                        wrapper.Error,
                        wrapper.Step,
                        wrapper.UserPassedState);
            }
            else
            {
                cacheRequestResult =
                    new CacheRequestResult(
                        wrapper.CacheRequest.RequestId,
                        wrapper.DownloadResponse,
                        wrapper.Error,
                        wrapper.Step,
                        wrapper.UserPassedState);
            }
            return cacheRequestResult;
        }
Пример #2
0
 /// <summary>
 /// Called by the CacheController when it wants this CacheRequest to be processed asynchronously.
 /// </summary>
 /// <param name="request">CacheRequest to be processed</param>
 /// <param name="state">User state object</param>
 public override void ProcessCacheRequestAsync(CacheRequest request, object state)
 {
     this._workerManager.AddWorkRequest(new AsyncWorkRequest(ProcessCacheRequestWorker, CacheRequestCompleted, request, state));
 }
Пример #3
0
 /// <summary>
 /// Called by the CacheController when it wants this CacheRequest to be processed asynchronously.
 /// </summary>
 /// <param name="request">CacheRequest to be processed</param>
 /// <param name="state">User state object</param>
 public override void ProcessCacheRequestAsync(CacheRequest request, object state)
 {
     this._workerManager.AddWorkRequest(new AsyncWorkRequest(ProcessCacheRequestWorker, CacheRequestCompleted, request, state));
 }
Пример #4
0
 /// <summary>
 /// Method that will contain the actual implementation of the cache request processing.
 /// </summary>
 /// <param name="request">CacheRequest object</param>
 /// <param name="state">User state object</param>
 public abstract void ProcessCacheRequestAsync(CacheRequest request, object state);
Пример #5
0
 /// <summary>
 /// Method that will contain the actual implementation of the cache request processing.
 /// </summary>
 /// <param name="request">CacheRequest object</param>
 /// <returns>ChangeSet for a dowload request or ChangeSetResponse for an upload request</returns>
 public abstract object ProcessCacheRequest(CacheRequest request);
Пример #6
0
        /// <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);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        private object ProcessUploadRequest(HttpWebRequest webRequest, CacheRequest request)
        {
            using (Stream memoryStream = new MemoryStream())
            {
                // Create a SyncWriter to write the contents
                this._syncWriter = new ODataAtomWriter(base.BaseUri);

                this._syncWriter.StartFeed(true, request.KnowledgeBlob ?? new byte[0]);

                foreach (IOfflineEntity entity in request.Changes)
                {
                    // Skip tombstones that dont have a ID element.
                    if (entity.ServiceMetadata.IsTombstone && string.IsNullOrEmpty(entity.ServiceMetadata.Id))
                    {
                        continue;
                    }

                    string tempId = null;

                    // Check to see if this is an insert. i.e ServiceMetadata.Id is null or empty
                    if (string.IsNullOrEmpty(entity.ServiceMetadata.Id))
                    {
                        if (TempIdToEntityMapping == null)
                        {
                            TempIdToEntityMapping = new Dictionary <string, IOfflineEntity>();
                        }
                        tempId = Guid.NewGuid().ToString();
                        TempIdToEntityMapping.Add(tempId, entity);
                    }

                    this._syncWriter.AddItem(entity, tempId);
                }

                this._syncWriter.WriteFeed(XmlWriter.Create(memoryStream));

                memoryStream.Flush();
                // Set the content length
                webRequest.ContentLength = memoryStream.Position;

                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    CopyStreamContent(memoryStream, requestStream);

                    // Close the request stream
                    requestStream.Flush();
                    requestStream.Close();
                }
            }

            // Fire the Before request handler
            this.FirePreRequestHandler(webRequest);

            // Get the response
            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

            if (webResponse.StatusCode == HttpStatusCode.OK)
            {
                ChangeSetResponse changeSetResponse = new ChangeSetResponse();

                using (Stream responseStream = webResponse.GetResponseStream())
                {
                    // Create the SyncReader
                    this._syncReader = new ODataAtomReader(responseStream, this._knownTypes);

                    // Read the response
                    while (this._syncReader.Next())
                    {
                        switch (this._syncReader.ItemType)
                        {
                        case ReaderItemType.Entry:
                            IOfflineEntity entity      = this._syncReader.GetItem();
                            IOfflineEntity ackedEntity = entity;
                            string         tempId      = null;

                            // If conflict only one temp ID should be set
                            if (this._syncReader.HasTempId() && this._syncReader.HasConflictTempId())
                            {
                                throw new CacheControllerException(string.Format("Service returned a TempId '{0}' in both live and conflicting entities.",
                                                                                 this._syncReader.GetTempId()));
                            }

                            // Validate the live temp ID if any, before adding anything to the offline context
                            if (this._syncReader.HasTempId())
                            {
                                tempId = this._syncReader.GetTempId();
                                CheckEntityServiceMetadataAndTempIds(TempIdToEntityMapping, entity, tempId, changeSetResponse);
                            }

                            //  If conflict
                            if (this._syncReader.HasConflict())
                            {
                                Conflict       conflict       = this._syncReader.GetConflict();
                                IOfflineEntity conflictEntity = (conflict is SyncConflict) ?
                                                                ((SyncConflict)conflict).LosingEntity : ((SyncError)conflict).ErrorEntity;

                                // Validate conflict temp ID if any
                                if (this._syncReader.HasConflictTempId())
                                {
                                    tempId = this._syncReader.GetConflictTempId();
                                    CheckEntityServiceMetadataAndTempIds(TempIdToEntityMapping, conflictEntity, tempId, changeSetResponse);
                                }

                                // Add conflict
                                changeSetResponse.AddConflict(conflict);

                                //
                                // If there is a conflict and the tempId is set in the conflict entity then the client version lost the
                                // conflict and the live entity is the server version (ServerWins)
                                //
                                if (this._syncReader.HasConflictTempId() && entity.ServiceMetadata.IsTombstone)
                                {
                                    //
                                    // This is a ServerWins conflict, or conflict error. The winning version is a tombstone without temp Id
                                    // so there is no way to map the winning entity with a temp Id. The temp Id is in the conflict so we are
                                    // using the conflict entity, which has the PK, to build a tombstone entity used to update the offline context
                                    //
                                    // In theory, we should copy the service metadata but it is the same end result as the service fills in
                                    // all the properties in the conflict entity
                                    //

                                    // Add the conflict entity
                                    conflictEntity.ServiceMetadata.IsTombstone = true;
                                    ackedEntity = conflictEntity;
                                }
                            }

                            // Add ackedEntity to storage. If ackedEntity is still equal to entity then add non-conflict entity.
                            if (!String.IsNullOrEmpty(tempId))
                            {
                                changeSetResponse.AddUpdatedItem(ackedEntity);
                            }

                            break;

                        case ReaderItemType.SyncBlob:
                            changeSetResponse.ServerBlob = this._syncReader.GetServerBlob();
                            break;
                        }
                    }
                }

                if (TempIdToEntityMapping != null && TempIdToEntityMapping.Count != 0)
                {
                    // The client sent some inserts which werent ack'd by the service. Throw.
                    StringBuilder builder = new StringBuilder("Server did not acknowledge with a permanant Id for the following tempId's: ");
                    builder.Append(string.Join(",", TempIdToEntityMapping.Keys.ToArray()));
                    throw new CacheControllerException(builder.ToString());
                }

                this.FirePostResponseHandler(webResponse);

                webResponse.Close();

                return(changeSetResponse);
            }
            else
            {
                throw new CacheControllerException(
                          string.Format("Remote service returned error status. Status: {0}, Description: {1}",
                                        webResponse.StatusCode,
                                        webResponse.StatusDescription));
            }
        }
Пример #9
0
        private object ProcessDownloadRequest(HttpWebRequest webRequest, CacheRequest request)
        {
            using (Stream memoryStream = new MemoryStream())
            {
                // Create a SyncWriter to write the contents
                this._syncWriter = new ODataAtomWriter(base.BaseUri);

                this._syncWriter.StartFeed(true, request.KnowledgeBlob ?? new byte[0]);

                this._syncWriter.WriteFeed(XmlWriter.Create(memoryStream));
                memoryStream.Flush();

                webRequest.ContentLength = memoryStream.Position;
                Stream requestStream = webRequest.GetRequestStream();
                CopyStreamContent(memoryStream, requestStream);

                requestStream.Flush();
                requestStream.Close();

                // Fire the Before request handler
                this.FirePreRequestHandler(webRequest);
            }

            // Get the response
            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

            if (webResponse.StatusCode == HttpStatusCode.OK)
            {
                ChangeSet changeSet = new ChangeSet();

                using (Stream responseStream = webResponse.GetResponseStream())
                {
                    // Create the SyncReader
                    this._syncReader = new ODataAtomReader(responseStream, this._knownTypes);

                    // Read the response
                    while (this._syncReader.Next())
                    {
                        switch (this._syncReader.ItemType)
                        {
                        case ReaderItemType.Entry:
                            changeSet.AddItem(this._syncReader.GetItem());
                            break;

                        case ReaderItemType.SyncBlob:
                            changeSet.ServerBlob = this._syncReader.GetServerBlob();
                            break;

                        case ReaderItemType.HasMoreChanges:
                            changeSet.IsLastBatch = !this._syncReader.GetHasMoreChangesValue();
                            break;
                        }
                    }

                    this.FirePostResponseHandler(webResponse);
                }

                webResponse.Close();

                return(changeSet);
            }
            else
            {
                throw new CacheControllerException(
                          string.Format("Remote service returned error status. Status: {0}, Description: {1}",
                                        webResponse.StatusCode,
                                        webResponse.StatusDescription));
            }
        }
Пример #10
0
 /// <summary>
 /// Method that will contain the actual implementation of the cache request processing.
 /// </summary>
 /// <param name="request">CacheRequest object</param>
 /// <param name="state">User state object</param>
 public abstract void ProcessCacheRequestAsync(CacheRequest request, object state);
        /// <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>
        void EnqueueDownloadRequest()
        {
            try
            {
                // Create a SyncRequest for download.
                CacheRequest request = new CacheRequest()
                {
                    Format = this.ControllerBehavior.SerializationFormat,
                    RequestType = CacheRequestType.DownloadChanges,
                    KnowledgeBlob = this.LocalProvider.GetServerBlob()
                };

                this._cacheRequestHandler.ProcessCacheRequestAsync(request, null /*state is null*/);
            }
            catch (Exception e)
            {
                if (ExceptionUtility.IsFatal(e))
                {
                    throw;
                }
                // Error. EndSession refresh and post callback
                CompleteAsyncWithException(e);
            }
        }
        /// <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>
        void EnqueueUploadRequest()
        {
            this.changeSetId = Guid.NewGuid();

            try
            {
                ChangeSet changeSet = this._localProvider.GetChangeSet(this.changeSetId);

                if (changeSet == null || changeSet.Data == null || changeSet.Data.Count == 0)
                {
                    // No data to upload. Skip upload phase.
                    this.EnqueueDownloadRequest();
                }
                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
                    };

                    this._cacheRequestHandler.ProcessCacheRequestAsync(request, changeSet.IsLastBatch /*state is the IsLastBatch flag*/);
                }
            }
            catch (Exception e)
            {
                if (ExceptionUtility.IsFatal(e))
                {
                    throw;
                }
                // Error. EndSession refresh and post callback
                CompleteAsyncWithException(e);
            }
        }
Пример #13
0
        /// <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;
            }
        }
Пример #14
0
        /// <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;
        }
Пример #15
0
        /// <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;
        }