public MigratorBuilder(IServiceCollection services = null) { _services = services ?? new ServiceCollection(); _migrationsProviders = new List <IMigrationsProvider>(); _preMigrationsProviders = new List <IMigrationsProvider>(); _upgradePolicy = MigrationPolicy.All; _downgradePolicy = MigrationPolicy.All; _targetVersion = default; _variables = new Dictionary <string, string>(); }
/// <summary> /// Check permission to migrate using specified policy /// </summary> /// <param name="versionDifference"></param> /// <param name="policy"></param> /// <returns></returns> private bool IsMigrationAllowed(DbVersionDifference versionDifference, MigrationPolicy policy) { switch (versionDifference) { case DbVersionDifference.Major: return(policy.HasFlag(MigrationPolicy.Major)); case DbVersionDifference.Minor: return(policy.HasFlag(MigrationPolicy.Minor)); default: return(false); } }
/// <summary> /// Default realisation of <see cref="IDbMigrator"/> /// </summary> /// <param name="dbProvider">Provider for database</param> /// <param name="migrations">Main migrations for changing database</param> /// <param name="upgradePolicy">Policy for upgrading database</param> /// <param name="downgradePolicy">Policy for downgrading database</param> /// <param name="preMigrations">Migrations that will be executed before <paramref name="migrations"/></param> /// <param name="targetVersion">Desired version of database after migration. If <see langword="null"/> migrator will upgrade database to the most actual version</param> /// <param name="logger">Optional logger</param> public DbMigrator( IDbProvider dbProvider, ICollection <IMigration> migrations, MigrationPolicy upgradePolicy, MigrationPolicy downgradePolicy, ICollection <IMigration> preMigrations = null, DbVersion?targetVersion = null, ILogger logger = null) { if (migrations == null) { throw new ArgumentNullException(nameof(migrations)); } _dbProvider = dbProvider ?? throw new ArgumentNullException(nameof(dbProvider)); _migrations = migrations.ToList(); var migrationCheckMap = new HashSet <DbVersion>(); foreach (var migration in _migrations) { if (migrationCheckMap.Contains(migration.Version)) { throw new InvalidOperationException( $"There is more than one migration with version {migration.Version}"); } migrationCheckMap.Add(migration.Version); } _upgradePolicy = upgradePolicy; _downgradePolicy = downgradePolicy; _preMigrations = preMigrations ?? new List <IMigration>(0); _targetVersion = targetVersion; _logger = logger; }
private async Task MigrateAsync(IReadOnlyCollection <IMigration> orderedMigrations, bool isUpgrade, MigrationPolicy policy, CancellationToken token = default) { if (policy == MigrationPolicy.Forbidden) { throw new MigrationException(MigrationError.PolicyError, $"{(isUpgrade ? "Upgrading": "Downgrading")} is forbidden due to migration policy"); } if (orderedMigrations.Count == 0) { return; } var operationName = isUpgrade ? "Upgrade" : "Downgrade"; foreach (var migration in orderedMigrations) { token.ThrowIfCancellationRequested(); DbTransaction?transaction = null; try { // sometimes transactions fails without reopening connection //todo #19: fix it later await _dbProvider.CloseConnectionAsync(); await _dbProvider.OpenConnectionAsync(token); if (migration.IsTransactionRequired) { transaction = _dbProvider.BeginTransaction(); } else { _logger?.LogWarning($"Transaction is disabled for migration {migration.Version}"); } _logger?.LogInformation($"{operationName} to {migration.Version} ({migration.Comment} for database \"{_dbProvider.DbName}\")..."); if (isUpgrade) { await migration.UpgradeAsync(transaction, token); await _dbProvider.SaveAppliedMigrationVersionAsync(migration.Comment, migration.Version, token); } else { if (!(migration is IDowngradeMigration downgradableMigration)) { throw new MigrationException(MigrationError.MigrationNotFound, $"Migration with version {migration.Version} doesn't support downgrade"); } await downgradableMigration.DowngradeAsync(transaction, token); await _dbProvider.DeleteAppliedMigrationVersionAsync(migration.Version, token); } // Commit transaction if all commands succeed, transaction will auto-rollback // when disposed if either commands fails transaction?.Commit(); _logger?.LogInformation($"{operationName} to {migration.Version} (database \"{_dbProvider.DbName}\") completed."); } catch (Exception e) { throw new MigrationException( MigrationError.MigratingError, $"Error while executing {operationName.ToLower()} migration to {migration.Version} for database \"{_dbProvider.DbName}\": {e.Message}", e); } finally { transaction?.Dispose(); } } }
/// <summary> /// Default realisation of <see cref="IDbMigrator"/> /// </summary> /// <param name="dbProvider">Provider for database</param> /// <param name="migrations">Main migrations for changing database</param> /// <param name="upgradePolicy">Policy for upgrading database</param> /// <param name="downgradePolicy">Policy for downgrading database</param> /// <param name="preMigrations">Migrations that will be executed before <paramref name="migrations"/></param> /// <param name="targetVersion">Desired version of database after migration. If <see langword="null"/> migrator will upgrade database to the most actual version</param> /// <param name="logger">Optional logger</param> public DbMigrator( IDbProvider dbProvider, ICollection <IMigration> migrations, MigrationPolicy upgradePolicy, MigrationPolicy downgradePolicy, ICollection <IMigration>?preMigrations = null, DbVersion?targetVersion = null, ILogger?logger = null) { if (migrations == null) { throw new ArgumentNullException(nameof(migrations)); } _dbProvider = dbProvider ?? throw new ArgumentNullException(nameof(dbProvider)); _logger = logger; var migrationMap = new Dictionary <DbVersion, IMigration>(); foreach (var migration in migrations) { if (migrationMap.ContainsKey(migration.Version)) { throw new ArgumentException( $"There is more than one migration with version {migration.Version}", nameof(migrations)); } migrationMap.Add(migration.Version, migration); } _migrationMap = migrationMap; _upgradePolicy = upgradePolicy; _downgradePolicy = downgradePolicy; _targetVersion = targetVersion; _preMigrations = preMigrations ?? Array.Empty <IMigration>(); var preMigrationCheckMap = new HashSet <DbVersion>(); foreach (var migration in _preMigrations) { if (preMigrationCheckMap.Contains(migration.Version)) { throw new ArgumentException($"There is more than one pre-migration with version = {migration.Version}", nameof(preMigrations)); } preMigrationCheckMap.Add(migration.Version); } if (targetVersion.HasValue && migrationMap.Values.Count > 0) { var migrationsMaxVersion = _migrationMap.Values.Max(x => x.Version); if (targetVersion > migrationsMaxVersion) { throw new ArgumentException("Target version can't be greater than max available migration version", nameof(targetVersion)); } if (_migrationMap.Values.All(x => x.Version != targetVersion)) { throw new ArgumentException($"No migrations were registered with desired target version (target version = {targetVersion})", nameof(targetVersion)); } } }
/// <summary> /// Setup downgrade migration policy /// </summary> /// <param name="policy">Policy</param> /// <returns></returns> public MigratorBuilder UseDowngradeMigrationPolicy(MigrationPolicy policy) { _downgradePolicy = policy; return(this); }
/// <summary> /// Setup upgrade migration policy /// </summary> /// <param name="policy">Policy</param> /// <returns></returns> public MigratorBuilder UseUpgradeMigrationPolicy(MigrationPolicy policy) { _upgradePolicy = policy; return(this); }