InternalGetSnapshotAsync(ServerScopeInfo serverScopeInfo, SyncContext context, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { try { await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ScopeLoading, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Get context or create a new one var changesSelected = new DatabaseChangesSelected(); BatchInfo serverBatchInfo = null; if (string.IsNullOrEmpty(this.Options.SnapshotsDirectory)) { return(context, 0, null, changesSelected); } //Direction set to Download context.SyncWay = SyncWay.Download; if (cancellationToken.IsCancellationRequested) { cancellationToken.ThrowIfCancellationRequested(); } // Get Schema from remote provider if no schema passed from args if (serverScopeInfo.Schema == null) { (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, serverScopeInfo.Setup, false, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false); } // When we get the changes from server, we create the batches if it's requested by the client // the batch decision comes from batchsize from client var(rootDirectory, nameDirectory) = await this.InternalGetSnapshotDirectoryPathAsync(serverScopeInfo.Name, context.Parameters, runner.CancellationToken, runner.Progress).ConfigureAwait(false); if (!string.IsNullOrEmpty(rootDirectory)) { var directoryFullPath = Path.Combine(rootDirectory, nameDirectory); // if no snapshot present, just return null value. if (Directory.Exists(directoryFullPath)) { // Serialize on disk. var jsonConverter = new Serialization.JsonConverter <BatchInfo>(); var summaryFileName = Path.Combine(directoryFullPath, "summary.json"); using (var fs = new FileStream(summaryFileName, FileMode.Open, FileAccess.Read)) { serverBatchInfo = await jsonConverter.DeserializeAsync(fs).ConfigureAwait(false); } // Create the schema changeset var changesSet = new SyncSet(); // Create a Schema set without readonly columns, attached to memory changes foreach (var table in serverScopeInfo.Schema.Tables) { DbSyncAdapter.CreateChangesTable(serverScopeInfo.Schema.Tables[table.TableName, table.SchemaName], changesSet); // Get all stats about this table var bptis = serverBatchInfo.BatchPartsInfo.SelectMany(bpi => bpi.Tables.Where(t => { var sc = SyncGlobalization.DataSourceStringComparison; var sn = t.SchemaName == null ? string.Empty : t.SchemaName; var otherSn = table.SchemaName == null ? string.Empty : table.SchemaName; return(table.TableName.Equals(t.TableName, sc) && sn.Equals(otherSn, sc)); })); if (bptis != null) { // Statistics var tableChangesSelected = new TableChangesSelected(table.TableName, table.SchemaName) { // we are applying a snapshot where it can't have any deletes, obviously Upserts = bptis.Sum(bpti => bpti.RowsCount) }; if (tableChangesSelected.Upserts > 0) { changesSelected.TableChangesSelected.Add(tableChangesSelected); } } } serverBatchInfo.SanitizedSchema = changesSet; } } if (serverBatchInfo == null) { return(context, 0, null, changesSelected); } await runner.CommitAsync().ConfigureAwait(false); return(context, serverBatchInfo.Timestamp, serverBatchInfo, changesSelected); } catch (Exception ex) { throw GetSyncError(context, ex); } }
InternalCreateSnapshotAsync(ServerScopeInfo serverScopeInfo, SyncContext context, long remoteClientTimestamp, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null) { if (Provider == null) { throw new MissingProviderException(nameof(InternalCreateSnapshotAsync)); } await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.SnapshotCreating, connection, transaction, cancellationToken, progress).ConfigureAwait(false); await this.InterceptAsync(new SnapshotCreatingArgs(context, serverScopeInfo.Schema, this.Options.SnapshotsDirectory, this.Options.BatchSize, remoteClientTimestamp, this.Provider.CreateConnection(), null), progress, cancellationToken).ConfigureAwait(false); if (!Directory.Exists(this.Options.SnapshotsDirectory)) { Directory.CreateDirectory(this.Options.SnapshotsDirectory); } var(rootDirectory, nameDirectory) = await this.InternalGetSnapshotDirectoryPathAsync(serverScopeInfo.Name, context.Parameters, cancellationToken, progress).ConfigureAwait(false); // create local directory with scope inside if (!Directory.Exists(rootDirectory)) { Directory.CreateDirectory(rootDirectory); } // Delete directory if already exists var directoryFullPath = Path.Combine(rootDirectory, nameDirectory); // Delete old version if exists if (Directory.Exists(directoryFullPath)) { Directory.Delete(directoryFullPath, true); } BatchInfo serverBatchInfo; (context, serverBatchInfo, _) = await this.InternalGetChangesAsync(serverScopeInfo, context, true, null, null, Guid.Empty, this.Provider.SupportsMultipleActiveResultSets, rootDirectory, nameDirectory, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // since we explicitely defined remote client timestamp to null, to get all rows, just reaffect here serverBatchInfo.Timestamp = remoteClientTimestamp; // Serialize on disk. var jsonConverter = new Serialization.JsonConverter <BatchInfo>(); var summaryFileName = Path.Combine(directoryFullPath, "summary.json"); using (var f = new FileStream(summaryFileName, FileMode.CreateNew, FileAccess.ReadWrite)) { var bytes = await jsonConverter.SerializeAsync(serverBatchInfo).ConfigureAwait(false); f.Write(bytes, 0, bytes.Length); } await this.InterceptAsync(new SnapshotCreatedArgs(context, serverBatchInfo, this.Provider.CreateConnection(), null), progress, cancellationToken).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(context, serverBatchInfo); }