/// <summary> /// Deprovision table /// </summary> public async Task DropAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null) { var alreadyOpened = connection.State != ConnectionState.Closed; if (!alreadyOpened) { await connection.OpenAsync().ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.StoredProcedures)) { await this.DropStoredProceduresAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Triggers)) { await this.DropTriggersAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.TrackingTable)) { await this.DropTrackingTableAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Table)) { await this.DropTableAsync(connection, transaction).ConfigureAwait(false); } if (!alreadyOpened) { connection.Close(); } }
/// <summary> /// Provision the local database based on the schema parameter, and the provision enumeration /// </summary> /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param> /// <param name="provision">Provision enumeration to determine which components to apply</param> /// <param name="clientScopeInfo">client scope. Will be saved once provision is done</param> /// <returns>Full schema with table and columns properties</returns> public virtual Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, bool overwrite = false, ScopeInfo clientScopeInfo = 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.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope)) { throw new InvalidProvisionForLocalOrchestratorException(); } // Get server scope if not supplied if (clientScopeInfo == null) { var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { clientScopeInfo = await this.InternalGetScopeAsync <ScopeInfo>(ctx, DbScopeType.Client, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } schema = await InternalProvisionAsync(ctx, overwrite, schema, this.Setup, provision, clientScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(schema); }, connection, transaction, cancellationToken);
/// <summary> /// Apply the config. /// Create the table if needed /// </summary> public async Task CreateAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null) { if (TableDescription.Columns.Count <= 0) { throw new MissingsColumnException(TableDescription.TableName); } if (TableDescription.PrimaryKeys.Count <= 0) { throw new MissingPrimaryKeyException(TableDescription.TableName); } var alreadyOpened = connection.State != ConnectionState.Closed; if (!alreadyOpened) { await connection.OpenAsync().ConfigureAwait(false); } System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); if (provision.HasFlag(SyncProvision.Table)) { await this.CreateTableAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.TrackingTable)) { await this.CreateTrackingTableAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Triggers)) { await this.CreateTriggersAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.StoredProcedures)) { await this.CreateStoredProceduresAsync(connection, transaction).ConfigureAwait(false); } stopwatch.Stop(); var str = $"{stopwatch.Elapsed.Minutes}:{stopwatch.Elapsed.Seconds}.{stopwatch.Elapsed.Milliseconds}"; System.Diagnostics.Debug.WriteLine(str); if (!alreadyOpened) { connection.Close(); } }
/// <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 async Task <ServerScopeInfo> ProvisionAsync(string scopeName, SyncSetup setup = null, 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(), scopeName); try { // Check incompatibility with the flags if (provision.HasFlag(SyncProvision.ClientScope)) { throw new InvalidProvisionForRemoteOrchestratorException(); } await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.Provisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); ServerScopeInfo serverScopeInfo; (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, setup, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false); // 2) Provision if (provision == SyncProvision.None) { provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers; } (context, _) = await this.InternalProvisionAsync(serverScopeInfo, context, false, provision, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); return(serverScopeInfo); } catch (Exception ex) { throw GetSyncError(context, ex); } }
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> /// Provision the orchestrator database based on the schema argument, and the provision enumeration /// </summary> /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param> /// <param name="provision">Provision enumeration to determine which components to apply</param> /// <returns>Full schema with table and columns properties</returns> public virtual Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, bool overwrite = false, 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 (this is LocalOrchestrator && (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope))) { throw new InvalidProvisionForLocalOrchestratorException(); } else if (!(this is LocalOrchestrator) && provision.HasFlag(SyncProvision.ClientScope)) { throw new InvalidProvisionForRemoteOrchestratorException(); } schema = await InternalProvisionAsync(ctx, overwrite, schema, provision, connection, transaction, cancellationToken, progress).ConfigureAwait(false); return(schema); }, connection, transaction, cancellationToken);
/// <summary> /// Apply the config. /// Create the table if needed /// </summary> public async Task CreateAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null) { if (TableDescription.Columns.Count <= 0) { throw new MissingsColumnException(TableDescription.TableName); } if (TableDescription.PrimaryKeys.Count <= 0) { throw new MissingPrimaryKeyException(TableDescription.TableName); } var alreadyOpened = connection.State != ConnectionState.Closed; if (!alreadyOpened) { await connection.OpenAsync().ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Table)) { await this.CreateTableAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.TrackingTable)) { await this.CreateTrackingTableAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Triggers)) { await this.CreateTriggersAsync(connection, transaction).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.StoredProcedures)) { await this.CreateStoredProceduresAsync(connection, transaction).ConfigureAwait(false); } if (!alreadyOpened) { connection.Close(); } }
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); }
/// <summary> /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables /// </summary> public async Task <SyncContext> DeprovisionAsync(SyncContext context, SyncSet schema, SyncSetup setup, SyncProvision provision, string scopeInfoTableName, bool disableConstraintsOnApplyChanges, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { if (schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } this.Orchestrator.logger.LogDebug(SyncEventsId.Deprovision, new { TablesCount = schema.Tables.Count, ScopeInfoTableName = scopeInfoTableName, DisableConstraintsOnApplyChanges = disableConstraintsOnApplyChanges, }); // get Database builder var builder = this.GetDatabaseBuilder(); builder.UseChangeTracking = this.UseChangeTracking; builder.UseBulkProcedures = this.SupportBulkOperations; // Sorting tables based on dependencies between them var schemaTables = schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); // Disable check constraints if (disableConstraintsOnApplyChanges) { foreach (var table in schemaTables.Reverse()) { await this.DisableConstraintsAsync(context, table, setup, connection, transaction).ConfigureAwait(false); } } // Creating a local function to mutualize call var deprovisionFuncAsync = new Func <SyncProvision, IEnumerable <SyncTable>, Task>(async(p, tables) => { foreach (var schemaTable in tables) { var tableBuilder = this.GetTableBuilder(schemaTable, setup); // set if the builder supports creating the bulk operations proc stock tableBuilder.UseBulkProcedures = this.SupportBulkOperations; tableBuilder.UseChangeTracking = this.UseChangeTracking; // adding filter this.AddFilters(schemaTable, tableBuilder); this.Orchestrator.logger.LogDebug(SyncEventsId.Deprovision, schemaTable); await tableBuilder.DropAsync(p, connection, transaction).ConfigureAwait(false); // Interceptor await this.Orchestrator.InterceptAsync(new TableDeprovisionedArgs(context, p, schemaTable, connection, transaction), cancellationToken).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; } // Deprovision everything in order, excepting table await deprovisionFuncAsync(provision, schemaTables).ConfigureAwait(false); // then in reverse side, deprovision tables, if Table was part of Provision enumeration. if (hasDeprovisionTableFlag) { await deprovisionFuncAsync(SyncProvision.Table, schemaTables.Reverse()).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.ClientScope)) { context = await this.DropClientScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.ServerScope)) { context = await this.DropServerScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.ServerHistoryScope)) { context = await this.DropServerHistoryScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } return(context); }
/// <summary> /// Be sure all tables are ready and configured for sync /// the ScopeSet Configuration MUST be filled by the schema form Database /// </summary> public virtual async Task <SyncContext> ProvisionAsync(SyncContext context, SyncSet schema, SyncSetup setup, SyncProvision provision, string scopeInfoTableName, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { if (schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } this.Orchestrator.logger.LogDebug(SyncEventsId.Provision, new { TablesCount = schema.Tables.Count, ScopeInfoTableName = scopeInfoTableName }); // get Database builder var builder = this.GetDatabaseBuilder(); builder.UseChangeTracking = this.UseChangeTracking; builder.UseBulkProcedures = this.SupportBulkOperations; // Initialize database if needed await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false); // Shoudl we create scope if (provision.HasFlag(SyncProvision.ClientScope)) { context = await this.EnsureClientScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.ServerScope)) { context = await this.EnsureServerScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.ServerHistoryScope)) { context = await this.EnsureServerHistoryScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Sorting tables based on dependencies between them var schemaTables = schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); foreach (var schemaTable in schemaTables) { var tableBuilder = this.GetTableBuilder(schemaTable, setup); // set if the builder supports creating the bulk operations proc stock tableBuilder.UseBulkProcedures = this.SupportBulkOperations; tableBuilder.UseChangeTracking = this.UseChangeTracking; // adding filter this.AddFilters(schemaTable, tableBuilder); this.Orchestrator.logger.LogDebug(SyncEventsId.Provision, schemaTable); // Interceptor await this.Orchestrator.InterceptAsync(new TableProvisioningArgs(context, provision, tableBuilder, connection, transaction), cancellationToken).ConfigureAwait(false); await tableBuilder.CreateAsync(provision, connection, transaction).ConfigureAwait(false); await tableBuilder.CreateForeignKeysAsync(connection, transaction).ConfigureAwait(false); // Interceptor await this.Orchestrator.InterceptAsync(new TableProvisionedArgs(context, provision, schemaTable, connection, transaction), cancellationToken).ConfigureAwait(false); } return(context); }
/// <summary> /// Deprovision a database /// </summary> public async Task ProvisionAsync(string[] tables, SyncProvision provision) { DbConnection connection = null; try { if (tables == null || tables.Length == 0) { throw new ArgumentNullException("tables", "You must set the tables you want to provision"); } // Load the configuration var configuration = await this.ReadConfigurationAsync(tables); // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync(); using (var transaction = connection.BeginTransaction()) { if (provision.HasFlag(SyncProvision.Scope) || provision == SyncProvision.All) { var scopeBuilder = GetScopeBuilder().CreateScopeInfoBuilder(connection, transaction); scopeBuilder.CreateScopeInfoTable(); } for (int i = 0; i < configuration.Count; i++) { // Get the table var dmTable = configuration[i]; // get the builder var builder = GetDatabaseBuilder(dmTable); // adding filters this.AddFilters(configuration, dmTable, builder); if (provision.HasFlag(SyncProvision.Table) || provision == SyncProvision.All) { builder.CreateTable(connection, transaction); } if (provision.HasFlag(SyncProvision.TrackingTable) || provision == SyncProvision.All) { builder.CreateTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision == SyncProvision.All) { builder.CreateTriggers(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision == SyncProvision.All) { builder.CreateStoredProcedures(connection, transaction); } } transaction.Commit(); } connection.Close(); } } catch (Exception ex) { throw new SyncException(ex, SyncStage.DatabaseApplying, this.ProviderTypeName); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } } }
/// <summary> /// Deprovision a database /// </summary> public async Task ProvisionAsync(SyncConfiguration configuration, SyncProvision provision) { DbConnection connection = null; try { if (configuration.Schema == null || !configuration.Schema.HasTables) { throw new ArgumentNullException("tables", "You must set the tables you want to provision"); } // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync(); using (var transaction = connection.BeginTransaction()) { // Load the configuration await this.ReadSchemaAsync(configuration.Schema, connection, transaction); var beforeArgs = new DatabaseProvisioningArgs(null, provision, configuration.Schema, connection, transaction); // Launch any interceptor if available await this.InterceptAsync(beforeArgs); if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All)) { var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction); if (scopeBuilder.NeedToCreateScopeInfoTable()) { scopeBuilder.CreateScopeInfoTable(); } } // Sorting tables based on dependencies between them var dmTables = configuration.Schema.Tables .SortByDependencies(tab => tab.ChildRelations .Select(r => r.ChildTable)); foreach (var dmTable in dmTables) { // get the builder var builder = this.GetDatabaseBuilder(dmTable); // call any interceptor await this.InterceptAsync(new TableProvisioningArgs(null, provision, dmTable, connection, transaction)); builder.UseBulkProcedures = this.SupportBulkOperations; // adding filters this.AddFilters(configuration.Filters, dmTable, builder); // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous... if (provision.HasFlag(SyncProvision.Table)) { builder.CreateTable(connection, transaction); } if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All)) { builder.CreateTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All)) { builder.CreateTriggers(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All)) { builder.CreateStoredProcedures(connection, transaction); } // call any interceptor await this.InterceptAsync(new TableProvisionedArgs(null, provision, dmTable, connection, transaction)); } // call any interceptor await this.InterceptAsync(new DatabaseProvisionedArgs(null, provision, configuration.Schema, null, connection, transaction)); transaction.Commit(); } connection.Close(); } } catch (Exception ex) { throw new SyncException(ex, SyncStage.DatabaseApplying); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } } }
/// <summary> /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables /// </summary> public async Task DeprovisionAsync(SyncConfiguration configuration, SyncProvision provision) { DbConnection connection = null; DbTransaction transaction = null; try { if (configuration.Schema == null || !configuration.Schema.HasTables) { throw new ArgumentNullException("tables", "You must set the tables you want to provision"); } // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync(); await this.InterceptAsync(new ConnectionOpenArgs(null, connection)); using (transaction = connection.BeginTransaction()) { await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction)); // Load the configuration this.ReadSchema(configuration.Schema, connection, transaction); // Launch any interceptor if available await this.InterceptAsync(new DatabaseDeprovisioningArgs(null, provision, configuration.Schema, connection, transaction)); for (var i = configuration.Count - 1; i >= 0; i--) { // Get the table var dmTable = configuration.Schema.Tables[i]; // call any interceptor await this.InterceptAsync(new TableDeprovisioningArgs(null, provision, dmTable, connection, transaction)); // get the builder var builder = this.GetDatabaseBuilder(dmTable); builder.UseBulkProcedures = this.SupportBulkOperations; // adding filters this.AddFilters(configuration.Filters, dmTable, builder); if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All)) { builder.DropTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All)) { builder.DropProcedures(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All)) { builder.DropTriggers(connection, transaction); } // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous... if (provision.HasFlag(SyncProvision.Table)) { builder.DropTable(connection, transaction); } // call any interceptor await this.InterceptAsync(new TableDeprovisionedArgs(null, provision, dmTable, connection, transaction)); } if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All)) { var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction); if (!scopeBuilder.NeedToCreateScopeInfoTable()) { scopeBuilder.DropScopeInfoTable(); } } // Launch any interceptor if available await this.InterceptAsync(new DatabaseDeprovisionedArgs(null, provision, configuration.Schema, null, connection, transaction)); await this.InterceptAsync(new TransactionCommitArgs(null, connection, transaction)); transaction.Commit(); } } } catch (Exception ex) { throw new SyncException(ex, SyncStage.SchemaApplying); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction)); } }
/// <summary> /// Deprovision a database /// </summary> public async Task ProvisionAsync(SyncSet schema, SyncProvision provision, string scopeInfoTableName = SyncOptions.DefaultScopeInfoTableName) { DbConnection connection = null; DbTransaction transaction = null; try { if (schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync().ConfigureAwait(false); // Let provider knows a connection is opened this.OnConnectionOpened(connection); await this.InterceptAsync(new ConnectionOpenArgs(null, connection)).ConfigureAwait(false); using (transaction = connection.BeginTransaction()) { await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction)).ConfigureAwait(false); var beforeArgs = new DatabaseProvisioningArgs(null, provision, schema, connection, transaction); // Launch any interceptor if available await this.InterceptAsync(beforeArgs).ConfigureAwait(false); // get Database builder var builder = this.GetDatabaseBuilder(); builder.UseChangeTracking = this.UseChangeTracking; builder.UseBulkProcedures = this.SupportBulkOperations; // Initialize database if needed builder.EnsureDatabase(connection, transaction); if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All)) { var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(scopeInfoTableName, connection, transaction); if (scopeBuilder.NeedToCreateScopeInfoTable()) { scopeBuilder.CreateScopeInfoTable(); } } // Sorting tables based on dependencies between them var schemaTables = schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); foreach (var schemaTable in schemaTables) { // get the builder var tableBuilder = this.GetTableBuilder(schemaTable); // call any interceptor await this.InterceptAsync(new TableProvisioningArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false); tableBuilder.UseBulkProcedures = this.SupportBulkOperations; tableBuilder.UseChangeTracking = this.UseChangeTracking; // adding filters this.AddFilters(schemaTable, tableBuilder); // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous... if (provision.HasFlag(SyncProvision.Table)) { tableBuilder.CreateTable(connection, transaction); } if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All)) { tableBuilder.CreateTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All)) { tableBuilder.CreateTriggers(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All)) { tableBuilder.CreateStoredProcedures(connection, transaction); } // call any interceptor await this.InterceptAsync(new TableProvisionedArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false); } // call any interceptor await this.InterceptAsync(new DatabaseProvisionedArgs(null, provision, schema, null, connection, transaction)).ConfigureAwait(false); await this.InterceptAsync(new TransactionCommitArgs(null, connection, transaction)).ConfigureAwait(false); transaction.Commit(); } connection.Close(); } } catch (Exception ex) { throw new SyncException(ex, SyncStage.SchemaApplying); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction)).ConfigureAwait(false); // Let provider knows a connection is closed this.OnConnectionClosed(connection); } }
/// <summary> /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables /// </summary> public async Task DeprovisionAsync(SyncSet schema, SyncProvision provision, string scopeInfoTableName = SyncOptions.DefaultScopeInfoTableName) { DbConnection connection = null; DbTransaction transaction = null; try { if (schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync().ConfigureAwait(false); // Let provider knows a connection is opened this.OnConnectionOpened(connection); await this.InterceptAsync(new ConnectionOpenArgs(null, connection)).ConfigureAwait(false); using (transaction = connection.BeginTransaction()) { await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction)).ConfigureAwait(false); // Launch any interceptor if available await this.InterceptAsync(new DatabaseDeprovisioningArgs(null, provision, schema, connection, transaction)).ConfigureAwait(false); for (var i = schema.Tables.Count - 1; i >= 0; i--) { // Get the table var schemaTable = schema.Tables[i]; // call any interceptor await this.InterceptAsync(new TableDeprovisioningArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false); // get the builder var builder = this.GetTableBuilder(schemaTable); builder.UseBulkProcedures = this.SupportBulkOperations; builder.UseChangeTracking = this.UseChangeTracking; // adding filters this.AddFilters(schemaTable, builder); if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All)) { builder.DropTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All)) { builder.DropProcedures(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All)) { builder.DropTriggers(connection, transaction); } // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous... if (provision.HasFlag(SyncProvision.Table)) { builder.DropTable(connection, transaction); } // call any interceptor await this.InterceptAsync(new TableDeprovisionedArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All)) { var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(scopeInfoTableName, connection, transaction); if (!scopeBuilder.NeedToCreateScopeInfoTable()) { scopeBuilder.DropScopeInfoTable(); } } // Launch any interceptor if available await this.InterceptAsync(new DatabaseDeprovisionedArgs(null, provision, schema, null, connection, transaction)).ConfigureAwait(false); await this.InterceptAsync(new TransactionCommitArgs(null, connection, transaction)).ConfigureAwait(false); transaction.Commit(); } } } catch (Exception ex) { throw new SyncException(ex, SyncStage.SchemaApplying); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction)).ConfigureAwait(false); // Let provider knows a connection is closed this.OnConnectionClosed(connection); } }
internal async Task <SyncSet> InternalProvisionAsync(SyncContext ctx, bool overwrite, SyncSet schema, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { // If schema does not have any table, raise an exception if (schema == null || schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } await this.InterceptAsync(new ProvisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false); // get Database builder var builder = this.Provider.GetDatabaseBuilder(); // Initialize database if needed await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false); // Check if we have tables AND columns // If we don't have any columns it's most probably because user called method with the Setup only // So far we have only tables names, it's enough to get the schema if (schema.HasTables && !schema.HasColumns) { schema = await this.InternalGetSchemaAsync(ctx, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Get Scope Builder var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); // Shoudl we create scope if (provision.HasFlag(SyncProvision.ClientScope)) { var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await this.InternalCreateScopeInfoTableAsync(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.InternalCreateScopeInfoTableAsync(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.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Sorting tables based on dependencies between them var schemaTables = schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); foreach (var schemaTable in schemaTables) { var tableBuilder = this.GetTableBuilder(schemaTable, this.Setup); // Check if we need to create a schema there var schemaExists = await InternalExistsSchemaAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!schemaExists) { await InternalCreateSchemaAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Table)) { var tableExistst = await this.InternalExistsTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!tableExistst) { await this.InternalCreateTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.TrackingTable)) { var trackingTableExistst = await this.InternalExistsTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!trackingTableExistst) { await this.InternalCreateTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.Triggers)) { await this.InternalCreateTriggersAsync(ctx, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.StoredProcedures)) { await this.InternalCreateStoredProceduresAsync(ctx, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } var args = new ProvisionedArgs(ctx, provision, schema, connection); await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false); this.ReportProgress(ctx, progress, args); return(schema); }
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); }
/// <summary> /// Provision the orchestrator database based on the schema argument, and the provision enumeration /// </summary> /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param> /// <param name="provision">Provision enumeration to determine which components to apply</param> /// <returns>Full schema with table and columns properties</returns> public virtual async Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null) { if (!this.StartTime.HasValue) { this.StartTime = DateTime.UtcNow; } // Get context or create a new one var ctx = this.GetContext(); using (var connection = this.Provider.CreateConnection()) { // log this.logger.LogInformation(SyncEventsId.Provision, new { connection.Database, Provision = provision }); try { ctx.SyncStage = SyncStage.Provisioning; // If schema does not have any table, just return if (schema == null || schema.Tables == null || !schema.HasTables) { throw new MissingTablesException(); } if (this is LocalOrchestrator && (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope))) { throw new InvalidProvisionForLocalOrchestratorException(); } else if (!(this is LocalOrchestrator) && provision.HasFlag(SyncProvision.ClientScope)) { throw new InvalidProvisionForRemoteOrchestratorException(); } // Open connection await this.OpenConnectionAsync(connection, cancellationToken).ConfigureAwait(false); // Create a transaction using (var transaction = connection.BeginTransaction()) { await this.InterceptAsync(new TransactionOpenedArgs(ctx, connection, transaction), cancellationToken).ConfigureAwait(false); // Check if we have tables AND columns // If we don't have any columns it's most probably because user called method with the Setup only // So far we have only tables names, it's enough to get the schema if (schema.HasTables && !schema.HasColumns) { this.logger.LogInformation(SyncEventsId.GetSchema, this.Setup); (ctx, schema) = await this.Provider.GetSchemaAsync(ctx, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (!schema.HasTables) { throw new MissingTablesException(); } if (!schema.HasColumns) { throw new MissingColumnsException(); } await this.InterceptAsync(new DatabaseProvisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false); await this.Provider.ProvisionAsync(ctx, schema, this.Setup, provision, this.Options.ScopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false); await this.InterceptAsync(new TransactionCommitArgs(ctx, connection, transaction), cancellationToken).ConfigureAwait(false); transaction.Commit(); } ctx.SyncStage = SyncStage.Provisioned; await this.CloseConnectionAsync(connection, cancellationToken).ConfigureAwait(false); var args = new DatabaseProvisionedArgs(ctx, provision, schema, connection); await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false); this.ReportProgress(ctx, progress, args); } catch (Exception ex) { RaiseError(ex); } finally { if (connection != null && connection.State == ConnectionState.Open) { connection.Close(); } } return(schema); } }
internal async Task <(SyncContext context, bool provisioned)> InternalProvisionAsync(IScopeInfo scopeInfo, SyncContext context, bool overwrite, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { if (Provider == null) { throw new MissingProviderException(nameof(InternalProvisionAsync)); } context.SyncStage = SyncStage.Provisioning; // If schema does not have any table, raise an exception if (scopeInfo.Schema == null || scopeInfo.Schema.Tables == null || !scopeInfo.Schema.HasTables) { throw new MissingTablesException(scopeInfo.Name); } await this.InterceptAsync(new ProvisioningArgs(context, provision, scopeInfo.Schema, connection, transaction), progress, cancellationToken).ConfigureAwait(false); // get Database builder var builder = this.Provider.GetDatabaseBuilder(); // Initialize database if needed await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false); // Check if we have tables AND columns // If we don't have any columns it's most probably because user called method with the Setup only // So far we have only tables names, it's enough to get the schema if (scopeInfo.Schema.HasTables && !scopeInfo.Schema.HasColumns) { (context, scopeInfo.Schema) = await this.InternalGetSchemaAsync(context, scopeInfo.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Shoudl we create scope if (provision.HasFlag(SyncProvision.ClientScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Client, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Client, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // if scope is not null, so obviously we have create the table before, so no need to test if (provision.HasFlag(SyncProvision.ServerScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Server, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.ServerHistoryScope)) { bool exists; (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.ServerHistory, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.ServerHistory, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Sorting tables based on dependencies between them var schemaTables = scopeInfo.Schema.Tables .SortByDependencies(tab => tab.GetRelations() .Select(r => r.GetParentTable())); foreach (var schemaTable in schemaTables) { var tableBuilder = this.GetTableBuilder(schemaTable, scopeInfo); // Check if we need to create a schema there bool schemaExists; (context, schemaExists) = await InternalExistsSchemaAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!schemaExists) { (context, _) = await InternalCreateSchemaAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.Table)) { bool tableExists; (context, tableExists) = await this.InternalExistsTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!tableExists) { (context, _) = await this.InternalCreateTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.TrackingTable)) { bool trackingTableExist; (context, trackingTableExist) = await this.InternalExistsTrackingTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!trackingTableExist) { (context, _) = await this.InternalCreateTrackingTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } if (provision.HasFlag(SyncProvision.Triggers)) { await this.InternalCreateTriggersAsync(scopeInfo, context, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } if (provision.HasFlag(SyncProvision.StoredProcedures)) { (context, _) = await this.InternalCreateStoredProceduresAsync(scopeInfo, context, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } await this.InterceptAsync(new ProvisionedArgs(context, provision, scopeInfo.Schema, connection, transaction), progress, cancellationToken).ConfigureAwait(false); return(context, true); }
/// <summary> /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables /// </summary> public async Task DeprovisionAsync(SyncConfiguration configuration, SyncProvision provision) { DbConnection connection = null; try { if (configuration.Schema == null || !configuration.Schema.HasTables) { throw new ArgumentNullException("tables", "You must set the tables you want to provision"); } // Load the configuration await this.ReadSchemaAsync(configuration.Schema); // Open the connection using (connection = this.CreateConnection()) { await connection.OpenAsync(); using (var transaction = connection.BeginTransaction()) { for (int i = configuration.Count - 1; i >= 0; i--) { // Get the table var dmTable = configuration.Schema.Tables[i]; // get the builder var builder = GetDatabaseBuilder(dmTable); // adding filters this.AddFilters(configuration.Filters, dmTable, builder); if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All)) { builder.DropTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All)) { builder.DropProcedures(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All)) { builder.DropTriggers(connection, transaction); } // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous... if (provision.HasFlag(SyncProvision.Table)) { builder.DropTable(connection, transaction); } } if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All)) { var scopeBuilder = GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction); if (!scopeBuilder.NeedToCreateScopeInfoTable()) { scopeBuilder.DropScopeInfoTable(); } } transaction.Commit(); } } } catch (Exception ex) { throw new SyncException(ex, SyncStage.DatabaseApplying, this.ProviderTypeName); } finally { if (connection != null && connection.State != ConnectionState.Closed) { connection.Close(); } } }
/// <summary> /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables /// </summary> public async Task DeprovisionAsync(string[] tables, SyncProvision provision) { if (tables == null || tables.Length == 0) { throw new SyncException("You must set the tables you want to modify"); } // Load the configuration var configuration = await this.ReadConfigurationAsync(tables); // Open the connection using (var connection = this.CreateConnection()) { try { await connection.OpenAsync(); using (var transaction = connection.BeginTransaction()) { for (int i = configuration.Count - 1; i >= 0; i--) { // Get the table var dmTable = configuration[i]; // get the builder var builder = GetDatabaseBuilder(dmTable); // adding filters this.AddFilters(configuration, dmTable, builder); if (provision.HasFlag(SyncProvision.TrackingTable) || provision == SyncProvision.All) { builder.DropTrackingTable(connection, transaction); } if (provision.HasFlag(SyncProvision.StoredProcedures) || provision == SyncProvision.All) { builder.DropProcedures(connection, transaction); } if (provision.HasFlag(SyncProvision.Triggers) || provision == SyncProvision.All) { builder.DropTriggers(connection, transaction); } if (provision.HasFlag(SyncProvision.Table) || provision == SyncProvision.All) { builder.DropTable(connection, transaction); } } transaction.Commit(); } } catch (Exception ex) { throw SyncException.CreateUnknowException(SyncStage.BeginSession, ex); } finally { if (connection.State != ConnectionState.Closed) { connection.Close(); } } } }