Пример #1
0
        /// <summary>
        /// Deprovision table
        /// </summary>
        public async Task DropAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null)
        {
            var alreadyOpened = connection.State != ConnectionState.Closed;

            if (!alreadyOpened)
            {
                await connection.OpenAsync().ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.StoredProcedures))
            {
                await this.DropStoredProceduresAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.Triggers))
            {
                await this.DropTriggersAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.TrackingTable))
            {
                await this.DropTrackingTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.Table))
            {
                await this.DropTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (!alreadyOpened)
            {
                connection.Close();
            }
        }
Пример #2
0
        /// <summary>
        /// Provision the local database based on the schema parameter, and the provision enumeration
        /// </summary>
        /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param>
        /// <param name="provision">Provision enumeration to determine which components to apply</param>
        /// <param name="clientScopeInfo">client scope. Will be saved once provision is done</param>
        /// <returns>Full schema with table and columns properties</returns>
        public virtual Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, bool overwrite = false, ScopeInfo clientScopeInfo = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        => RunInTransactionAsync(SyncStage.Provisioning, async(ctx, connection, transaction) =>
        {
            // Check incompatibility with the flags
            if (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope))
            {
                throw new InvalidProvisionForLocalOrchestratorException();
            }

            // Get server scope if not supplied
            if (clientScopeInfo == null)
            {
                var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    clientScopeInfo = await this.InternalGetScopeAsync <ScopeInfo>(ctx, DbScopeType.Client, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            schema = await InternalProvisionAsync(ctx, overwrite, schema, this.Setup, provision, clientScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            return(schema);
        }, connection, transaction, cancellationToken);
Пример #3
0
        /// <summary>
        /// Apply the config.
        /// Create the table if needed
        /// </summary>
        public async Task CreateAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null)
        {
            if (TableDescription.Columns.Count <= 0)
            {
                throw new MissingsColumnException(TableDescription.TableName);
            }

            if (TableDescription.PrimaryKeys.Count <= 0)
            {
                throw new MissingPrimaryKeyException(TableDescription.TableName);
            }

            var alreadyOpened = connection.State != ConnectionState.Closed;

            if (!alreadyOpened)
            {
                await connection.OpenAsync().ConfigureAwait(false);
            }

            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();

            if (provision.HasFlag(SyncProvision.Table))
            {
                await this.CreateTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.TrackingTable))
            {
                await this.CreateTrackingTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.Triggers))
            {
                await this.CreateTriggersAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.StoredProcedures))
            {
                await this.CreateStoredProceduresAsync(connection, transaction).ConfigureAwait(false);
            }

            stopwatch.Stop();
            var str = $"{stopwatch.Elapsed.Minutes}:{stopwatch.Elapsed.Seconds}.{stopwatch.Elapsed.Milliseconds}";

            System.Diagnostics.Debug.WriteLine(str);

            if (!alreadyOpened)
            {
                connection.Close();
            }
        }
        /// <summary>
        /// Provision the remote database based on the Setup parameter, and the provision enumeration
        /// </summary>
        /// <param name="provision">Provision enumeration to determine which components to apply</param>
        /// <param name="serverScopeInfo">server scope. Will be saved once provision is done</param>
        /// <returns>Full schema with table and columns properties</returns>
        public virtual async Task <ServerScopeInfo> ProvisionAsync(string scopeName, SyncSetup setup = null, SyncProvision provision = default, bool overwrite = false, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), scopeName);

            try
            {
                // Check incompatibility with the flags
                if (provision.HasFlag(SyncProvision.ClientScope))
                {
                    throw new InvalidProvisionForRemoteOrchestratorException();
                }

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

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

                // 2) Provision
                if (provision == SyncProvision.None)
                {
                    provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;
                }

                (context, _) = await this.InternalProvisionAsync(serverScopeInfo, context, false, provision, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

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

                return(serverScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
        InternalProvisionClientAsync(ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, SyncContext context, SyncProvision provision = default, bool overwrite = true, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                if (serverScopeInfo.Schema == null)
                {
                    throw new Exception($"No Schema in your server scope info {serverScopeInfo.Name}");
                }

                if (serverScopeInfo.Schema == null)
                {
                    throw new Exception($"No Setup in your server scope info {serverScopeInfo.Name}");
                }

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

                // Check incompatibility with the flags
                if (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope))
                {
                    throw new InvalidProvisionForLocalOrchestratorException();
                }

                // 2) Provision
                if (provision == SyncProvision.None)
                {
                    provision = SyncProvision.Table | SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable;
                }

                (context, _) = await this.InternalProvisionAsync(serverScopeInfo, context, overwrite, provision, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                // set client scope setup and schema
                clientScopeInfo.Setup  = serverScopeInfo.Setup;
                clientScopeInfo.Schema = serverScopeInfo.Schema;

                // Write scopes locally
                (context, clientScopeInfo) = await this.InternalSaveClientScopeInfoAsync(clientScopeInfo, context, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

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

                return(context, clientScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
        /// <summary>
        /// Provision the orchestrator database based on the schema argument, and the provision enumeration
        /// </summary>
        /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param>
        /// <param name="provision">Provision enumeration to determine which components to apply</param>
        /// <returns>Full schema with table and columns properties</returns>
        public virtual Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, bool overwrite = false, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        => RunInTransactionAsync(SyncStage.Provisioning, async(ctx, connection, transaction) =>
        {
            // Check incompatibility with the flags
            if (this is LocalOrchestrator && (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope)))
            {
                throw new InvalidProvisionForLocalOrchestratorException();
            }
            else if (!(this is LocalOrchestrator) && provision.HasFlag(SyncProvision.ClientScope))
            {
                throw new InvalidProvisionForRemoteOrchestratorException();
            }

            schema = await InternalProvisionAsync(ctx, overwrite, schema, provision, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            return(schema);
        }, connection, transaction, cancellationToken);
Пример #7
0
        /// <summary>
        /// Apply the config.
        /// Create the table if needed
        /// </summary>
        public async Task CreateAsync(SyncProvision provision, DbConnection connection, DbTransaction transaction = null)
        {
            if (TableDescription.Columns.Count <= 0)
            {
                throw new MissingsColumnException(TableDescription.TableName);
            }

            if (TableDescription.PrimaryKeys.Count <= 0)
            {
                throw new MissingPrimaryKeyException(TableDescription.TableName);
            }

            var alreadyOpened = connection.State != ConnectionState.Closed;

            if (!alreadyOpened)
            {
                await connection.OpenAsync().ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.Table))
            {
                await this.CreateTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.TrackingTable))
            {
                await this.CreateTrackingTableAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.Triggers))
            {
                await this.CreateTriggersAsync(connection, transaction).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.StoredProcedures))
            {
                await this.CreateStoredProceduresAsync(connection, transaction).ConfigureAwait(false);
            }

            if (!alreadyOpened)
            {
                connection.Close();
            }
        }
        internal async Task <(SyncContext context, bool deprovisioned)> InternalDeprovisionAsync(IScopeInfo scopeInfo, SyncContext context, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (Provider == null)
            {
                throw new MissingProviderException(nameof(InternalDeprovisionAsync));
            }

            context.SyncStage = SyncStage.Deprovisioning;

            // If schema does not have any table, raise an exception
            if (scopeInfo.Setup == null || scopeInfo.Setup.Tables == null || scopeInfo.Setup.Tables.Count <= 0)
            {
                throw new MissingTablesException(scopeInfo.Name);
            }

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

            await this.InterceptAsync(new DeprovisioningArgs(context, provision, scopeInfo.Setup, runner.Connection, runner.Transaction), progress, cancellationToken).ConfigureAwait(false);

            // get Database builder
            var builder = this.Provider.GetDatabaseBuilder();

            // Sorting tables based on dependencies between them
            IEnumerable <SyncTable> schemaTables;

            if (scopeInfo.Schema != null)
            {
                schemaTables = scopeInfo.Schema.Tables.SortByDependencies(tab => tab.GetRelations().Select(r => r.GetParentTable())).ToList();
            }
            else
            {
                schemaTables = new List <SyncTable>();
                foreach (var setupTable in scopeInfo.Setup.Tables)
                {
                    ((List <SyncTable>)schemaTables).Add(new SyncTable(setupTable.TableName, setupTable.SchemaName));
                }
            }

            // Disable check constraints
            if (this.Options.DisableConstraintsOnApplyChanges)
            {
                foreach (var table in schemaTables.Reverse())
                {
                    await this.InternalDisableConstraintsAsync(scopeInfo, context, this.GetSyncAdapter(table, scopeInfo), runner.Connection, runner.Transaction).ConfigureAwait(false);
                }
            }


            // Checking if we have to deprovision tables
            bool hasDeprovisionTableFlag = provision.HasFlag(SyncProvision.Table);

            // Firstly, removing the flag from the provision, because we need to drop everything in correct order, then drop tables in reverse side
            if (hasDeprovisionTableFlag)
            {
                provision ^= SyncProvision.Table;
            }

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

                if (provision.HasFlag(SyncProvision.StoredProcedures))
                {
                    (context, _) = await InternalDropStoredProceduresAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                    // Removing cached commands
                    var syncAdapter = this.GetSyncAdapter(schemaTable, scopeInfo);
                    syncAdapter.RemoveCommands();
                }

                if (provision.HasFlag(SyncProvision.Triggers))
                {
                    (context, _) = await InternalDropTriggersAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (provision.HasFlag(SyncProvision.TrackingTable))
                {
                    bool exists;
                    (context, exists) = await InternalExistsTrackingTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (exists)
                    {
                        (context, _) = await this.InternalDropTrackingTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }
            }

            // Eventually if we have the "Table" flag, then drop the table
            if (hasDeprovisionTableFlag)
            {
                foreach (var schemaTable in schemaTables.Reverse())
                {
                    var  tableBuilder = this.GetTableBuilder(schemaTable, scopeInfo);
                    bool exists;
                    (context, exists) = await InternalExistsTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (exists)
                    {
                        (context, _) = await this.InternalDropTableAsync(scopeInfo, context, tableBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }
            }

            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.Server, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    (context, _) = await this.InternalDropScopeInfoTableAsync(context, DbScopeType.ServerHistory, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            var args = new DeprovisionedArgs(context, provision, scopeInfo.Setup, runner.Connection);

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

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


            return(context, true);
        }
        /// <summary>
        /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables
        /// </summary>
        public async Task <SyncContext> DeprovisionAsync(SyncContext context, SyncSet schema, SyncSetup setup, SyncProvision provision, string scopeInfoTableName,
                                                         bool disableConstraintsOnApplyChanges, DbConnection connection, DbTransaction transaction,
                                                         CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (schema.Tables == null || !schema.HasTables)
            {
                throw new MissingTablesException();
            }

            this.Orchestrator.logger.LogDebug(SyncEventsId.Deprovision, new { TablesCount = schema.Tables.Count, ScopeInfoTableName = scopeInfoTableName, DisableConstraintsOnApplyChanges = disableConstraintsOnApplyChanges, });

            // get Database builder
            var builder = this.GetDatabaseBuilder();

            builder.UseChangeTracking = this.UseChangeTracking;
            builder.UseBulkProcedures = this.SupportBulkOperations;


            // Sorting tables based on dependencies between them
            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));


            // Disable check constraints
            if (disableConstraintsOnApplyChanges)
            {
                foreach (var table in schemaTables.Reverse())
                {
                    await this.DisableConstraintsAsync(context, table, setup, connection, transaction).ConfigureAwait(false);
                }
            }

            // Creating a local function to mutualize call
            var deprovisionFuncAsync = new Func <SyncProvision, IEnumerable <SyncTable>, Task>(async(p, tables) =>
            {
                foreach (var schemaTable in tables)
                {
                    var tableBuilder = this.GetTableBuilder(schemaTable, setup);
                    // set if the builder supports creating the bulk operations proc stock
                    tableBuilder.UseBulkProcedures = this.SupportBulkOperations;
                    tableBuilder.UseChangeTracking = this.UseChangeTracking;

                    // adding filter
                    this.AddFilters(schemaTable, tableBuilder);

                    this.Orchestrator.logger.LogDebug(SyncEventsId.Deprovision, schemaTable);

                    await tableBuilder.DropAsync(p, connection, transaction).ConfigureAwait(false);

                    // Interceptor
                    await this.Orchestrator.InterceptAsync(new TableDeprovisionedArgs(context, p, schemaTable, connection, transaction), cancellationToken).ConfigureAwait(false);
                }
            });

            // Checking if we have to deprovision tables
            bool hasDeprovisionTableFlag = provision.HasFlag(SyncProvision.Table);

            // Firstly, removing the flag from the provision, because we need to drop everything in correct order, then drop tables in reverse side
            if (hasDeprovisionTableFlag)
            {
                provision ^= SyncProvision.Table;
            }

            // Deprovision everything in order, excepting table
            await deprovisionFuncAsync(provision, schemaTables).ConfigureAwait(false);

            // then in reverse side, deprovision tables, if Table was part of Provision enumeration.
            if (hasDeprovisionTableFlag)
            {
                await deprovisionFuncAsync(SyncProvision.Table, schemaTables.Reverse()).ConfigureAwait(false);
            }


            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                context = await this.DropClientScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                context = await this.DropServerScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                context = await this.DropServerHistoryScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            return(context);
        }
        /// <summary>
        /// Be sure all tables are ready and configured for sync
        /// the ScopeSet Configuration MUST be filled by the schema form Database
        /// </summary>
        public virtual async Task <SyncContext> ProvisionAsync(SyncContext context, SyncSet schema, SyncSetup setup, SyncProvision provision, string scopeInfoTableName,
                                                               DbConnection connection, DbTransaction transaction,
                                                               CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (schema.Tables == null || !schema.HasTables)
            {
                throw new MissingTablesException();
            }

            this.Orchestrator.logger.LogDebug(SyncEventsId.Provision, new { TablesCount = schema.Tables.Count, ScopeInfoTableName = scopeInfoTableName });

            // get Database builder
            var builder = this.GetDatabaseBuilder();

            builder.UseChangeTracking = this.UseChangeTracking;
            builder.UseBulkProcedures = this.SupportBulkOperations;

            // Initialize database if needed
            await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false);

            // Shoudl we create scope
            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                context = await this.EnsureClientScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                context = await this.EnsureServerScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                context = await this.EnsureServerHistoryScopeAsync(context, scopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }


            // Sorting tables based on dependencies between them
            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

            foreach (var schemaTable in schemaTables)
            {
                var tableBuilder = this.GetTableBuilder(schemaTable, setup);
                // set if the builder supports creating the bulk operations proc stock
                tableBuilder.UseBulkProcedures = this.SupportBulkOperations;
                tableBuilder.UseChangeTracking = this.UseChangeTracking;

                // adding filter
                this.AddFilters(schemaTable, tableBuilder);

                this.Orchestrator.logger.LogDebug(SyncEventsId.Provision, schemaTable);

                // Interceptor
                await this.Orchestrator.InterceptAsync(new TableProvisioningArgs(context, provision, tableBuilder, connection, transaction), cancellationToken).ConfigureAwait(false);

                await tableBuilder.CreateAsync(provision, connection, transaction).ConfigureAwait(false);

                await tableBuilder.CreateForeignKeysAsync(connection, transaction).ConfigureAwait(false);

                // Interceptor
                await this.Orchestrator.InterceptAsync(new TableProvisionedArgs(context, provision, schemaTable, connection, transaction), cancellationToken).ConfigureAwait(false);
            }

            return(context);
        }
Пример #11
0
        /// <summary>
        /// Deprovision a database
        /// </summary>
        public async Task ProvisionAsync(string[] tables, SyncProvision provision)
        {
            DbConnection connection = null;

            try
            {
                if (tables == null || tables.Length == 0)
                {
                    throw new ArgumentNullException("tables", "You must set the tables you want to provision");
                }

                // Load the configuration
                var configuration = await this.ReadConfigurationAsync(tables);

                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync();

                    using (var transaction = connection.BeginTransaction())
                    {
                        if (provision.HasFlag(SyncProvision.Scope) || provision == SyncProvision.All)
                        {
                            var scopeBuilder = GetScopeBuilder().CreateScopeInfoBuilder(connection, transaction);
                            scopeBuilder.CreateScopeInfoTable();
                        }

                        for (int i = 0; i < configuration.Count; i++)
                        {
                            // Get the table
                            var dmTable = configuration[i];

                            // get the builder
                            var builder = GetDatabaseBuilder(dmTable);

                            // adding filters
                            this.AddFilters(configuration, dmTable, builder);

                            if (provision.HasFlag(SyncProvision.Table) || provision == SyncProvision.All)
                            {
                                builder.CreateTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision == SyncProvision.All)
                            {
                                builder.CreateTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision == SyncProvision.All)
                            {
                                builder.CreateTriggers(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision == SyncProvision.All)
                            {
                                builder.CreateStoredProcedures(connection, transaction);
                            }
                        }
                        transaction.Commit();
                    }

                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.DatabaseApplying, this.ProviderTypeName);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }
            }
        }
Пример #12
0
        /// <summary>
        /// Deprovision a database
        /// </summary>
        public async Task ProvisionAsync(SyncConfiguration configuration, SyncProvision provision)
        {
            DbConnection connection = null;

            try
            {
                if (configuration.Schema == null || !configuration.Schema.HasTables)
                {
                    throw new ArgumentNullException("tables", "You must set the tables you want to provision");
                }


                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync();

                    using (var transaction = connection.BeginTransaction())
                    {
                        // Load the configuration
                        await this.ReadSchemaAsync(configuration.Schema, connection, transaction);

                        var beforeArgs =
                            new DatabaseProvisioningArgs(null, provision, configuration.Schema, connection, transaction);

                        // Launch any interceptor if available
                        await this.InterceptAsync(beforeArgs);

                        if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All))
                        {
                            var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction);
                            if (scopeBuilder.NeedToCreateScopeInfoTable())
                            {
                                scopeBuilder.CreateScopeInfoTable();
                            }
                        }

                        // Sorting tables based on dependencies between them
                        var dmTables = configuration.Schema.Tables
                                       .SortByDependencies(tab => tab.ChildRelations
                                                           .Select(r => r.ChildTable));

                        foreach (var dmTable in dmTables)
                        {
                            // get the builder
                            var builder = this.GetDatabaseBuilder(dmTable);

                            // call any interceptor
                            await this.InterceptAsync(new TableProvisioningArgs(null, provision, dmTable, connection, transaction));

                            builder.UseBulkProcedures = this.SupportBulkOperations;

                            // adding filters
                            this.AddFilters(configuration.Filters, dmTable, builder);

                            // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous...
                            if (provision.HasFlag(SyncProvision.Table))
                            {
                                builder.CreateTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.CreateTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.CreateTriggers(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.CreateStoredProcedures(connection, transaction);
                            }

                            // call any interceptor
                            await this.InterceptAsync(new TableProvisionedArgs(null, provision, dmTable, connection, transaction));
                        }

                        // call any interceptor
                        await this.InterceptAsync(new DatabaseProvisionedArgs(null, provision, configuration.Schema, null, connection, transaction));

                        transaction.Commit();
                    }

                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.DatabaseApplying);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }
            }
        }
