/// <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(); } } } }
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); }
/// <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); }
/// <summary> /// Create a Sync Adapter /// </summary> public SyncAdapter(SyncTable tableDescription, SyncSetup setup) { this.TableDescription = tableDescription; this.Setup = setup; }
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); }
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; }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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);
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);
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);
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); } }
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); }
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); }
/// <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); }
/// <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) { }