/// <summary> /// Send changes to server /// </summary> public async Task <(SyncContext, ChangesApplied)> ApplyChangesAsync(SyncContext context, MessageApplyChanges message) { if (message.Changes == null || message.Changes.BatchPartsInfo.Count == 0) { return(context, new ChangesApplied()); } SyncContext syncContext = null; ChangesApplied changesApplied = null; // Foreach part, will have to send them to the remote // once finished, return context foreach (var bpi in message.Changes.BatchPartsInfo.OrderBy(bpi => bpi.Index)) { var applyChanges = new HttpMessageApplyChanges { FromScope = message.FromScope, Schema = new DmSetSurrogate(message.Schema), Policy = message.Policy, UseBulkOperations = message.UseBulkOperations, ScopeInfoTableName = message.ScopeInfoTableName, SerializationFormat = message.SerializationFormat }; HttpMessage httpMessage = new HttpMessage { Step = HttpStep.ApplyChanges, SyncContext = context, Content = applyChanges }; // If BPI is InMempory, no need to deserialize from disk // Set already contained in part.Set if (!message.Changes.InMemory) { // get the batch var partBatch = bpi.GetBatch(); // get the surrogate dmSet if (partBatch != null) { applyChanges.Set = partBatch.DmSetSurrogate; } } else if (bpi.Set != null) { applyChanges.Set = new DmSetSurrogate(bpi.Set); } if (applyChanges.Set == null || applyChanges.Set.Tables == null) { throw new ArgumentException("No changes to upload found."); } // no need to send filename applyChanges.BatchPartInfo = new BatchPartInfo { FileName = null, Index = bpi.Index, IsLastBatch = bpi.IsLastBatch, Tables = bpi.Tables }; applyChanges.InMemory = message.Changes.InMemory; applyChanges.BatchIndex = bpi.Index; //Post request and get response var httpMessageResponse = await this.httpRequestHandler.ProcessRequest(httpMessage, message.SerializationFormat, cancellationToken); // Clear surrogate applyChanges.Set.Dispose(); applyChanges.Set = null; if (httpMessageResponse == null) { throw new Exception("Can't have an empty body"); } HttpMessageApplyChanges httpMessageContent; if (httpMessageResponse.Content is HttpMessageApplyChanges) { httpMessageContent = httpMessageResponse.Content as HttpMessageApplyChanges; } else { httpMessageContent = (httpMessageResponse.Content as JObject).ToObject <HttpMessageApplyChanges>(); } syncContext = httpMessageResponse.SyncContext; changesApplied = httpMessageContent.ChangesApplied; } return(syncContext, changesApplied); }
public async Task <(SyncContext, BatchInfo, ChangesSelected)> GetChangeBatchAsync( SyncContext context, MessageGetChangesBatch message) { // While we have an other batch to process var isLastBatch = false; // Create the BatchInfo and SyncContext to return at the end BatchInfo changes = new BatchInfo { Directory = BatchInfo.GenerateNewDirectoryName() }; SyncContext syncContext = null; ChangesSelected changesSelected = null; while (!isLastBatch) { HttpMessage httpMessage = new HttpMessage { SyncContext = context, Step = HttpStep.GetChangeBatch, Content = new HttpMessageGetChangesBatch { ScopeInfo = message.ScopeInfo, BatchIndexRequested = changes.BatchIndex, DownloadBatchSizeInKB = message.DownloadBatchSizeInKB, BatchDirectory = message.BatchDirectory, Schema = new DmSetSurrogate(message.Schema), Filters = message.Filters, Policy = message.Policy, SerializationFormat = message.SerializationFormat } }; var httpMessageResponse = await this.httpRequestHandler.ProcessRequest(httpMessage, message.SerializationFormat, cancellationToken); if (httpMessageResponse == null) { throw new Exception("Can't have an empty body"); } HttpMessageGetChangesBatch httpMessageContent; if (httpMessageResponse.Content is HttpMessageGetChangesBatch) { httpMessageContent = httpMessageResponse.Content as HttpMessageGetChangesBatch; } else { httpMessageContent = (httpMessageResponse.Content as JObject).ToObject <HttpMessageGetChangesBatch>(); } if (httpMessageContent == null) { throw new Exception("Can't have an empty GetChangeBatch"); } changesSelected = httpMessageContent.ChangesSelected; changes.InMemory = httpMessageContent.InMemory; syncContext = httpMessageResponse.SyncContext; // get the bpi and add it to the BatchInfo var bpi = httpMessageContent.BatchPartInfo; if (bpi != null) { changes.BatchIndex = bpi.Index; changes.BatchPartsInfo.Add(bpi); isLastBatch = bpi.IsLastBatch; } else { changes.BatchIndex = 0; isLastBatch = true; // break the while { } story break; } if (changes.InMemory) { // load the DmSet in memory bpi.Set = httpMessageContent.Set.ConvertToDmSet(); } else { // Serialize the file ! var bpId = BatchInfo.GenerateNewFileName(changes.BatchIndex.ToString()); var fileName = Path.Combine(message.BatchDirectory, changes.Directory, bpId); BatchPart.Serialize(httpMessageContent.Set, fileName); bpi.FileName = fileName; bpi.Clear(); } // Clear the DmSetSurrogate from response, we don't need it anymore if (httpMessageContent.Set != null) { httpMessageContent.Set.Dispose(); httpMessageContent.Set = null; } // if not last, increment batchIndex for next request if (!isLastBatch) { changes.BatchIndex++; } } return(syncContext, changes, changesSelected); }
private async Task <HttpMessage> ApplyChangesAsync(HttpMessage httpMessage) { if (httpMessage.ApplyChanges == null) { throw new ArgumentException("ApplyChanges message could not be null"); } var scopeInfo = httpMessage.ApplyChanges.ScopeInfo; if (scopeInfo == null) { throw new ArgumentException("ApplyChanges ScopeInfo could not be null"); } BatchInfo batchInfo; var bpi = httpMessage.ApplyChanges.BatchPartInfo; if (httpMessage.ApplyChanges.InMemory) { batchInfo = new BatchInfo { BatchIndex = 0, BatchPartsInfo = new List <BatchPartInfo>(new[] { bpi }), InMemory = true }; bpi.Set = httpMessage.ApplyChanges.Set.ConvertToDmSet(); httpMessage.ApplyChanges.Set.Dispose(); httpMessage.ApplyChanges.Set = null; var(c, s) = await this.ApplyChangesAsync(httpMessage.SyncContext, scopeInfo, batchInfo); httpMessage.SyncContext = c; httpMessage.ApplyChanges.ChangesApplied = s; httpMessage.ApplyChanges.BatchPartInfo.Clear(); httpMessage.ApplyChanges.BatchPartInfo.FileName = null; return(httpMessage); } // not in memory batchInfo = this.LocalProvider.CacheManager.GetValue <BatchInfo>("ApplyChanges_BatchInfo"); if (batchInfo == null) { batchInfo = new BatchInfo { BatchIndex = 0, BatchPartsInfo = new List <BatchPartInfo>(new[] { bpi }), InMemory = false, Directory = BatchInfo.GenerateNewDirectoryName() }; } else { batchInfo.BatchPartsInfo.Add(bpi); } var bpId = BatchInfo.GenerateNewFileName(httpMessage.ApplyChanges.BatchIndex.ToString()); var fileName = Path.Combine(this.LocalProvider.GetCacheConfiguration().BatchDirectory, batchInfo.Directory, bpId); BatchPart.Serialize(httpMessage.ApplyChanges.Set, fileName); bpi.FileName = fileName; this.LocalProvider.CacheManager.Set("ApplyChanges_BatchInfo", batchInfo); // Clear the httpMessage set if (httpMessage.ApplyChanges != null) { httpMessage.ApplyChanges.Set.Dispose(); httpMessage.ApplyChanges.Set = null; } // if it's last batch sent if (bpi.IsLastBatch) { var(c, s) = await this.ApplyChangesAsync(httpMessage.SyncContext, scopeInfo, batchInfo); this.LocalProvider.CacheManager.Remove("ApplyChanges_BatchInfo"); httpMessage.SyncContext = c; httpMessage.ApplyChanges.ChangesApplied = s; } httpMessage.ApplyChanges.BatchPartInfo.Clear(); httpMessage.ApplyChanges.BatchPartInfo.FileName = null; return(httpMessage); }
/// <summary> /// Call this method to handle requests on the server, sent by the client /// </summary> public async Task HandleRequestAsync(HttpContext context, CancellationToken cancellationToken) { var httpRequest = context.Request; var httpResponse = context.Response; var streamArray = httpRequest.Body; // Check if we should handle a session store to handle configuration if (!this.IsRegisterAsSingleton) { // try to get the session store service from DI var sessionStore = context.RequestServices.GetService(typeof(ISessionStore)); if (sessionStore != null) { this.LocalProvider.CacheManager = new SessionCache(context); } } try { var httpMessage = serializer.Deserialize(streamArray); HttpMessage httpMessageResponse = null; switch (httpMessage.Step) { case HttpStep.BeginSession: httpMessageResponse = await BeginSessionAsync(httpMessage); break; case HttpStep.EnsureScopes: httpMessageResponse = await EnsureScopesAsync(httpMessage); break; case HttpStep.EnsureConfiguration: httpMessageResponse = await EnsureConfigurationAsync(httpMessage); break; case HttpStep.EnsureDatabase: httpMessageResponse = await EnsureDatabaseAsync(httpMessage); break; case HttpStep.GetChangeBatch: httpMessageResponse = await GetChangeBatchAsync(httpMessage); break; case HttpStep.ApplyChanges: httpMessageResponse = await ApplyChangesAsync(httpMessage); break; case HttpStep.GetLocalTimestamp: httpMessageResponse = await GetLocalTimestampAsync(httpMessage); break; case HttpStep.WriteScopes: httpMessageResponse = await WriteScopesAsync(httpMessage); break; case HttpStep.EndSession: httpMessageResponse = await EndSessionAsync(httpMessage); break; } var binaryData = serializer.Serialize(httpMessageResponse); await httpResponse.Body.WriteAsync(binaryData, 0, binaryData.Length); } catch (Exception ex) { await this.WriteExceptionAsync(httpResponse, ex); } }
/// <summary> /// From request, I get a BI /// I need to send back a BPI /// </summary> /// <param name="httpMessage"></param> /// <returns></returns> private async Task <HttpMessage> GetChangeBatchAsync(HttpMessage httpMessage) { if (httpMessage.GetChangeBatch == null) { throw new ArgumentException("GetChangeBatch message could not be null"); } var scopeInfo = httpMessage.GetChangeBatch.ScopeInfo; if (scopeInfo == null) { throw new ArgumentException("GetChangeBatch ScopeInfo could not be null"); } // if we get the first batch info request, made it. // Server is able to define if it's in memory or not if (httpMessage.GetChangeBatch.BatchIndexRequested == 0) { var(syncContext, bi, changesSelected) = await this.GetChangeBatchAsync(httpMessage.SyncContext, scopeInfo); // Select the first bpi needed (index == 0) if (bi.BatchPartsInfo.Count > 0) { httpMessage.GetChangeBatch.BatchPartInfo = bi.BatchPartsInfo.First(bpi => bpi.Index == 0); } httpMessage.SyncContext = syncContext; httpMessage.GetChangeBatch.InMemory = bi.InMemory; httpMessage.GetChangeBatch.ChangesSelected = changesSelected; // if no changes, return if (httpMessage.GetChangeBatch.BatchPartInfo == null) { return(httpMessage); } // if we are not in memory, we set the BI in session, to be able to get it back on next request if (!bi.InMemory) { // Save the BatchInfo this.LocalProvider.CacheManager.Set("GetChangeBatch_BatchInfo", bi); this.LocalProvider.CacheManager.Set("GetChangeBatch_ChangesSelected", changesSelected); // load the batchpart set directly, to be able to send it back var batchPart = httpMessage.GetChangeBatch.BatchPartInfo.GetBatch(); httpMessage.GetChangeBatch.Set = batchPart.DmSetSurrogate; } else { // We are in memory, so generate the DmSetSurrogate to be able to send it back httpMessage.GetChangeBatch.Set = new DmSetSurrogate(httpMessage.GetChangeBatch.BatchPartInfo.Set); httpMessage.GetChangeBatch.BatchPartInfo.Set.Clear(); httpMessage.GetChangeBatch.BatchPartInfo.Set = null; } // no need fileName info httpMessage.GetChangeBatch.BatchPartInfo.FileName = null; return(httpMessage); } // We are in batch mode here var batchInfo = this.LocalProvider.CacheManager.GetValue <BatchInfo>("GetChangeBatch_BatchInfo"); var stats = this.LocalProvider.CacheManager.GetValue <ChangesSelected>("GetChangeBatch_ChangesSelected"); if (batchInfo == null) { throw new ArgumentNullException("batchInfo stored in session can't be null if request more batch part info."); } httpMessage.GetChangeBatch.ChangesSelected = stats; httpMessage.GetChangeBatch.InMemory = batchInfo.InMemory; var batchPartInfo = batchInfo.BatchPartsInfo.FirstOrDefault(bpi => bpi.Index == httpMessage.GetChangeBatch.BatchIndexRequested); httpMessage.GetChangeBatch.BatchPartInfo = batchPartInfo; // load the batchpart set directly, to be able to send it back httpMessage.GetChangeBatch.Set = httpMessage.GetChangeBatch.BatchPartInfo.GetBatch().DmSetSurrogate; if (httpMessage.GetChangeBatch.BatchPartInfo.IsLastBatch) { this.LocalProvider.CacheManager.Remove("GetChangeBatch_BatchInfo"); this.LocalProvider.CacheManager.Remove("GetChangeBatch_ChangesSelected"); } return(httpMessage); }