Exemple #1
0
        /// <summary>
        /// Migrate an old setup configuration to a new one. This method is usefull if you are changing your SyncSetup when a database has been already configured previously
        /// </summary>
        public virtual async Task MigrationAsync(SyncSetup oldSetup, SyncSet schema, 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())
            {
                try
                {
                    ctx.SyncStage = SyncStage.Migrating;

                    // If schema does not have any table, just return
                    if (schema == null || schema.Tables == null || !schema.HasTables)
                    {
                        throw new MissingTablesException();
                    }

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

                        // Launch InterceptAsync on Migrating
                        await this.InterceptAsync(new DatabaseMigratingArgs(ctx, schema, oldSetup, this.Setup, connection, transaction), cancellationToken).ConfigureAwait(false);

                        this.logger.LogDebug(SyncEventsId.Migration, oldSetup);
                        this.logger.LogDebug(SyncEventsId.Migration, this.Setup);

                        // Migrate the db structure
                        await this.Provider.MigrationAsync(ctx, schema, oldSetup, this.Setup, true, connection, transaction, cancellationToken, progress);

                        // Now call the ProvisionAsync() to provision new tables
                        var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;

                        // Provision new tables if needed
                        await this.Provider.ProvisionAsync(ctx, schema, this.Setup, provision, this.Options.ScopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        ScopeInfo localScope = null;

                        ctx = await this.Provider.EnsureClientScopeAsync(ctx, this.Options.ScopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        (ctx, localScope) = await this.Provider.GetClientScopeAsync(ctx, this.Options.ScopeInfoTableName, this.ScopeName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        localScope.Setup  = this.Setup;
                        localScope.Schema = schema;

                        // Write scopes locally
                        ctx = await this.Provider.WriteClientScopeAsync(ctx, this.Options.ScopeInfoTableName, localScope, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        await this.InterceptAsync(new TransactionCommitArgs(ctx, connection, transaction), cancellationToken).ConfigureAwait(false);

                        transaction.Commit();
                    }

                    ctx.SyncStage = SyncStage.Migrated;

                    await this.CloseConnectionAsync(connection, cancellationToken).ConfigureAwait(false);

                    // InterceptAsync Migrated
                    var args = new DatabaseMigratedArgs(ctx, schema, this.Setup);
                    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();
                    }
                }
            }
        }
Exemple #2
0
        private async Task <Version> UpgdrateTo602Async(SyncContext context, SyncSet schema, SyncSetup setup, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var newVersion = new Version(0, 6, 2);

            var message = $"Upgrade to {newVersion}:";
            var args    = new UpgradeProgressArgs(context, message, newVersion, connection, transaction);

            this.ReportProgress(context, progress, args, connection, transaction);

            // Update the "Update trigger" for all tables

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

                // Upgrade Select Initial Changes
                var exists = await InternalExistsTriggerAsync(context, tableBuilder, DbTriggerType.Update, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await InternalDropTriggerAsync(context, tableBuilder, DbTriggerType.Update, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
                await InternalCreateTriggerAsync(context, tableBuilder, DbTriggerType.Update, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                args = new UpgradeProgressArgs(context, $"Update Trigger for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                this.ReportProgress(context, progress, args, connection, transaction);
            }

            message = $"Upgrade to {newVersion} done.";
            args    = new UpgradeProgressArgs(context, message, newVersion, connection, transaction);
            this.ReportProgress(context, progress, args, connection, transaction);



            return(newVersion);
        }
Exemple #3
0
        /// <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, oldSetup, 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, newSetup, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }

                    var exists = await InternalExistsTableAsync(context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (!exists)
                    {
                        await InternalCreateTableAsync(context, newSetup, 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, newSetup, 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, newSetup, 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, newSetup, 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, newSetup, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }

                    await InternalCreateTrackingTableAsync(context, newSetup, 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, newSetup, migrationResults, connection, transaction);

            await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false);

            this.ReportProgress(context, progress, args);

            return(context);
        }
Exemple #4
0
 /// <summary>
 /// Create a Sync Adapter
 /// </summary>
 public SyncAdapter(SyncTable tableDescription, SyncSetup setup)
 {
     this.TableDescription = tableDescription;
     this.Setup            = setup;
 }
Exemple #5
0
        internal virtual async Task <ScopeInfo> InternalUpgradeAsync(SyncContext context, SyncSet schema, SyncSetup setup, ScopeInfo scopeInfo, DbScopeBuilder builder, DbConnection connection, DbTransaction transaction,
                                                                     CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var version    = SyncVersion.EnsureVersion(scopeInfo.Version);
            var oldVersion = version.Clone() as Version;

            // beta version
            if (version.Major == 0)
            {
                if (version.Minor <= 5)
                {
                    version = await AutoUpgdrateToNewVersionAsync(context, new Version(0, 6, 0), connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 6 && version.Build == 0)
                {
                    version = await UpgdrateTo601Async(context, schema, setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 6 && version.Build == 1)
                {
                    version = await UpgdrateTo602Async(context, schema, setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 6 && version.Build >= 2)
                {
                    version = await UpgdrateTo700Async(context, schema, setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 7 && version.Build == 0)
                {
                    version = await AutoUpgdrateToNewVersionAsync(context, new Version(0, 7, 1), connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 7 && version.Build == 1)
                {
                    version = await AutoUpgdrateToNewVersionAsync(context, new Version(0, 7, 2), connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 7 && version.Build == 2)
                {
                    version = await AutoUpgdrateToNewVersionAsync(context, new Version(0, 7, 3), connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (version.Minor == 7 && version.Build >= 3)
                {
                    version = await AutoUpgdrateToNewVersionAsync(context, new Version(0, 8, 0), connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (oldVersion != version)
            {
                scopeInfo.Version = version.ToString();
                scopeInfo         = await this.InternalSaveScopeAsync(context, DbScopeType.Client, scopeInfo, builder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }
            return(scopeInfo);
        }
Exemple #6
0
 public MigratedArgs(SyncContext context, SyncSet schema, SyncSetup setup, MigrationResults migration, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction)
 {
     this.Schema    = schema;
     this.Setup     = setup;
     this.Migration = migration;
 }
Exemple #7
0
        /// <summary>
        /// Check
        /// </summary>
        public virtual async Task <(SyncContext, bool, ClientScopeInfo, ServerScopeInfo)> IsConflictingSetupAsync(SyncContext context, SyncSetup inputSetup, ClientScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            if (clientScopeInfo.IsNewScope || clientScopeInfo.Schema == null)
            {
                return(context, false, clientScopeInfo, serverScopeInfo);
            }

            if (inputSetup != null && clientScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(inputSetup))
            {
                var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo);
                await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false);

                if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback)
                {
                    throw new Exception("Seems you are trying another Setup tables that what is stored in your client scope database. Please create a new scope or deprovision and provision again your client scope.");
                }
                if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort)
                {
                    return(context, true, clientScopeInfo, serverScopeInfo);
                }

                // re affect scope infos
                clientScopeInfo = conflictingSetupArgs.ClientScopeInfo;
                serverScopeInfo = conflictingSetupArgs.ServerScopeInfo;
            }

            if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup))
            {
                var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo);
                await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false);

                if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback)
                {
                    throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope.");
                }
                if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort)
                {
                    return(context, true, clientScopeInfo, serverScopeInfo);
                }

                // re affect scope infos
                clientScopeInfo = conflictingSetupArgs.ClientScopeInfo;
                serverScopeInfo = conflictingSetupArgs.ServerScopeInfo;
            }

            // We gave 2 chances to user to edit the setup and fill correct values.
            // Final check, but if not valid, raise an error
            if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup))
            {
                throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope.");
            }

            return(context, false, clientScopeInfo, serverScopeInfo);
        }
Exemple #8
0
        /// <summary>
        /// update configuration object with tables desc from server database
        /// </summary>
        public SyncSet ReadSchema(SyncSetup setup, DbConnection connection, DbTransaction transaction)
        {
            if (setup == null || setup.Tables.Count <= 0)
            {
                throw new MissingTablesException();
            }

            // Create the schema
            var schema = new SyncSet(setup.ScopeName)
            {
                StoredProceduresPrefix = setup.StoredProceduresPrefix,
                StoredProceduresSuffix = setup.StoredProceduresSuffix,
                TrackingTablesPrefix   = setup.TrackingTablesPrefix,
                TrackingTablesSuffix   = setup.TrackingTablesSuffix,
                TriggersPrefix         = setup.TriggersPrefix,
                TriggersSuffix         = setup.TriggersSuffix,
            };

            // copy filters from setup
            foreach (var filter in setup.Filters)
            {
                schema.Filters.Add(filter);
            }

            var relations = new List <DbRelationDefinition>(20);

            foreach (var setupTable in setup.Tables)
            {
                var builderTable = this.GetTableManagerFactory(setupTable.TableName, setupTable.SchemaName);
                var tblManager   = builderTable.CreateManagerTable(connection, transaction);

                // Check if table exists
                var syncTable = tblManager.GetTable();

                if (syncTable == null)
                {
                    throw new MissingTableException(string.IsNullOrEmpty(setupTable.SchemaName) ? setupTable.TableName : setupTable.SchemaName + "." + setupTable.TableName);
                }

                // get columns list
                var lstColumns = tblManager.GetColumns();

                // Validate the column list and get the dmTable configuration object.
                this.FillSyncTableWithColumns(setupTable, syncTable, lstColumns, tblManager);

                // Add this table to schema
                schema.Tables.Add(syncTable);

                // Check primary Keys
                SetPrimaryKeys(syncTable, tblManager);

                // get all relations
                var tableRelations = tblManager.GetRelations();

                // Since we are not sure of the order of reading tables
                // create a tmp relations list
                relations.AddRange(tableRelations);
            }

            // Parse and affect relations to schema
            SetRelations(relations, schema);

            return(schema);
        }
Exemple #9
0
        /// <summary>
        /// Ensure configuration is correct on both server and client side
        /// </summary>
        public virtual async Task <(SyncContext, SyncSet)> EnsureSchemaAsync(SyncContext context, SyncSetup setup,
                                                                             DbConnection connection, DbTransaction transaction,
                                                                             CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
        {
            context.SyncStage = SyncStage.SchemaReading;

            var schema = this.ReadSchema(setup, connection, transaction);

            // Progress & Interceptor
            context.SyncStage = SyncStage.SchemaRead;
            var schemaArgs = new SchemaArgs(context, schema, connection, transaction);

            this.ReportProgress(context, progress, schemaArgs);
            await this.InterceptAsync(schemaArgs).ConfigureAwait(false);

            return(context, schema);
        }
        /// <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);
        }
Exemple #11
0
        /// <summary>
        /// update configuration object with tables desc from server database
        /// </summary>
        public async Task <(SyncContext, SyncSet)> GetSchemaAsync(SyncContext context, SyncSetup setup, DbConnection connection, DbTransaction transaction,
                                                                  CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (setup == null || setup.Tables.Count <= 0)
            {
                throw new MissingTablesException();
            }

            // Create the schema
            var schema = new SyncSet();

            // copy filters from setup
            foreach (var filter in setup.Filters)
            {
                schema.Filters.Add(filter);
            }

            var relations = new List <DbRelationDefinition>(20);

            foreach (var setupTable in setup.Tables)
            {
                this.Orchestrator.logger.LogDebug(SyncEventsId.GetSchema, setupTable);

                var builderTable = this.GetTableManagerFactory(setupTable.TableName, setupTable.SchemaName);
                var tblManager   = builderTable.CreateManagerTable(connection, transaction);

                // Check if table exists
                var syncTable = await tblManager.GetTableAsync().ConfigureAwait(false);

                if (syncTable == null)
                {
                    throw new MissingTableException(string.IsNullOrEmpty(setupTable.SchemaName) ? setupTable.TableName : setupTable.SchemaName + "." + setupTable.TableName);
                }

                // get columns list
                var lstColumns = await tblManager.GetColumnsAsync().ConfigureAwait(false);

                if (this.Orchestrator.logger.IsEnabled(LogLevel.Debug))
                {
                    foreach (var col in lstColumns)
                    {
                        this.Orchestrator.logger.LogDebug(SyncEventsId.GetSchema, col);
                    }
                }

                // Validate the column list and get the dmTable configuration object.
                this.FillSyncTableWithColumns(setupTable, syncTable, lstColumns, tblManager);

                // Add this table to schema
                schema.Tables.Add(syncTable);

                // Check primary Keys
                await SetPrimaryKeysAsync(syncTable, tblManager).ConfigureAwait(false);

                // get all relations
                var tableRelations = await tblManager.GetRelationsAsync().ConfigureAwait(false);

                // Since we are not sure of the order of reading tables
                // create a tmp relations list
                relations.AddRange(tableRelations);
            }

            // Parse and affect relations to schema
            SetRelations(relations, schema);

            // Ensure all objects have correct relations to schema
            schema.EnsureSchema();

            return(context, schema);
        }
 /// <summary>
 /// Applying changes message.
 /// Be careful policy could be differente from the schema (especially on client side, it's the reverse one, by default)
 /// </summary>
 public MessageApplyChanges(Guid localScopeId, Guid senderScopeId, bool isNew, long?lastTimestamp, SyncSet schema, SyncSetup setup,
                            ConflictResolutionPolicy policy, bool disableConstraintsOnApplyChanges,
                            bool useBulkOperations, bool cleanMetadatas, bool cleanFolder, BatchInfo changes)
 {
     this.LocalScopeId  = localScopeId;
     this.SenderScopeId = senderScopeId;
     this.IsNew         = isNew;
     this.LastTimestamp = lastTimestamp;
     this.Schema        = schema ?? throw new ArgumentNullException(nameof(schema));
     this.Setup         = setup ?? throw new ArgumentNullException(nameof(setup));
     this.Policy        = policy;
     this.DisableConstraintsOnApplyChanges = disableConstraintsOnApplyChanges;
     this.UseBulkOperations = useBulkOperations;
     this.CleanMetadatas    = cleanMetadatas;
     this.CleanFolder       = cleanFolder;
     this.Changes           = changes ?? throw new ArgumentNullException(nameof(changes));
 }
 public virtual Task <ServerScopeInfo> ProvisionAsync(SyncSetup setup, SyncProvision provision = default, bool overwrite = false, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
 => ProvisionAsync(SyncOptions.DefaultScopeName, setup, provision, overwrite, connection, transaction, cancellationToken, progress);
 public virtual Task <bool> DeprovisionAsync(SyncSetup setup, SyncProvision provision = default, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
 => DeprovisionAsync(SyncOptions.DefaultScopeName, setup, provision, connection, transaction, cancellationToken, progress);
        /// <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);
        }
Exemple #16
0
 /// <summary>
 /// Get a table builder helper. Need a complete table description (SchemaTable). Will then generate table, table tracking, stored proc and triggers
 /// </summary>
 /// <returns></returns>
 public abstract DbTableBuilder GetTableBuilder(SyncTable tableDescription, SyncSetup setup);
Exemple #17
0
 public MigratingArgs(SyncContext context, SyncSet newSchema, SyncSetup oldSetup, SyncSetup newSetup, MigrationResults migrationResults, DbConnection connection, DbTransaction transaction) : base(context, connection, transaction)
 {
     this.NewSchema        = newSchema;
     this.OldSetup         = oldSetup;
     this.NewSetup         = newSetup;
     this.MigrationResults = migrationResults;
 }
        /// <summary>
        /// Launch a synchronization with the specified mode
        /// </summary>
        public async Task <SyncResult> SynchronizeAsync(string scopeName, SyncSetup setup, SyncType syncType, SyncParameters parameters, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
        {
            // checkpoints dates
            var startTime    = DateTime.UtcNow;
            var completeTime = DateTime.UtcNow;

            // Create a logger
            var logger = this.Options.Logger ?? new SyncLogger().AddDebug();

            // Lock sync to prevent multi call to sync at the same time
            LockSync();

            // Context, used to back and forth data between servers
            var context = new SyncContext(Guid.NewGuid(), scopeName)
            {
                // if any parameters, set in context
                Parameters = parameters,
                // set sync type (Normal, Reinitialize, ReinitializeWithUpload)
                SyncType = syncType
            };


            // Result, with sync results stats.
            var result = new SyncResult(context.SessionId)
            {
                // set start time
                StartTime    = startTime,
                CompleteTime = completeTime,
            };

            this.SessionState = SyncSessionState.Synchronizing;
            this.SessionStateChanged?.Invoke(this, this.SessionState);
            //await Task.Run(async () =>
            //{
            try
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                if (setup != null)
                {
                    var remoteOrchestratorType = this.RemoteOrchestrator.GetType();
                    var providerType           = remoteOrchestratorType.Name;
                    if (providerType.ToLowerInvariant() == "webclientorchestrator" || providerType.ToLowerInvariant() == "webremotetorchestrator")
                    {
                        throw new Exception("Do not set Tables (or SyncSetup) from your client. Please use SyncAgent, without any Tables or SyncSetup. The tables will come from the server side");
                    }
                }

                // Begin session
                context = await this.LocalOrchestrator.InternalBeginSessionAsync(context, cancellationToken, progress).ConfigureAwait(false);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }


                // no need to check on every call to SynchronizeAsync
                if (!checkUpgradeDone)
                {
                    var needToUpgrade = await this.LocalOrchestrator.NeedsToUpgradeAsync(default, default, cancellationToken, progress).ConfigureAwait(false);
Exemple #19
0
 public Migration(SyncSetup newSetup, SyncSetup oldSetup)
 {
     this.newSetup = newSetup;
     this.oldSetup = oldSetup;
 }
        InternalGetServerScopeInfoAsync(SyncContext context, SyncSetup setup, bool overwrite, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.ScopeLoading, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                if (!exists)
                {
                    await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);
                }

                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                if (!exists)
                {
                    await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);
                }

                ServerScopeInfo serverScopeInfo;
                (context, serverScopeInfo) = await this.InternalLoadServerScopeInfoAsync(context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                if (serverScopeInfo == null)
                {
                    serverScopeInfo = this.InternalCreateScopeInfo(context.ScopeName, DbScopeType.Server) as ServerScopeInfo;

                    (context, serverScopeInfo) = await this.InternalSaveServerScopeInfoAsync(serverScopeInfo, context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);
                }

                // Raise error only on server side, since we can't do nothing if we don't have any tables provisionned and no setup provided
                if ((serverScopeInfo.Setup == null || serverScopeInfo.Schema == null) && (setup == null || setup.Tables.Count <= 0))
                {
                    throw new MissingServerScopeTablesException(context.ScopeName);
                }

                // if serverscopeinfo is a new, because we never run any sync before, grab schema and affect setup
                if (setup != null && setup.Tables.Count > 0)
                {
                    if ((serverScopeInfo.Setup == null && serverScopeInfo.Schema == null) || overwrite)
                    {
                        SyncSet schema;
                        (context, schema) = await this.InternalGetSchemaAsync(context, setup, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                        serverScopeInfo.Setup  = setup;
                        serverScopeInfo.Schema = schema;

                        // Checking if we have already some scopes
                        // Then gets the first scope to get the id
                        List <ServerScopeInfo> allScopes;
                        (context, allScopes) = await this.InternalLoadAllServerScopesInfosAsync(context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                        if (allScopes.Count > 0)
                        {
                            // Get the first scope with an existing setup
                            var firstScope = allScopes.FirstOrDefault(sc => sc.Setup != null);

                            if (firstScope != null)
                            {
                                if (serverScopeInfo.Setup.TrackingTablesPrefix != firstScope.Setup.TrackingTablesPrefix)
                                {
                                    throw new Exception($"Can't add a new setup with different tracking table prefix. Please use same tracking table prefix as your first setup ([\"{firstScope.Setup.TrackingTablesPrefix}\"])");
                                }

                                if (serverScopeInfo.Setup.TrackingTablesSuffix != firstScope.Setup.TrackingTablesSuffix)
                                {
                                    throw new Exception($"Can't add a new setup with different tracking table suffix. Please use same tracking table suffix as your first setup ([\"{firstScope.Setup.TrackingTablesSuffix}\"])");
                                }

                                if (serverScopeInfo.Setup.TriggersPrefix != firstScope.Setup.TriggersPrefix)
                                {
                                    throw new Exception($"Can't add a new setup with different trigger prefix. Please use same trigger prefix as your first setup ([\"{firstScope.Setup.TriggersPrefix}\"])");
                                }

                                if (serverScopeInfo.Setup.TriggersSuffix != firstScope.Setup.TriggersSuffix)
                                {
                                    throw new Exception($"Can't add a new setup with different trigger suffix. Please use same trigger suffix as your first setup ([\"{firstScope.Setup.TriggersSuffix}\"])");
                                }
                            }
                        }

                        // Write scopes locally
                        (context, serverScopeInfo) = await this.InternalSaveServerScopeInfoAsync(serverScopeInfo, context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                        // override default value that is always false after saving
                        serverScopeInfo.IsNewScope = true;
                    }
                }
                await runner.CommitAsync().ConfigureAwait(false);

                return(context, serverScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
        internal virtual async Task <DatabaseMetadatasCleaned> InternalDeleteMetadatasAsync(SyncContext context, SyncSet schema, SyncSetup setup, long timestampLimit,
                                                                                            DbConnection connection, DbTransaction transaction,
                                                                                            CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            await this.InterceptAsync(new MetadataCleaningArgs(context, this.Setup, timestampLimit, connection, transaction), cancellationToken).ConfigureAwait(false);

            DatabaseMetadatasCleaned databaseMetadatasCleaned = new DatabaseMetadatasCleaned {
                TimestampLimit = timestampLimit
            };

            foreach (var syncTable in schema.Tables)
            {
                // Create sync adapter
                var syncAdapter = this.GetSyncAdapter(syncTable, setup);

                var command = await syncAdapter.GetCommandAsync(DbCommandType.DeleteMetadata, connection, transaction);

                // Set the special parameters for delete metadata
                DbSyncAdapter.SetParameterValue(command, "sync_row_timestamp", timestampLimit);

                var rowsCleanedCount = await command.ExecuteNonQueryAsync().ConfigureAwait(false);

                // Check if we have a return value instead
                var syncRowCountParam = DbSyncAdapter.GetParameter(command, "sync_row_count");

                if (syncRowCountParam != null)
                {
                    rowsCleanedCount = (int)syncRowCountParam.Value;
                }

                // Only add a new table metadata stats object, if we have, at least, purged 1 or more rows
                if (rowsCleanedCount > 0)
                {
                    var tableMetadatasCleaned = new TableMetadatasCleaned(syncTable.TableName, syncTable.SchemaName)
                    {
                        RowsCleanedCount = rowsCleanedCount,
                        TimestampLimit   = timestampLimit
                    };

                    databaseMetadatasCleaned.Tables.Add(tableMetadatasCleaned);
                }
            }

            await this.InterceptAsync(new MetadataCleanedArgs(context, databaseMetadatasCleaned, connection), cancellationToken).ConfigureAwait(false);

            return(databaseMetadatasCleaned);
        }
 public virtual Task <ServerScopeInfo> GetServerScopeInfoAsync(SyncSetup setup, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
 => GetServerScopeInfoAsync(SyncOptions.DefaultScopeName, setup, connection, transaction, cancellationToken, progress);
Exemple #23
0
        public virtual async Task <(SyncContext syncContext, DatabaseMetadatasCleaned databaseMetadatasCleaned)> DeleteMetadatasAsync(SyncContext context, SyncSet schema, SyncSetup setup, long timestampLimit,
                                                                                                                                      DbConnection connection, DbTransaction transaction,
                                                                                                                                      CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            DatabaseMetadatasCleaned databaseMetadatasCleaned = new DatabaseMetadatasCleaned {
                TimestampLimit = timestampLimit
            };

            foreach (var syncTable in schema.Tables)
            {
                // get table builder
                var tableBuilder = this.GetTableBuilder(syncTable, setup);

                var tableHelper = tableBuilder.CreateTableBuilder(connection, transaction);

                // check if table exists
                // If not, kindly continue, without exception
                if (await tableHelper.NeedToCreateTableAsync().ConfigureAwait(false))
                {
                    continue;
                }

                // Create sync adapter
                var syncAdapter = tableBuilder.CreateSyncAdapter(connection, transaction);

                // Delete metadatas
                var rowsCleanedCount = await syncAdapter.DeleteMetadatasAsync(timestampLimit).ConfigureAwait(false);

                // Only add a new table metadata stats object, if we have, at least, purged 1 or more rows
                if (rowsCleanedCount > 0)
                {
                    var tableMetadatasCleaned = new TableMetadatasCleaned(syncTable.TableName, syncTable.SchemaName);
                    tableMetadatasCleaned.RowsCleanedCount = rowsCleanedCount;
                    tableMetadatasCleaned.TimestampLimit   = timestampLimit;

                    this.Orchestrator.logger.LogDebug(SyncEventsId.MetadataCleaning, tableMetadatasCleaned);
                    databaseMetadatasCleaned.Tables.Add(tableMetadatasCleaned);
                }
            }

            this.Orchestrator.logger.LogDebug(SyncEventsId.MetadataCleaning, databaseMetadatasCleaned);

            return(context, databaseMetadatasCleaned);
        }
        /// <summary>
        /// Get the server scope info, ensures the scope is created.
        /// Provision is setup is defined (and scope does not exists in the database yet)
        /// </summary>
        /// <returns>Server scope info, containing scope name, version, setup and related schema infos</returns>
        public virtual async Task <ServerScopeInfo> GetServerScopeInfoAsync(string scopeName, SyncSetup setup = default, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), scopeName);

            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.ScopeLoading, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                ServerScopeInfo serverScopeInfo;
                (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, setup, false, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                await runner.CommitAsync().ConfigureAwait(false);

                return(serverScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
Exemple #25
0
        private async Task <Version> UpgdrateTo601Async(SyncContext context, SyncSet schema, SyncSetup setup, DbConnection connection, DbTransaction transaction,
                                                        CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var newVersion = new Version(0, 6, 1);
            // Sorting tables based on dependencies between them
            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

            var message = $"Upgrade to {newVersion}:";
            var args    = new UpgradeProgressArgs(context, message, newVersion, connection, transaction);

            this.ReportProgress(context, progress, args, connection, transaction);

            foreach (var schemaTable in schemaTables)
            {
                var tableBuilder = this.GetTableBuilder(schemaTable, setup);


                // Upgrade Select Initial Changes
                var exists = await InternalExistsStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChanges, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await InternalDropStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChanges, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
                await InternalCreateStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChanges, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                args = new UpgradeProgressArgs(context, $"SelectInitializedChanges stored procedure for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                this.ReportProgress(context, progress, args, connection, transaction);

                // Upgrade Select Initial Changes With Filter
                if (tableBuilder.TableDescription.GetFilter() != null)
                {
                    var existsWithFilter = await InternalExistsStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChangesWithFilters, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (existsWithFilter)
                    {
                        await InternalDropStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChangesWithFilters, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                    await InternalCreateStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.SelectInitializedChangesWithFilters, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    args = new UpgradeProgressArgs(context, $"SelectInitializedChangesWithFilters stored procedure for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                    this.ReportProgress(context, progress, args, connection, transaction);
                }
            }

            return(newVersion);
        }
        /// <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);
        }
Exemple #27
0
        private async Task <Version> UpgdrateTo700Async(SyncContext context, SyncSet schema, SyncSetup setup, DbConnection connection, DbTransaction transaction,
                                                        CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var newVersion = new Version(0, 7, 0);
            // Sorting tables based on dependencies between them

            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

            var message = $"Upgrade to {newVersion}:";
            var args    = new UpgradeProgressArgs(context, message, newVersion, connection, transaction);

            this.ReportProgress(context, progress, args, connection, transaction);

            foreach (var schemaTable in schemaTables)
            {
                var tableBuilder = this.GetTableBuilder(schemaTable, setup);

                // Upgrade Reset stored procedure
                var exists = await InternalExistsStoredProcedureAsync(context, tableBuilder,
                                                                      DbStoredProcedureType.Reset, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await InternalDropStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.Reset, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
                await InternalCreateStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.Reset, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                args = new UpgradeProgressArgs(context, $"Reset stored procedure for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                this.ReportProgress(context, progress, args, connection, transaction);

                // Upgrade Update stored procedure
                var existsUpdateSP = await InternalExistsStoredProcedureAsync(context, tableBuilder,
                                                                              DbStoredProcedureType.UpdateRow, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (existsUpdateSP)
                {
                    await InternalDropStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.UpdateRow, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
                await InternalCreateStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.UpdateRow, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                args = new UpgradeProgressArgs(context, $"Update stored procedure for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                this.ReportProgress(context, progress, args, connection, transaction);

                // Upgrade Bulk Update stored procedure
                if (this.Provider.SupportBulkOperations)
                {
                    var existsBulkUpdateSP = await InternalExistsStoredProcedureAsync(context, tableBuilder,
                                                                                      DbStoredProcedureType.BulkUpdateRows, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (existsBulkUpdateSP)
                    {
                        await InternalDropStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.BulkUpdateRows, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                    await InternalCreateStoredProcedureAsync(context, tableBuilder, DbStoredProcedureType.BulkUpdateRows, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    args = new UpgradeProgressArgs(context, $"Bulk Update stored procedure for table {tableBuilder.TableDescription.GetFullName()} updated", newVersion, connection, transaction);
                    this.ReportProgress(context, progress, args, connection, transaction);
                }
            }

            return(newVersion);
        }
        public virtual async Task <SyncContext> UpdateUntrackedRowsAsync(SyncContext context, SyncSet schema, SyncSetup setup,
                                                                         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 });

            foreach (var syncTable in schema.Tables)
            {
                var tableBuilder = this.GetTableBuilder(syncTable, setup);
                var syncAdapter  = tableBuilder.CreateSyncAdapter();

                await syncAdapter.UpdateUntrackedRowsAsync(connection, transaction).ConfigureAwait(false);
            }

            return(context);
        }
Exemple #29
0
        /// <summary>
        /// Get the correct Select changes command
        /// Can be either
        /// - SelectInitializedChanges              : All changes for first sync
        /// - SelectChanges                         : All changes filtered by timestamp
        /// - SelectInitializedChangesWithFilters   : All changes for first sync with filters
        /// - SelectChangesWithFilters              : All changes filtered by timestamp with filters
        /// </summary>
        internal async Task <DbCommand> GetSelectChangesCommandAsync(SyncContext context, SyncTable syncTable, SyncSetup setup, bool isNew, DbConnection connection, DbTransaction transaction)
        {
            DbCommand     command;
            DbCommandType dbCommandType;

            SyncFilter tableFilter = null;

            var syncAdapter = this.GetSyncAdapter(syncTable, setup);

            // Check if we have parameters specified

            // Sqlite does not have any filter, since he can't be server side
            if (this.Provider.CanBeServerProvider)
            {
                tableFilter = syncTable.GetFilter();
            }

            var hasFilters = tableFilter != null;

            // Determing the correct DbCommandType
            if (isNew && hasFilters)
            {
                dbCommandType = DbCommandType.SelectInitializedChangesWithFilters;
            }
            else if (isNew && !hasFilters)
            {
                dbCommandType = DbCommandType.SelectInitializedChanges;
            }
            else if (!isNew && hasFilters)
            {
                dbCommandType = DbCommandType.SelectChangesWithFilters;
            }
            else
            {
                dbCommandType = DbCommandType.SelectChanges;
            }

            // Get correct Select incremental changes command
            command = await syncAdapter.GetCommandAsync(dbCommandType, connection, transaction, tableFilter);

            return(command);
        }
Exemple #30
0
 /// <summary>
 /// Create a local orchestrator, used to orchestrates the whole sync on the client side
 /// </summary>
 public LocalOrchestrator(CoreProvider provider, SyncOptions options, SyncSetup setup, string scopeName = SyncOptions.DefaultScopeName)
     : base(provider, options, setup, scopeName)
 {
 }