/// <summary> /// Update or Insert a server scope row /// </summary> public virtual async Task <ServerScopeInfo> SaveServerScopeInfoAsync(ServerScopeInfo serverScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { var context = new SyncContext(Guid.NewGuid(), serverScopeInfo.Name); try { await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.ScopeWriting, connection, transaction, cancellationToken, progress).ConfigureAwait(false); bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } // Write scopes locally (context, serverScopeInfo) = await this.InternalSaveServerScopeInfoAsync(serverScopeInfo, context, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(serverScopeInfo); } catch (Exception ex) { throw GetSyncError(context, ex); } }
private async Task <Version> UpgdrateTo094Async(ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { var newVersion = new Version(0, 9, 4); // Sorting tables based on dependencies between them var schemaTables = clientScopeInfo.Schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); var message = $"Upgrade to {newVersion}:"; await this.InterceptAsync(new UpgradeProgressArgs(context, message, newVersion, connection, transaction), progress, cancellationToken).ConfigureAwait(false); var provision = SyncProvision.StoredProcedures | SyncProvision.Triggers; await this.DeprovisionAsync(clientScopeInfo.Name, provision, connection, transaction, cancellationToken, progress).ConfigureAwait(false); var clientScope = await this.GetClientScopeInfoAsync(clientScopeInfo.Name, connection, transaction, cancellationToken, progress).ConfigureAwait(false); var serverScope = new ServerScopeInfo { Schema = clientScope.Schema, Setup = clientScope.Setup, Version = clientScope.Version }; clientScope = await this.ProvisionAsync(serverScope, provision, true, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(newVersion); }
InternalSaveServerScopeInfoAsync(ServerScopeInfo serverScopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); bool scopeExists; (context, scopeExists) = await InternalExistsServerScopeInfoAsync(context, connection, transaction, cancellationToken, progress).ConfigureAwait(false); DbCommand command; if (scopeExists) { command = scopeBuilder.GetCommandAsync(DbScopeCommandType.UpdateServerScopeInfo, connection, transaction); } else { command = scopeBuilder.GetCommandAsync(DbScopeCommandType.InsertServerScopeInfo, connection, transaction); } if (command == null) { return(context, null); } command = InternalSetSaveServerScopeInfoParameters(serverScopeInfo, command); var action = new ScopeSavingArgs(context, scopeBuilder.ScopeInfoTableName.ToString(), DbScopeType.Server, serverScopeInfo, command, connection, transaction); await this.InterceptAsync(action, progress, cancellationToken).ConfigureAwait(false); if (action.Cancel || action.Command == null) { return(default);
public OperationArgs(SyncContext context, ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction) { this.ServerScopeInfo = serverScopeInfo; this.ClientScopeInfo = clientScopeInfo; }
/// <summary> /// Get the local configuration, ensures the local scope is created /// </summary> /// <returns>Server scope info, containing all scopes names, version, setup and related schema infos</returns> public virtual Task <ServerScopeInfo> GetServerScopeAsync(DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) => RunInTransactionAsync(SyncStage.ScopeLoading, async(ctx, connection, transaction) => { ServerScopeInfo serverScopeInfo = null; var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } serverScopeInfo = await this.InternalGetScopeAsync <ServerScopeInfo>(ctx, DbScopeType.Server, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // if serverscopeinfo is a new, because we never run any sync before, grab schema and affect setup if (serverScopeInfo.Setup == null && serverScopeInfo.Schema == null) { // 1) Get Schema from remote provider var schema = await this.InternalGetSchemaAsync(ctx, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // 2) Provision var provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers; schema = await InternalProvisionAsync(ctx, false, schema, this.Setup, provision, serverScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(serverScopeInfo); } // Compare serverscope setup with current if (!serverScopeInfo.Setup.EqualsByProperties(this.Setup)) { SyncSet schema; // 1) Get Schema from remote provider schema = await this.InternalGetSchemaAsync(ctx, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Migrate the old setup (serverScopeInfo.Setup) to the new setup (this.Setup) based on the new schema await this.InternalMigrationAsync(ctx, schema, serverScopeInfo.Setup, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); serverScopeInfo.Setup = this.Setup; serverScopeInfo.Schema = schema; // Write scopes locally await this.InternalSaveScopeAsync(ctx, DbScopeType.Server, serverScopeInfo, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } return(serverScopeInfo); }, connection, transaction, cancellationToken);
private ServerScopeInfo InternalReadServerScopeInfo(DbDataReader reader) { var serverScopeInfo = new ServerScopeInfo { Name = reader["sync_scope_name"] as string, Schema = reader["sync_scope_schema"] == DBNull.Value ? null : JsonConvert.DeserializeObject <SyncSet>((string)reader["sync_scope_schema"]), Setup = reader["sync_scope_setup"] == DBNull.Value ? null : JsonConvert.DeserializeObject <SyncSetup>((string)reader["sync_scope_setup"]), Version = reader["sync_scope_version"] as string, LastCleanupTimestamp = reader["sync_scope_last_clean_timestamp"] != DBNull.Value ? reader.GetInt64(reader.GetOrdinal("sync_scope_last_clean_timestamp")) : 0L }; return(serverScopeInfo); }
private DbCommand InternalSetSaveServerScopeInfoParameters(ServerScopeInfo serverScopeInfo, DbCommand command) { var serializedSchema = JsonConvert.SerializeObject(serverScopeInfo.Schema); var serializedSetup = JsonConvert.SerializeObject(serverScopeInfo.Setup); DbSyncAdapter.SetParameterValue(command, "sync_scope_name", serverScopeInfo.Name); DbSyncAdapter.SetParameterValue(command, "sync_scope_schema", serverScopeInfo.Schema == null ? DBNull.Value : serializedSchema); DbSyncAdapter.SetParameterValue(command, "sync_scope_setup", serverScopeInfo.Setup == null ? DBNull.Value : serializedSetup); DbSyncAdapter.SetParameterValue(command, "sync_scope_version", serverScopeInfo.Version); DbSyncAdapter.SetParameterValue(command, "sync_scope_last_clean_timestamp", serverScopeInfo.LastCleanupTimestamp); return(command); }
/// <summary> /// Check if the orchestrator database is outdated /// </summary> /// <param name="timeStampStart">Timestamp start. Used to limit the delete metadatas rows from now to this timestamp</param> /// <param name="cancellationToken">Cancellation token</param> /// <param name="progress">Progress args</param> public virtual async Task <bool> IsOutDated(ScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { if (!this.StartTime.HasValue) { this.StartTime = DateTime.UtcNow; } bool isOutdated = false; // Get context or create a new one var ctx = this.GetContext(); // if we have a new client, obviously the last server sync is < to server stored last clean up (means OutDated !) // so far we return directly false if (clientScopeInfo.IsNewScope) { return(false); } // Check if the provider is not outdated // We can have negative value where we want to compare anyway if (clientScopeInfo.LastServerSyncTimestamp != 0 || serverScopeInfo.LastCleanupTimestamp != 0) { isOutdated = clientScopeInfo.LastServerSyncTimestamp < serverScopeInfo.LastCleanupTimestamp; this.logger.LogInformation(SyncEventsId.IsOutdated, new { serverScopeInfo.LastCleanupTimestamp, clientScopeInfo.LastServerSyncTimestamp, IsOutDated = isOutdated }); } // Get a chance to make the sync even if it's outdated if (isOutdated) { var outdatedArgs = new OutdatedArgs(ctx, clientScopeInfo, serverScopeInfo); // Interceptor await this.InterceptAsync(outdatedArgs, cancellationToken).ConfigureAwait(false); if (outdatedArgs.Action != OutdatedAction.Rollback) { ctx.SyncType = outdatedArgs.Action == OutdatedAction.Reinitialize ? SyncType.Reinitialize : SyncType.ReinitializeWithUpload; this.logger.LogDebug(SyncEventsId.IsOutdated, outdatedArgs); } if (outdatedArgs.Action == OutdatedAction.Rollback) { throw new OutOfDateException(clientScopeInfo.LastServerSyncTimestamp, serverScopeInfo.LastCleanupTimestamp); } } return(isOutdated); }
InternalLoadServerScopeInfoAsync(SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); using var command = scopeBuilder.GetCommandAsync(DbScopeCommandType.GetServerScopeInfo, connection, transaction); if (command == null) { return(context, null); } DbSyncAdapter.SetParameterValue(command, "sync_scope_name", context.ScopeName); var action = new ServerScopeInfoLoadingArgs(context, context.ScopeName, command, connection, transaction); await this.InterceptAsync(action, progress, cancellationToken).ConfigureAwait(false); if (action.Cancel || action.Command == null) { return(context, null); } await this.InterceptAsync(new DbCommandArgs(context, action.Command, connection, transaction), progress, cancellationToken).ConfigureAwait(false); using DbDataReader reader = await action.Command.ExecuteReaderAsync().ConfigureAwait(false); ServerScopeInfo serverScopeInfo = null; if (reader.Read()) { serverScopeInfo = InternalReadServerScopeInfo(reader); } reader.Close(); if (serverScopeInfo?.Schema != null) { serverScopeInfo.Schema.EnsureSchema(); } var scopeLoadedArgs = new ServerScopeInfoLoadedArgs(context, context.ScopeName, serverScopeInfo, connection, transaction); await this.InterceptAsync(scopeLoadedArgs, progress, cancellationToken).ConfigureAwait(false); serverScopeInfo = scopeLoadedArgs.ServerScopeInfo; action.Command.Dispose(); return(context, serverScopeInfo); }
public virtual async Task <ServerScopeInfo> ProvisionAsync(ServerScopeInfo serverScopeInfo, SyncProvision provision = default, bool overwrite = false, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { var context = new SyncContext(Guid.NewGuid(), serverScopeInfo.Name); try { (_, serverScopeInfo) = await InternalProvisionServerAsync(serverScopeInfo, context, provision, overwrite, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(serverScopeInfo); } catch (Exception ex) { throw GetSyncError(context, ex); } }
/// <summary> /// Update or Insert a server scope row /// </summary> public virtual Task <ServerScopeInfo> SaveServerScopeAsync(ServerScopeInfo scopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) => RunInTransactionAsync(SyncStage.ScopeWriting, async(ctx, connection, transaction) => { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Write scopes locally var scopeInfoUpdated = await this.InternalSaveScopeAsync(ctx, DbScopeType.Server, scopeInfo, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(scopeInfoUpdated); }, connection, transaction, cancellationToken);
InternalProvisionClientAsync(ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, SyncContext context, SyncProvision provision = default, bool overwrite = true, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { try { if (serverScopeInfo.Schema == null) { throw new Exception($"No Schema in your server scope info {serverScopeInfo.Name}"); } if (serverScopeInfo.Schema == null) { throw new Exception($"No Setup in your server scope info {serverScopeInfo.Name}"); } await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.Provisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Check incompatibility with the flags if (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope)) { throw new InvalidProvisionForLocalOrchestratorException(); } // 2) Provision if (provision == SyncProvision.None) { provision = SyncProvision.Table | SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable; } (context, _) = await this.InternalProvisionAsync(serverScopeInfo, context, overwrite, provision, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); // set client scope setup and schema clientScopeInfo.Setup = serverScopeInfo.Setup; clientScopeInfo.Schema = serverScopeInfo.Schema; // Write scopes locally (context, clientScopeInfo) = await this.InternalSaveClientScopeInfoAsync(clientScopeInfo, context, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(context, clientScopeInfo); } catch (Exception ex) { throw GetSyncError(context, ex); } }
/// <summary> /// Get Client scope information /// </summary> public virtual async Task <(SyncContext, ServerScopeInfo)> GetServerScopeAsync(SyncContext context, string scopeInfoTableName, string scopeName, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null) { var scopeBuilder = this.GetScopeBuilder(); var scopeInfoBuilder = scopeBuilder.CreateScopeInfoBuilder(scopeInfoTableName); this.Orchestrator.logger.LogDebug(SyncEventsId.GetClientScope, new { ScopeName = scopeName, ScopeInfoTableName = scopeInfoTableName }); // get all scopes shared by all (identified by scopeName) var serverScopes = await scopeInfoBuilder.GetAllServerScopesAsync(scopeName, connection, transaction).ConfigureAwait(false); this.Orchestrator.logger.LogDebug(SyncEventsId.GetServerScope, new { ScopeCountFound = serverScopes.Count }); // If no scope found, create it on the local provider if (serverScopes == null || serverScopes.Count <= 0) { serverScopes = new List <ServerScopeInfo>(); // create a new scope id for the current owner (could be server or client as well) var scope = new ServerScopeInfo { Name = scopeName, LastCleanupTimestamp = 0, Version = "1" }; scope = await scopeInfoBuilder.InsertOrUpdateServerScopeInfoAsync(scope, connection, transaction).ConfigureAwait(false); serverScopes.Add(scope); } // get first scope var localScope = serverScopes.FirstOrDefault(); if (localScope.Schema != null) { localScope.Schema.EnsureSchema(); } this.Orchestrator.logger.LogDebug(SyncEventsId.GetServerScope, localScope); return(context, localScope); }
ProvisionAsync(ServerScopeInfo serverScopeInfo, SyncProvision provision = default, bool overwrite = true, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { var context = new SyncContext(Guid.NewGuid(), serverScopeInfo.Name); try { await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.Provisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); ClientScopeInfo clientScopeInfo; (context, clientScopeInfo) = await InternalGetClientScopeInfoAsync(context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false); (context, clientScopeInfo) = await InternalProvisionClientAsync(serverScopeInfo, clientScopeInfo, context, provision, overwrite, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(clientScopeInfo); } catch (Exception ex) { throw GetSyncError(context, ex); } }
InternalGetOperationAsync(ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { try { SyncOperation syncOperation = SyncOperation.Normal; await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.Provisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); var operationArgs = new OperationArgs(context, serverScopeInfo, clientScopeInfo, runner.Connection, runner.Transaction); await this.InterceptAsync(operationArgs, runner.Progress, runner.CancellationToken).ConfigureAwait(false); syncOperation = operationArgs.Operation; await runner.CommitAsync().ConfigureAwait(false); return(context, syncOperation); } catch (Exception ex) { throw GetSyncError(context, ex); } }
/// <summary> /// Deprovision the orchestrator database based on the schema argument, and the provision enumeration /// </summary> /// <param name="schema">Schema to be deprovisioned from the database managed by the orchestrator, through the provider.</param> /// <param name="provision">Provision enumeration to determine which components to deprovision</param> public virtual Task DeprovisionAsync(SyncProvision provision, ServerScopeInfo serverScopeInfo = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) => RunInTransactionAsync(SyncStage.Deprovisioning, async(ctx, connection, transaction) => { // Get server scope if not supplied if (serverScopeInfo == null) { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { serverScopeInfo = await this.InternalGetScopeAsync <ServerScopeInfo>(ctx, DbScopeType.Server, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Create a temporary SyncSet for attaching to the schemaTable var tmpSchema = new SyncSet(); // Add this table to schema foreach (var table in this.Setup.Tables) { tmpSchema.Tables.Add(new SyncTable(table.TableName, table.SchemaName)); } tmpSchema.EnsureSchema(); // copy filters from old setup foreach (var filter in this.Setup.Filters) { tmpSchema.Filters.Add(filter); } var isDeprovisioned = await InternalDeprovisionAsync(ctx, tmpSchema, this.Setup, provision, serverScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(isDeprovisioned); }, connection, transaction, cancellationToken);
GetSnapshotAsync(ServerScopeInfo serverScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { var context = new SyncContext(Guid.NewGuid(), serverScopeInfo.Name); try { await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ScopeLoading, connection, transaction, cancellationToken, progress).ConfigureAwait(false); long remoteClientTimestamp; BatchInfo serverBatchInfo; DatabaseChangesSelected databaseChangesSelected; (context, remoteClientTimestamp, serverBatchInfo, databaseChangesSelected) = await this.InternalGetSnapshotAsync(serverScopeInfo, context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(remoteClientTimestamp, serverBatchInfo, databaseChangesSelected); } catch (Exception ex) { throw GetSyncError(context, ex); } }
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); }
public ServerScopeLoadedArgs(SyncContext context, ServerScopeInfo scope, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction) { this.ScopeInfo = scope; }
public HttpGettingScopeResponseArgs(ServerScopeInfo scopeInfo, SyncContext context, string host) : base(context, null) { this.ServerScopeInfo = scopeInfo; this.Host = host; }
public ServerScopeInfoLoadedArgs(SyncContext context, string scopeName, ServerScopeInfo serverScopeInfo, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction) { this.ScopeName = scopeName; this.ServerScopeInfo = serverScopeInfo; }
/// <summary> /// Check /// </summary> public virtual async Task <(SyncContext, bool, ClientScopeInfo, ServerScopeInfo)> IsConflictingSetupAsync(SyncContext context, SyncSetup inputSetup, ClientScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { if (clientScopeInfo.IsNewScope || clientScopeInfo.Schema == null) { return(context, false, clientScopeInfo, serverScopeInfo); } if (inputSetup != null && clientScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(inputSetup)) { var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo); await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false); if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback) { throw new Exception("Seems you are trying another Setup tables that what is stored in your client scope database. Please create a new scope or deprovision and provision again your client scope."); } if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort) { return(context, true, clientScopeInfo, serverScopeInfo); } // re affect scope infos clientScopeInfo = conflictingSetupArgs.ClientScopeInfo; serverScopeInfo = conflictingSetupArgs.ServerScopeInfo; } if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup)) { var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo); await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false); if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback) { throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope."); } if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort) { return(context, true, clientScopeInfo, serverScopeInfo); } // re affect scope infos clientScopeInfo = conflictingSetupArgs.ClientScopeInfo; serverScopeInfo = conflictingSetupArgs.ServerScopeInfo; } // We gave 2 chances to user to edit the setup and fill correct values. // Final check, but if not valid, raise an error if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup)) { throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope."); } return(context, false, clientScopeInfo, serverScopeInfo); }
public ConflictingSetupArgs(SyncContext context, SyncSetup setup, ClientScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction) { this.Setup = setup; this.ClientScopeInfo = clientScopeInfo; this.ServerScopeInfo = serverScopeInfo; }
/// <summary> /// Write scope in the remote data source /// </summary> public virtual async Task <SyncContext> WriteServerScopeAsync(SyncContext context, string scopeInfoTableName, ServerScopeInfo scope, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null) { var scopeBuilder = this.GetScopeBuilder(); var scopeInfoBuilder = scopeBuilder.CreateScopeInfoBuilder(scopeInfoTableName); this.Orchestrator.logger.LogDebug(SyncEventsId.WriteServerScope, scope); await scopeInfoBuilder.InsertOrUpdateServerScopeInfoAsync(scope, connection, transaction).ConfigureAwait(false); return(context); }
/// <summary> /// Provision the remote database based on the Setup parameter, and the provision enumeration /// </summary> /// <param name="provision">Provision enumeration to determine which components to apply</param> /// <param name="serverScopeInfo">server scope. Will be saved once provision is done</param> /// <returns>Full schema with table and columns properties</returns> public virtual Task <SyncSet> ProvisionAsync(SyncProvision provision, bool overwrite = false, ServerScopeInfo serverScopeInfo = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) => RunInTransactionAsync(SyncStage.Provisioning, async(ctx, connection, transaction) => { // Check incompatibility with the flags if (provision.HasFlag(SyncProvision.ClientScope)) { throw new InvalidProvisionForRemoteOrchestratorException(); } // Get server scope if not supplied if (serverScopeInfo == null) { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { serverScopeInfo = await this.InternalGetScopeAsync <ServerScopeInfo>(ctx, DbScopeType.Server, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } var schema = new SyncSet(this.Setup); schema = await InternalProvisionAsync(ctx, overwrite, schema, this.Setup, provision, serverScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(schema); }, connection, transaction, cancellationToken);