/// <summary> /// Migrate from a setup to another setup /// </summary> public virtual async Task <SyncContext> MigrationAsync(SyncContext context, SyncSet schema, SyncSetup oldSetup, SyncSetup newSetup, bool includeTable, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { // Create a new migration var migration = new Migration(newSetup, oldSetup); // get comparision results var migrationResults = migration.Compare(); // get Database builder var builder = this.GetDatabaseBuilder(); builder.UseChangeTracking = this.UseChangeTracking; builder.UseBulkProcedures = this.SupportBulkOperations; // Deprovision // Generate a fake SyncSet since we don't need complete table schema foreach (var migrationTable in migrationResults.Tables) { this.Orchestrator.logger.LogDebug(SyncEventsId.Migration, migrationTable); var tableBuilder = this.GetTableBuilder(new SyncTable(migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName), oldSetup); // set if the builder supports creating the bulk operations proc stock tableBuilder.UseBulkProcedures = this.SupportBulkOperations; tableBuilder.UseChangeTracking = this.UseChangeTracking; // Deprovision if (migrationTable.StoredProcedures == MigrationAction.Drop || migrationTable.StoredProcedures == MigrationAction.CreateOrRecreate) { await tableBuilder.DropStoredProceduresAsync(connection, transaction); } if (migrationTable.Triggers == MigrationAction.Drop || migrationTable.Triggers == MigrationAction.CreateOrRecreate) { await tableBuilder.DropTriggersAsync(connection, transaction); } if (migrationTable.TrackingTable == MigrationAction.Drop || migrationTable.TrackingTable == MigrationAction.CreateOrRecreate) { await tableBuilder.DropTrackingTableAsync(connection, transaction); } if (includeTable && (migrationTable.Table == MigrationAction.Drop || migrationTable.Table == MigrationAction.CreateOrRecreate)) { await tableBuilder.DropTableAsync(connection, transaction); } } // Provision // need the real SyncSet since we need columns definition foreach (var migrationTable in migrationResults.Tables) { var syncTable = schema.Tables[migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName]; // a table that we drop of the setup if (syncTable == null) { continue; } var tableBuilder = this.GetTableBuilder(syncTable, newSetup); // set if the builder supports creating the bulk operations proc stock tableBuilder.UseBulkProcedures = this.SupportBulkOperations; tableBuilder.UseChangeTracking = this.UseChangeTracking; // Re provision table if (migrationTable.Table == MigrationAction.CreateOrRecreate && includeTable) { await tableBuilder.DropTableAsync(connection, transaction); await tableBuilder.CreateTableAsync(connection, transaction); } // Re provision tracking table if (migrationTable.TrackingTable == MigrationAction.Rename) { var oldTable = oldSetup.Tables[migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName]; var oldTableBuilder = this.GetTableBuilder(new SyncTable(oldTable.TableName, oldTable.SchemaName), oldSetup); var oldTableName = oldTableBuilder.TrackingTableName; if (oldTable != null) { await tableBuilder.RenameTrackingTableAsync(oldTableName, connection, transaction); } } else if (migrationTable.TrackingTable == MigrationAction.CreateOrRecreate) { await tableBuilder.DropTrackingTableAsync(connection, transaction); await tableBuilder.CreateTrackingTableAsync(connection, transaction); } // Re provision stored procedures if (migrationTable.StoredProcedures == MigrationAction.CreateOrRecreate) { await tableBuilder.DropStoredProceduresAsync(connection, transaction); await tableBuilder.CreateStoredProceduresAsync(connection, transaction); } // Re provision triggers if (migrationTable.Triggers == MigrationAction.CreateOrRecreate) { await tableBuilder.DropTriggersAsync(connection, transaction); await tableBuilder.CreateTriggersAsync(connection, transaction); } } return(context); }
/// <summary> /// Migrate from a setup to another setup /// </summary> internal async Task <SyncContext> InternalMigrationAsync(SyncContext context, SyncSet schema, SyncSetup oldSetup, SyncSetup newSetup, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress) { // Create a new migration var migration = new Migration(newSetup, oldSetup); // get comparision results var migrationResults = migration.Compare(); // Launch InterceptAsync on Migrating await this.InterceptAsync(new MigratingArgs(context, schema, oldSetup, newSetup, migrationResults, connection, transaction), cancellationToken).ConfigureAwait(false); // Deprovision triggers stored procedures and tracking table if required foreach (var migrationTable in migrationResults.Tables) { // using a fake SyncTable based on oldSetup, since we don't need columns, but we need to have the filters var schemaTable = new SyncTable(migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName); // Create a temporary SyncSet for attaching to the schemaTable var tmpSchema = new SyncSet(); // Add this table to schema tmpSchema.Tables.Add(schemaTable); tmpSchema.EnsureSchema(); // copy filters from old setup foreach (var filter in oldSetup.Filters) { tmpSchema.Filters.Add(filter); } // using a fake Synctable, since we don't need columns to deprovision var tableBuilder = this.GetTableBuilder(schemaTable, oldSetup); // Deprovision stored procedures if (migrationTable.StoredProcedures == MigrationAction.Drop || migrationTable.StoredProcedures == MigrationAction.Create) { await InternalDropStoredProceduresAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Deprovision triggers if (migrationTable.Triggers == MigrationAction.Drop || migrationTable.Triggers == MigrationAction.Create) { await InternalDropTriggersAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Deprovision tracking table if (migrationTable.TrackingTable == MigrationAction.Drop || migrationTable.TrackingTable == MigrationAction.Create) { var exists = await InternalExistsTrackingTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await InternalDropTrackingTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Removing cached commands var syncAdapter = this.GetSyncAdapter(schemaTable, oldSetup); syncAdapter.RemoveCommands(); } // Provision table (create or alter), tracking tables, stored procedures and triggers // Need the real SyncSet since we need columns definition foreach (var migrationTable in migrationResults.Tables) { var syncTable = schema.Tables[migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName]; var oldTable = oldSetup.Tables[migrationTable.SetupTable.TableName, migrationTable.SetupTable.SchemaName]; if (syncTable == null) { continue; } var tableBuilder = this.GetTableBuilder(syncTable, newSetup); // Re provision table if (migrationTable.Table == MigrationAction.Create) { // Check if we need to create a schema there var schemaExists = await InternalExistsSchemaAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!schemaExists) { await InternalCreateSchemaAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } var exists = await InternalExistsTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await InternalCreateTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // Re provision table if (migrationTable.Table == MigrationAction.Alter) { var exists = await InternalExistsTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!exists) { await InternalCreateTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } else if (oldTable != null) { //get new columns to add var newColumns = syncTable.Columns.Where(c => !oldTable.Columns.Any(oldC => string.Equals(oldC, c.ColumnName, SyncGlobalization.DataSourceStringComparison))); if (newColumns != null) { foreach (var newColumn in newColumns) { var columnExist = await InternalExistsColumnAsync(context, newColumn.ColumnName, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (!columnExist) { await InternalAddColumnAsync(context, newColumn.ColumnName, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } } } } // Re provision tracking table if (migrationTable.TrackingTable == MigrationAction.Rename && oldTable != null) { var(_, oldTableName) = this.Provider.GetParsers(new SyncTable(oldTable.TableName, oldTable.SchemaName), oldSetup); await InternalRenameTrackingTableAsync(context, oldTableName, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } else if (migrationTable.TrackingTable == MigrationAction.Create) { var exists = await InternalExistsTrackingTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) { await InternalDropTrackingTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } await InternalCreateTrackingTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Re provision stored procedures if (migrationTable.StoredProcedures == MigrationAction.Create) { await InternalCreateStoredProceduresAsync(context, true, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } // Re provision triggers if (migrationTable.Triggers == MigrationAction.Create) { await InternalCreateTriggersAsync(context, true, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false); } } // InterceptAsync Migrated var args = new MigratedArgs(context, schema, this.Setup, migrationResults, connection, transaction); await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false); this.ReportProgress(context, progress, args); return(context); }