Пример #13
0
        /// <summary>
        /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables
        /// </summary>
        public async Task DeprovisionAsync(SyncConfiguration configuration, SyncProvision provision)
        {
            DbConnection  connection  = null;
            DbTransaction transaction = null;

            try
            {
                if (configuration.Schema == null || !configuration.Schema.HasTables)
                {
                    throw new ArgumentNullException("tables", "You must set the tables you want to provision");
                }


                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync();

                    await this.InterceptAsync(new ConnectionOpenArgs(null, connection));

                    using (transaction = connection.BeginTransaction())
                    {
                        await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction));

                        // Load the configuration
                        this.ReadSchema(configuration.Schema, connection, transaction);

                        // Launch any interceptor if available
                        await this.InterceptAsync(new DatabaseDeprovisioningArgs(null, provision, configuration.Schema, connection, transaction));

                        for (var i = configuration.Count - 1; i >= 0; i--)
                        {
                            // Get the table
                            var dmTable = configuration.Schema.Tables[i];

                            // call any interceptor
                            await this.InterceptAsync(new TableDeprovisioningArgs(null, provision, dmTable, connection, transaction));

                            // get the builder
                            var builder = this.GetDatabaseBuilder(dmTable);
                            builder.UseBulkProcedures = this.SupportBulkOperations;

                            // adding filters
                            this.AddFilters(configuration.Filters, dmTable, builder);

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropProcedures(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTriggers(connection, transaction);
                            }

                            // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous...
                            if (provision.HasFlag(SyncProvision.Table))
                            {
                                builder.DropTable(connection, transaction);
                            }

                            // call any interceptor
                            await this.InterceptAsync(new TableDeprovisionedArgs(null, provision, dmTable, connection, transaction));
                        }

                        if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All))
                        {
                            var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction);
                            if (!scopeBuilder.NeedToCreateScopeInfoTable())
                            {
                                scopeBuilder.DropScopeInfoTable();
                            }
                        }

                        // Launch any interceptor if available
                        await this.InterceptAsync(new DatabaseDeprovisionedArgs(null, provision, configuration.Schema, null, connection, transaction));

                        await this.InterceptAsync(new TransactionCommitArgs(null, connection, transaction));

                        transaction.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.SchemaApplying);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }

                await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction));
            }
        }
