internal async Task <(SyncContext context, bool deprovisioned)> InternalDeprovisionAsync(IScopeInfo scopeInfo, SyncContext context, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { if (Provider == null) { throw new MissingProviderException(nameof(InternalDeprovisionAsync)); } context.SyncStage = SyncStage.Deprovisioning; // If schema does not have any table, raise an exception if (scopeInfo.Setup == null || scopeInfo.Setup.Tables == null || scopeInfo.Setup.Tables.Count <= 0) { throw new MissingTablesException(scopeInfo.Name); } await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.Deprovisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); await this.InterceptAsync(new DeprovisioningArgs(context, provision, scopeInfo.Setup, runner.Connection, runner.Transaction), progress, cancellationToken).ConfigureAwait(false); // get Database builder var builder = this.Provider.GetDatabaseBuilder(); // Sorting tables based on dependencies between them IEnumerable <SyncTable> schemaTables; if (scopeInfo.Schema != null) { schemaTables = scopeInfo.Schema.Tables.SortByDependencies(tab => tab.GetRelations().Select(r => r.GetParentTable())).ToList(); } else { schemaTables = new List <SyncTable>(); foreach (var setupTable in scopeInfo.Setup.Tables) { ((List <SyncTable>)schemaTables).Add(new SyncTable(setupTable.TableName, setupTable.SchemaName)); } } // Disable check constraints if (this.Options.DisableConstraintsOnApplyChanges) { foreach (var table in schemaTables.Reverse()) { await this.InternalDisableConstraintsAsync(scopeInfo, context, this.GetSyncAdapter(table, scopeInfo), runner.Connection, runner.Transaction).ConfigureAwait(false); } } // Checking if we have to deprovision tables bool hasDeprovisionTableFlag = provision.HasFlag(SyncProvision.Table); // Firstly, removing the flag from the provision, because we need to drop everything in correct order, then drop tables in reverse side if (hasDeprovisionTableFlag) { provision ^= SyncProvision.Table; } foreach (var schemaTable in schemaTables) { var tableBuilder = this.GetTableBuilder(schemaTable, scopeInfo); if (provision.HasFlag(SyncProvision.StoredProcedures)) { (context, _) = await InternalDropStoredProceduresAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); // Removing cached commands var syncAdapter = this.GetSyncAdapter(schemaTable, scopeInfo); syncAdapter.RemoveCommands(); } if (provision.HasFlag(SyncProvision.Triggers)) { (context, _) = await InternalDropTriggersAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.TrackingTable)) { bool exists; (context, exists) = await InternalExistsTrackingTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { (context, _) = await this.InternalDropTrackingTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } } } // Eventually if we have the "Table" flag, then drop the table if (hasDeprovisionTableFlag) { foreach (var schemaTable in schemaTables.Reverse()) { var tableBuilder = this.GetTableBuilder(schemaTable, scopeInfo); bool exists; (context, exists) = await InternalExistsTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { (context, _) = await this.InternalDropTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } } } if (provision.HasFlag(SyncProvision.ClientScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.ServerScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.ServerHistoryScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } } var args = new DeprovisionedArgs(context, provision, scopeInfo.Setup, runner.Connection); await this.InterceptAsync(args, progress, cancellationToken).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(context, true); }
internal async Task <bool> InternalDeprovisionAsync(SyncContext ctx, SyncSet schema, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { await this.InterceptAsync(new DeprovisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false); // get Database builder var builder = this.Provider.GetDatabaseBuilder(); // Sorting tables based on dependencies between them var schemaTables = schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); // Disable check constraints if (this.Options.DisableConstraintsOnApplyChanges) { foreach (var table in schemaTables.Reverse()) { await this.InternalDisableConstraintsAsync(ctx, this.GetSyncAdapter(table, this.Setup), connection, transaction).ConfigureAwait(false); } } // Checking if we have to deprovision tables bool hasDeprovisionTableFlag = provision.HasFlag(SyncProvision.Table); // Firstly, removing the flag from the provision, because we need to drop everything in correct order, then drop tables in reverse side if (hasDeprovisionTableFlag) { provision ^= SyncProvision.Table; } foreach (var schemaTable in schemaTables) { var tableBuilder = this.GetTableBuilder(schemaTable, this.Setup); if (provision.HasFlag(SyncProvision.StoredProcedures)) { var allStoredProcedures = new List <DbStoredProcedureType>(); foreach (var spt in Enum.GetValues(typeof(DbStoredProcedureType))) { allStoredProcedures.Add((DbStoredProcedureType)spt); } allStoredProcedures.Reverse(); foreach (DbStoredProcedureType storedProcedureType in allStoredProcedures) { // if we are iterating on bulk, but provider do not support it, just loop through and continue if ((storedProcedureType is DbStoredProcedureType.BulkTableType || storedProcedureType is DbStoredProcedureType.BulkUpdateRows || storedProcedureType is DbStoredProcedureType.BulkDeleteRows) && !this.Provider.SupportBulkOperations) { continue; } var exists = await InternalExistsStoredProcedureAsync(ctx, tableBuilder, storedProcedureType, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Drop storedProcedure if already exists if (exists) { await InternalDropStoredProcedureAsync(ctx, tableBuilder, storedProcedureType, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Removing cached commands var syncAdapter = this.GetSyncAdapter(schemaTable, this.Setup); syncAdapter.RemoveCommands(); } if (provision.HasFlag(SyncProvision.Triggers)) { foreach (DbTriggerType triggerType in Enum.GetValues(typeof(DbTriggerType))) { var exists = await InternalExistsTriggerAsync(ctx, tableBuilder, triggerType, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Drop trigger if already exists if (exists) { await InternalDropTriggerAsync(ctx, tableBuilder, triggerType, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } } if (provision.HasFlag(SyncProvision.TrackingTable)) { var exists = await InternalExistsTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await this.InternalDropTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } } // Eventually if we have the "Table" flag, then drop the table if (hasDeprovisionTableFlag) { foreach (var schemaTable in schemaTables.Reverse()) { var tableBuilder = this.GetTableBuilder(schemaTable, this.Setup); var exists = await InternalExistsTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await this.InternalDropTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } } // Get Scope Builder var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); if (provision.HasFlag(SyncProvision.ClientScope)) { var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.ServerScope)) { var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.ServerHistoryScope)) { var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } var args = new DeprovisionedArgs(ctx, provision, schema, connection); await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false); this.ReportProgress(ctx, progress, args); return(true); }