/// <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);
        }