Пример #14
0
        /// <summary>
        /// Deprovision a database
        /// </summary>
        public async Task ProvisionAsync(SyncSet schema, SyncProvision provision, string scopeInfoTableName = SyncOptions.DefaultScopeInfoTableName)
        {
            DbConnection  connection  = null;
            DbTransaction transaction = null;

            try
            {
                if (schema.Tables == null || !schema.HasTables)
                {
                    throw new MissingTablesException();
                }

                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync().ConfigureAwait(false);

                    // Let provider knows a connection is opened
                    this.OnConnectionOpened(connection);

                    await this.InterceptAsync(new ConnectionOpenArgs(null, connection)).ConfigureAwait(false);

                    using (transaction = connection.BeginTransaction())
                    {
                        await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction)).ConfigureAwait(false);

                        var beforeArgs =
                            new DatabaseProvisioningArgs(null, provision, schema, connection, transaction);

                        // Launch any interceptor if available
                        await this.InterceptAsync(beforeArgs).ConfigureAwait(false);

                        // get Database builder
                        var builder = this.GetDatabaseBuilder();
                        builder.UseChangeTracking = this.UseChangeTracking;
                        builder.UseBulkProcedures = this.SupportBulkOperations;

                        // Initialize database if needed
                        builder.EnsureDatabase(connection, transaction);


                        if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All))
                        {
                            var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(scopeInfoTableName, connection, transaction);
                            if (scopeBuilder.NeedToCreateScopeInfoTable())
                            {
                                scopeBuilder.CreateScopeInfoTable();
                            }
                        }

                        // Sorting tables based on dependencies between them
                        var schemaTables = schema.Tables
                                           .SortByDependencies(tab => tab.GetRelations()
                                                               .Select(r => r.GetParentTable()));

                        foreach (var schemaTable in schemaTables)
                        {
                            // get the builder
                            var tableBuilder = this.GetTableBuilder(schemaTable);

                            // call any interceptor
                            await this.InterceptAsync(new TableProvisioningArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false);

                            tableBuilder.UseBulkProcedures = this.SupportBulkOperations;
                            tableBuilder.UseChangeTracking = this.UseChangeTracking;

                            // adding filters
                            this.AddFilters(schemaTable, tableBuilder);

                            // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous...
                            if (provision.HasFlag(SyncProvision.Table))
                            {
                                tableBuilder.CreateTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All))
                            {
                                tableBuilder.CreateTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All))
                            {
                                tableBuilder.CreateTriggers(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All))
                            {
                                tableBuilder.CreateStoredProcedures(connection, transaction);
                            }

                            // call any interceptor
                            await this.InterceptAsync(new TableProvisionedArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false);
                        }

                        // call any interceptor
                        await this.InterceptAsync(new DatabaseProvisionedArgs(null, provision, schema, null, connection, transaction)).ConfigureAwait(false);

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

                        transaction.Commit();
                    }

                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.SchemaApplying);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }

                await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction)).ConfigureAwait(false);

                // Let provider knows a connection is closed
                this.OnConnectionClosed(connection);
            }
        }
