GetEstimatedChangesCountAsync(ClientScopeInfo clientScopeInfo, SyncParameters parameters = default, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { var context = new SyncContext(Guid.NewGuid(), clientScopeInfo.Name); if (parameters != null) { context.Parameters = parameters; } SyncSet schema; ServerScopeInfo serverScopeInfo; // Need the server scope (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, clientScopeInfo.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); schema = serverScopeInfo.Schema; schema.EnsureSchema(); clientScopeInfo.Schema = schema; clientScopeInfo.Setup = serverScopeInfo.Setup; clientScopeInfo.Version = serverScopeInfo.Version; // generate a message to send var changesToSend = new HttpMessageSendChangesRequest(context, clientScopeInfo) { Changes = null, IsLastBatch = true, BatchIndex = 0, BatchCount = 0 }; var serializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>(); var binaryData = await serializer.SerializeAsync(changesToSend); // Raise progress for sending request and waiting server response await this.InterceptAsync(new HttpGettingServerChangesRequestArgs(0, 0, context, this.GetServiceHost()), progress, cancellationToken).ConfigureAwait(false); // response var response = await this.httpRequestHandler.ProcessRequestAsync (this.HttpClient, context, this.ServiceUri, binaryData, HttpStep.GetEstimatedChangesCount, this.SerializerFactory, this.Converter, this.Options.BatchSize, this.SyncPolicy, cancellationToken, progress).ConfigureAwait(false); HttpMessageSendChangesResponse summaryResponseContent = null; using (var streamResponse = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { var responseSerializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesResponse>(); if (streamResponse.CanRead) { summaryResponseContent = await responseSerializer.DeserializeAsync(streamResponse); } } if (summaryResponseContent == null) { throw new Exception("Summary can't be null"); } // generate the new scope item this.CompleteTime = DateTime.UtcNow; return(new (summaryResponseContent.RemoteClientTimestamp, null, summaryResponseContent.ServerChangesSelected)); }
ApplyThenGetChangesAsync(ScopeInfo scope, BatchInfo clientBatchInfo, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { SyncSet schema; // Get context or create a new one var ctx = this.GetContext(); if (!this.StartTime.HasValue) { this.StartTime = DateTime.UtcNow; } //// create the in memory changes set //var changesSet = new SyncSet(); // is it something that could happens ? if (scope.Schema == null) { // Make a remote call to get Schema from remote provider var serverScopeInfo = await this.EnsureSchemaAsync(cancellationToken, progress).ConfigureAwait(false); schema = serverScopeInfo.Schema; } else { schema = scope.Schema; } schema.EnsureSchema(); // if we don't have any BatchPartsInfo, just generate a new one to get, at least, something to send to the server // and get a response with new data from server if (clientBatchInfo == null) { clientBatchInfo = new BatchInfo(true, schema); } // Get sanitized schema, without readonly columns var sanitizedSchema = clientBatchInfo.SanitizedSchema; // -------------------------------------------------------------- // STEP 1 : Send everything to the server side // -------------------------------------------------------------- // response HttpMessageSendChangesResponse httpMessageContent = null; // If not in memory and BatchPartsInfo.Count == 0, nothing to send. // But we need to send something, so generate a little batch part if (clientBatchInfo.InMemory || (!clientBatchInfo.InMemory && clientBatchInfo.BatchPartsInfo.Count == 0)) { var changesToSend = new HttpMessageSendChangesRequest(ctx, scope); if (this.Converter != null && clientBatchInfo.InMemoryData != null && clientBatchInfo.InMemoryData.HasRows) { this.BeforeSerializeRows(clientBatchInfo.InMemoryData); } var containerSet = clientBatchInfo.InMemoryData == null ? new ContainerSet() : clientBatchInfo.InMemoryData.GetContainerSet(); changesToSend.Changes = containerSet; changesToSend.IsLastBatch = true; changesToSend.BatchIndex = 0; // serialize message var serializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>(); var binaryData = await serializer.SerializeAsync(changesToSend); await this.InterceptAsync(new HttpMessageSendChangesRequestArgs(binaryData), cancellationToken).ConfigureAwait(false); httpMessageContent = await this.httpRequestHandler.ProcessRequestAsync <HttpMessageSendChangesResponse> (this.HttpClient, this.ServiceUri, binaryData, HttpStep.SendChanges, ctx.SessionId, scope.Name, this.SerializerFactory, this.Converter, this.Options.BatchSize, cancellationToken).ConfigureAwait(false); } else { // Foreach part, will have to send them to the remote // once finished, return context foreach (var bpi in clientBatchInfo.BatchPartsInfo.OrderBy(bpi => bpi.Index)) { // If BPI is InMempory, no need to deserialize from disk // othewise load it await bpi.LoadBatchAsync(sanitizedSchema, clientBatchInfo.GetDirectoryFullPath(), this); var changesToSend = new HttpMessageSendChangesRequest(ctx, scope); if (this.Converter != null && bpi.Data.HasRows) { BeforeSerializeRows(bpi.Data); } // Set the change request properties changesToSend.Changes = bpi.Data.GetContainerSet(); changesToSend.IsLastBatch = bpi.IsLastBatch; changesToSend.BatchIndex = bpi.Index; // serialize message var serializer = this.SerializerFactory.GetSerializer <HttpMessageSendChangesRequest>(); var binaryData = await serializer.SerializeAsync(changesToSend); await this.InterceptAsync(new HttpMessageSendChangesRequestArgs(binaryData), cancellationToken).ConfigureAwait(false); httpMessageContent = await this.httpRequestHandler.ProcessRequestAsync <HttpMessageSendChangesResponse> (this.HttpClient, this.ServiceUri, binaryData, HttpStep.SendChanges, ctx.SessionId, scope.Name, this.SerializerFactory, this.Converter, this.Options.BatchSize, cancellationToken).ConfigureAwait(false); // for some reasons, if server don't want to wait for more, just break // That should never happened, actually if (httpMessageContent.ServerStep != HttpStep.SendChangesInProgress) { break; } } } // -------------------------------------------------------------- // STEP 2 : Receive everything from the server side // -------------------------------------------------------------- // Now we have sent all the datas to the server and now : // We have a FIRST response from the server with new datas // 1) Could be the only one response (enough or InMemory is set on the server side) // 2) Could bt the first response and we need to download all batchs // While we have an other batch to process var isLastBatch = false; // Get if we need to work in memory or serialize things var workInMemoryLocally = this.Options.BatchSize == 0; // Create the BatchInfo and SyncContext to return at the end // Set InMemory by default to "true", but the real value is coming from server side var serverBatchInfo = new BatchInfo(workInMemoryLocally, schema, this.Options.BatchDirectory); // stats DatabaseChangesSelected serverChangesSelected = null; DatabaseChangesApplied clientChangesApplied = null; //timestamp generated by the server, hold in the client db long remoteClientTimestamp = 0; // While we are not reaching the last batch from server do { // Check if we are at the last batch. // If so, we won't make another loop isLastBatch = httpMessageContent.IsLastBatch; serverChangesSelected = httpMessageContent.ServerChangesSelected; clientChangesApplied = httpMessageContent.ClientChangesApplied; ctx = httpMessageContent.SyncContext; remoteClientTimestamp = httpMessageContent.RemoteClientTimestamp; var changesSet = sanitizedSchema.Clone(); changesSet.ImportContainerSet(httpMessageContent.Changes, false); if (this.Converter != null && changesSet.HasRows) { AfterDeserializedRows(changesSet); } // Create a BatchPartInfo instance await serverBatchInfo.AddChangesAsync(changesSet, httpMessageContent.BatchIndex, isLastBatch, this); // free some memory if (!workInMemoryLocally && httpMessageContent.Changes != null) { httpMessageContent.Changes.Clear(); } if (!isLastBatch) { // Ask for the next batch index var requestBatchIndex = httpMessageContent.BatchIndex + 1; // Create the message enveloppe var httpMessage = new HttpMessageGetMoreChangesRequest(ctx, requestBatchIndex); // serialize message var serializer = this.SerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>(); var binaryData = await serializer.SerializeAsync(httpMessage); await this.InterceptAsync(new HttpMessageGetMoreChangesRequestArgs(binaryData), cancellationToken).ConfigureAwait(false); httpMessageContent = await this.httpRequestHandler.ProcessRequestAsync <HttpMessageSendChangesResponse>( this.HttpClient, this.ServiceUri, binaryData, HttpStep.GetMoreChanges, ctx.SessionId, scope.Name, this.SerializerFactory, this.Converter, this.Options.BatchSize, cancellationToken).ConfigureAwait(false); } } while (!isLastBatch); // generate the new scope item this.CompleteTime = DateTime.UtcNow; // Reaffect context this.SetContext(httpMessageContent.SyncContext); return(remoteClientTimestamp, serverBatchInfo, httpMessageContent.ConflictResolutionPolicy, clientChangesApplied, serverChangesSelected); }