Пример #15
0
        /// <summary>
        /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables
        /// </summary>
        public async Task DeprovisionAsync(SyncSet schema, SyncProvision provision, string scopeInfoTableName = SyncOptions.DefaultScopeInfoTableName)
        {
            DbConnection  connection  = null;
            DbTransaction transaction = null;

            try
            {
                if (schema.Tables == null || !schema.HasTables)
                {
                    throw new MissingTablesException();
                }

                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync().ConfigureAwait(false);

                    // Let provider knows a connection is opened
                    this.OnConnectionOpened(connection);

                    await this.InterceptAsync(new ConnectionOpenArgs(null, connection)).ConfigureAwait(false);

                    using (transaction = connection.BeginTransaction())
                    {
                        await this.InterceptAsync(new TransactionOpenArgs(null, connection, transaction)).ConfigureAwait(false);

                        // Launch any interceptor if available
                        await this.InterceptAsync(new DatabaseDeprovisioningArgs(null, provision, schema, connection, transaction)).ConfigureAwait(false);

                        for (var i = schema.Tables.Count - 1; i >= 0; i--)
                        {
                            // Get the table
                            var schemaTable = schema.Tables[i];

                            // call any interceptor
                            await this.InterceptAsync(new TableDeprovisioningArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false);

                            // get the builder
                            var builder = this.GetTableBuilder(schemaTable);
                            builder.UseBulkProcedures = this.SupportBulkOperations;
                            builder.UseChangeTracking = this.UseChangeTracking;

                            // adding filters
                            this.AddFilters(schemaTable, builder);

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropProcedures(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTriggers(connection, transaction);
                            }

                            // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous...
                            if (provision.HasFlag(SyncProvision.Table))
                            {
                                builder.DropTable(connection, transaction);
                            }

                            // call any interceptor
                            await this.InterceptAsync(new TableDeprovisionedArgs(null, provision, schemaTable, connection, transaction)).ConfigureAwait(false);
                        }

                        if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All))
                        {
                            var scopeBuilder = this.GetScopeBuilder().CreateScopeInfoBuilder(scopeInfoTableName, connection, transaction);
                            if (!scopeBuilder.NeedToCreateScopeInfoTable())
                            {
                                scopeBuilder.DropScopeInfoTable();
                            }
                        }

                        // Launch any interceptor if available
                        await this.InterceptAsync(new DatabaseDeprovisionedArgs(null, provision, schema, null, connection, transaction)).ConfigureAwait(false);

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

                        transaction.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.SchemaApplying);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }

                await this.InterceptAsync(new ConnectionCloseArgs(null, connection, transaction)).ConfigureAwait(false);

                // Let provider knows a connection is closed
                this.OnConnectionClosed(connection);
            }
        }
        internal async Task <SyncSet> InternalProvisionAsync(SyncContext ctx, bool overwrite, SyncSet schema, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            // If schema does not have any table, raise an exception
            if (schema == null || schema.Tables == null || !schema.HasTables)
            {
                throw new MissingTablesException();
            }

            await this.InterceptAsync(new ProvisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false);

            // get Database builder
            var builder = this.Provider.GetDatabaseBuilder();

            // Initialize database if needed
            await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false);

            // Check if we have tables AND columns
            // If we don't have any columns it's most probably because user called method with the Setup only
            // So far we have only tables names, it's enough to get the schema
            if (schema.HasTables && !schema.HasColumns)
            {
                schema = await this.InternalGetSchemaAsync(ctx, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            // Get Scope Builder
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            // Shoudl we create scope
            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            // Sorting tables based on dependencies between them
            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

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

                // Check if we need to create a schema there
                var schemaExists = await InternalExistsSchemaAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!schemaExists)
                {
                    await InternalCreateSchemaAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (provision.HasFlag(SyncProvision.Table))
                {
                    var tableExistst = await this.InternalExistsTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (!tableExistst)
                    {
                        await this.InternalCreateTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }

                if (provision.HasFlag(SyncProvision.TrackingTable))
                {
                    var trackingTableExistst = await this.InternalExistsTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (!trackingTableExistst)
                    {
                        await this.InternalCreateTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }

                if (provision.HasFlag(SyncProvision.Triggers))
                {
                    await this.InternalCreateTriggersAsync(ctx, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (provision.HasFlag(SyncProvision.StoredProcedures))
                {
                    await this.InternalCreateStoredProceduresAsync(ctx, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            var args = new ProvisionedArgs(ctx, provision, schema, connection);

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

            this.ReportProgress(ctx, progress, args);

            return(schema);
        }
        internal async Task <bool> InternalDeprovisionAsync(SyncContext ctx, SyncSet schema, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            await this.InterceptAsync(new DeprovisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false);

            // get Database builder
            var builder = this.Provider.GetDatabaseBuilder();

            // Sorting tables based on dependencies between them
            var schemaTables = schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

            // Disable check constraints
            if (this.Options.DisableConstraintsOnApplyChanges)
            {
                foreach (var table in schemaTables.Reverse())
                {
                    await this.InternalDisableConstraintsAsync(ctx, this.GetSyncAdapter(table, this.Setup), connection, transaction).ConfigureAwait(false);
                }
            }


            // Checking if we have to deprovision tables
            bool hasDeprovisionTableFlag = provision.HasFlag(SyncProvision.Table);

            // Firstly, removing the flag from the provision, because we need to drop everything in correct order, then drop tables in reverse side
            if (hasDeprovisionTableFlag)
            {
                provision ^= SyncProvision.Table;
            }

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

                if (provision.HasFlag(SyncProvision.StoredProcedures))
                {
                    var allStoredProcedures = new List <DbStoredProcedureType>();

                    foreach (var spt in Enum.GetValues(typeof(DbStoredProcedureType)))
                    {
                        allStoredProcedures.Add((DbStoredProcedureType)spt);
                    }

                    allStoredProcedures.Reverse();

                    foreach (DbStoredProcedureType storedProcedureType in allStoredProcedures)
                    {
                        // if we are iterating on bulk, but provider do not support it, just loop through and continue
                        if ((storedProcedureType is DbStoredProcedureType.BulkTableType || storedProcedureType is DbStoredProcedureType.BulkUpdateRows || storedProcedureType is DbStoredProcedureType.BulkDeleteRows) &&
                            !this.Provider.SupportBulkOperations)
                        {
                            continue;
                        }

                        var exists = await InternalExistsStoredProcedureAsync(ctx, tableBuilder, storedProcedureType, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        // Drop storedProcedure if already exists
                        if (exists)
                        {
                            await InternalDropStoredProcedureAsync(ctx, tableBuilder, storedProcedureType, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                        }
                    }

                    // Removing cached commands
                    var syncAdapter = this.GetSyncAdapter(schemaTable, this.Setup);
                    syncAdapter.RemoveCommands();
                }

                if (provision.HasFlag(SyncProvision.Triggers))
                {
                    foreach (DbTriggerType triggerType in Enum.GetValues(typeof(DbTriggerType)))
                    {
                        var exists = await InternalExistsTriggerAsync(ctx, tableBuilder, triggerType, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                        // Drop trigger if already exists
                        if (exists)
                        {
                            await InternalDropTriggerAsync(ctx, tableBuilder, triggerType, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                        }
                    }
                }

                if (provision.HasFlag(SyncProvision.TrackingTable))
                {
                    var exists = await InternalExistsTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (exists)
                    {
                        await this.InternalDropTrackingTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }
            }

            // Eventually if we have the "Table" flag, then drop the table
            if (hasDeprovisionTableFlag)
            {
                foreach (var schemaTable in schemaTables.Reverse())
                {
                    var tableBuilder = this.GetTableBuilder(schemaTable, this.Setup);

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

                    if (exists)
                    {
                        await this.InternalDropTableAsync(ctx, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }
            }

            // Get Scope Builder
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.Client, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (exists)
                {
                    await this.InternalDropScopeInfoTableAsync(ctx, DbScopeType.ServerHistory, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            var args = new DeprovisionedArgs(ctx, provision, schema, connection);

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

            this.ReportProgress(ctx, progress, args);

            return(true);
        }
Пример #18
0
        /// <summary>
        /// Provision the orchestrator database based on the schema argument, and the provision enumeration
        /// </summary>
        /// <param name="schema">Schema to be applied to the database managed by the orchestrator, through the provider.</param>
        /// <param name="provision">Provision enumeration to determine which components to apply</param>
        /// <returns>Full schema with table and columns properties</returns>
        public virtual async Task <SyncSet> ProvisionAsync(SyncSet schema, SyncProvision provision, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            if (!this.StartTime.HasValue)
            {
                this.StartTime = DateTime.UtcNow;
            }

            // Get context or create a new one
            var ctx = this.GetContext();

            using (var connection = this.Provider.CreateConnection())
            {
                // log
                this.logger.LogInformation(SyncEventsId.Provision, new { connection.Database, Provision = provision });

                try
                {
                    ctx.SyncStage = SyncStage.Provisioning;

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

                    if (this is LocalOrchestrator && (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope)))
                    {
                        throw new InvalidProvisionForLocalOrchestratorException();
                    }
                    else if (!(this is LocalOrchestrator) && provision.HasFlag(SyncProvision.ClientScope))
                    {
                        throw new InvalidProvisionForRemoteOrchestratorException();
                    }


                    // Open connection
                    await this.OpenConnectionAsync(connection, cancellationToken).ConfigureAwait(false);

                    // Create a transaction
                    using (var transaction = connection.BeginTransaction())
                    {
                        await this.InterceptAsync(new TransactionOpenedArgs(ctx, connection, transaction), cancellationToken).ConfigureAwait(false);

                        // Check if we have tables AND columns
                        // If we don't have any columns it's most probably because user called method with the Setup only
                        // So far we have only tables names, it's enough to get the schema
                        if (schema.HasTables && !schema.HasColumns)
                        {
                            this.logger.LogInformation(SyncEventsId.GetSchema, this.Setup);
                            (ctx, schema) = await this.Provider.GetSchemaAsync(ctx, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                        }

                        if (!schema.HasTables)
                        {
                            throw new MissingTablesException();
                        }

                        if (!schema.HasColumns)
                        {
                            throw new MissingColumnsException();
                        }

                        await this.InterceptAsync(new DatabaseProvisioningArgs(ctx, provision, schema, connection, transaction), cancellationToken).ConfigureAwait(false);

                        await this.Provider.ProvisionAsync(ctx, schema, this.Setup, provision, this.Options.ScopeInfoTableName, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

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

                        transaction.Commit();
                    }

                    ctx.SyncStage = SyncStage.Provisioned;

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

                    var args = new DatabaseProvisionedArgs(ctx, provision, schema, connection);
                    await this.InterceptAsync(args, cancellationToken).ConfigureAwait(false);

                    this.ReportProgress(ctx, progress, args);
                }
                catch (Exception ex)
                {
                    RaiseError(ex);
                }
                finally
                {
                    if (connection != null && connection.State == ConnectionState.Open)
                    {
                        connection.Close();
                    }
                }

                return(schema);
            }
        }
        internal async Task <(SyncContext context, bool provisioned)> InternalProvisionAsync(IScopeInfo scopeInfo, SyncContext context, bool overwrite, SyncProvision provision, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            if (Provider == null)
            {
                throw new MissingProviderException(nameof(InternalProvisionAsync));
            }

            context.SyncStage = SyncStage.Provisioning;

            // If schema does not have any table, raise an exception
            if (scopeInfo.Schema == null || scopeInfo.Schema.Tables == null || !scopeInfo.Schema.HasTables)
            {
                throw new MissingTablesException(scopeInfo.Name);
            }

            await this.InterceptAsync(new ProvisioningArgs(context, provision, scopeInfo.Schema, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            // get Database builder
            var builder = this.Provider.GetDatabaseBuilder();

            // Initialize database if needed
            await builder.EnsureDatabaseAsync(connection, transaction).ConfigureAwait(false);

            // Check if we have tables AND columns
            // If we don't have any columns it's most probably because user called method with the Setup only
            // So far we have only tables names, it's enough to get the schema
            if (scopeInfo.Schema.HasTables && !scopeInfo.Schema.HasColumns)
            {
                (context, scopeInfo.Schema) = await this.InternalGetSchemaAsync(context, scopeInfo.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            // Shoudl we create scope
            if (provision.HasFlag(SyncProvision.ClientScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Client, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Client, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            // if scope is not null, so obviously we have create the table before, so no need to test
            if (provision.HasFlag(SyncProvision.ServerScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Server, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Server, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            if (provision.HasFlag(SyncProvision.ServerHistoryScope))
            {
                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.ServerHistory, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    (context, _) = await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.ServerHistory, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }

            // Sorting tables based on dependencies between them
            var schemaTables = scopeInfo.Schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

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

                // Check if we need to create a schema there
                bool schemaExists;
                (context, schemaExists) = await InternalExistsSchemaAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!schemaExists)
                {
                    (context, _) = await InternalCreateSchemaAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (provision.HasFlag(SyncProvision.Table))
                {
                    bool tableExists;
                    (context, tableExists) = await this.InternalExistsTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (!tableExists)
                    {
                        (context, _) = await this.InternalCreateTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }

                if (provision.HasFlag(SyncProvision.TrackingTable))
                {
                    bool trackingTableExist;
                    (context, trackingTableExist) = await this.InternalExistsTrackingTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                    if (!trackingTableExist)
                    {
                        (context, _) = await this.InternalCreateTrackingTableAsync(scopeInfo, context, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }

                if (provision.HasFlag(SyncProvision.Triggers))
                {
                    await this.InternalCreateTriggersAsync(scopeInfo, context, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                if (provision.HasFlag(SyncProvision.StoredProcedures))
                {
                    (context, _) = await this.InternalCreateStoredProceduresAsync(scopeInfo, context, overwrite, tableBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
                }
            }


            await this.InterceptAsync(new ProvisionedArgs(context, provision, scopeInfo.Schema, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            return(context, true);
        }
Пример #20
0
        /// <summary>
        /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables
        /// </summary>
        public async Task DeprovisionAsync(SyncConfiguration configuration, SyncProvision provision)
        {
            DbConnection connection = null;

            try
            {
                if (configuration.Schema == null || !configuration.Schema.HasTables)
                {
                    throw new ArgumentNullException("tables", "You must set the tables you want to provision");
                }

                // Load the configuration
                await this.ReadSchemaAsync(configuration.Schema);

                // Open the connection
                using (connection = this.CreateConnection())
                {
                    await connection.OpenAsync();

                    using (var transaction = connection.BeginTransaction())
                    {
                        for (int i = configuration.Count - 1; i >= 0; i--)
                        {
                            // Get the table
                            var dmTable = configuration.Schema.Tables[i];

                            // get the builder
                            var builder = GetDatabaseBuilder(dmTable);

                            // adding filters
                            this.AddFilters(configuration.Filters, dmTable, builder);

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropProcedures(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision.HasFlag(SyncProvision.All))
                            {
                                builder.DropTriggers(connection, transaction);
                            }

                            // On purpose, the flag SyncProvision.All does not include the SyncProvision.Table, too dangerous...
                            if (provision.HasFlag(SyncProvision.Table))
                            {
                                builder.DropTable(connection, transaction);
                            }
                        }

                        if (provision.HasFlag(SyncProvision.Scope) || provision.HasFlag(SyncProvision.All))
                        {
                            var scopeBuilder = GetScopeBuilder().CreateScopeInfoBuilder(configuration.ScopeInfoTableName, connection, transaction);
                            if (!scopeBuilder.NeedToCreateScopeInfoTable())
                            {
                                scopeBuilder.DropScopeInfoTable();
                            }
                        }
                        transaction.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.DatabaseApplying, this.ProviderTypeName);
            }
            finally
            {
                if (connection != null && connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }
            }
        }
Пример #21
0
        /// <summary>
        /// Deprovision a database. You have to passe a configuration object, containing at least the dmTables
        /// </summary>
        public async Task DeprovisionAsync(string[] tables, SyncProvision provision)
        {
            if (tables == null || tables.Length == 0)
            {
                throw new SyncException("You must set the tables you want to modify");
            }

            // Load the configuration
            var configuration = await this.ReadConfigurationAsync(tables);

            // Open the connection
            using (var connection = this.CreateConnection())
            {
                try
                {
                    await connection.OpenAsync();

                    using (var transaction = connection.BeginTransaction())
                    {
                        for (int i = configuration.Count - 1; i >= 0; i--)
                        {
                            // Get the table
                            var dmTable = configuration[i];

                            // get the builder
                            var builder = GetDatabaseBuilder(dmTable);

                            // adding filters
                            this.AddFilters(configuration, dmTable, builder);

                            if (provision.HasFlag(SyncProvision.TrackingTable) || provision == SyncProvision.All)
                            {
                                builder.DropTrackingTable(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.StoredProcedures) || provision == SyncProvision.All)
                            {
                                builder.DropProcedures(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Triggers) || provision == SyncProvision.All)
                            {
                                builder.DropTriggers(connection, transaction);
                            }

                            if (provision.HasFlag(SyncProvision.Table) || provision == SyncProvision.All)
                            {
                                builder.DropTable(connection, transaction);
                            }
                        }


                        transaction.Commit();
                    }
                }
                catch (Exception ex)
                {
                    throw SyncException.CreateUnknowException(SyncStage.BeginSession, ex);
                }
                finally
                {
                    if (connection.State != ConnectionState.Closed)
                    {
                        connection.Close();
                    }
                }
            }
        }