private void ParseFileName() { var match = FileNameRegex.Match(FileName); if (!match.Success) { throw new FormatException($"Invalid FileSqlDatabaseMigration file name: must match the following regex '{FileNameRegex}'"); } moduleName = match.Groups["module"].Captures.Count > 0 ? match.Groups["module"].Captures[0].Value : throw new FormatException($"Invalid FileSqlDatabaseMigration file name: missing module name in '{FileName}'"); isBaseline = match.Groups["baseline"].Captures.Count > 0; isRepeatable = match.Groups["repeatable"].Captures.Count > 0; version = match.Groups["version"].Captures.Count > 0 ? DatabaseVersion.Parse(match.Groups["version"].Captures[0].Value) : null; tags = match.Groups["tag"].Captures.Count > 0 ? new[] { match.Groups["tag"].Captures.OfType <Capture>().Select(x => x.Value).ToArray() } : new string[0][]; }
public DatabaseMigrationSpecifier(string moduleName, DatabaseVersion version) { ModuleName = moduleName; Version = version; }
private async Task <IReadOnlyCollection <IDatabaseMigration> > DoSelectMigrationsAsync(string moduleName, string[] tags, DatabaseVersion targetVersion, List <DatabaseMigrationSpecifier> queuedMigrations) { IEnumerable <IDatabaseMigration> moduleMigrations = migrationRegistry.Migrations .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .Where(x => x.Tags.All(tagGroup => tags.Any(tagGroup.Contains))); if (targetVersion != null) { moduleMigrations = moduleMigrations.Where(x => x.Version.CompareTo(targetVersion) <= 0); } moduleMigrations = moduleMigrations .OrderBy(x => x.Version) .ToArray(); if (targetVersion != null) { if (!Equals(moduleMigrations.LastOrDefault()?.Version, targetVersion)) { throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}': migration for required version {targetVersion} was not found"); } } else if (!moduleMigrations.Any()) { throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}': no version for specified module were found"); } List <IDatabaseMigration> result = new List <IDatabaseMigration>(); if (targetVersion == null && moduleMigrations.Last().IsRepeatable) { if (!queuedMigrations.Any(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase))) { history = history ?? await migrationProvider.GetMigrationHistoryAsync(); string lastMigrationChecksum = history .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.TimeApplied) .Select(x => x.Checksum).FirstOrDefault(); var migration = moduleMigrations.Last(); if (lastMigrationChecksum != migration.Checksum) { result.Add(migration); } } } else { var version = queuedMigrations .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.Version) .Select(x => x.Version) .FirstOrDefault(); if (version == null) { history = history ?? await migrationProvider.GetMigrationHistoryAsync(); var lastMigration = history .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.Version).FirstOrDefault(); version = lastMigration?.Version; } if (version == null) { var baseline = moduleMigrations.FirstOrDefault(x => x.IsBaseline); if (baseline != null) { result.Add(baseline); result.AddRange(moduleMigrations .Where(x => !x.IsRepeatable && x.Version.CompareTo(baseline.Version) > 0)); } else { result.AddRange(moduleMigrations.Where(x => !x.IsRepeatable)); } result.AddRange(moduleMigrations.Where(x => x.IsRepeatable)); } else { result.AddRange(moduleMigrations .Where(x => !x.IsRepeatable && !x.IsBaseline && x.Version.CompareTo(version) > 0)); if (result.Count > 0) { result.AddRange(moduleMigrations.Where(x => x.IsRepeatable)); } } } foreach (var migration in result) { queuedMigrations.Add(new DatabaseMigrationSpecifier(migration.ModuleName, migration.Version)); } await AddRequiredDependenciesAsync(result, queuedMigrations, tags); return(result); }
public DatabaseMigrationSearchSpecifier(string moduleNameWildcard, DatabaseVersion version) { ModuleNameWildcard = moduleNameWildcard; Version = version; }
private async Task <IReadOnlyCollection <IDatabaseMigration> > DoSelectMigrationsAsync(string moduleName, string[] tags, DatabaseVersion targetVersion, List <DatabaseMigrationSpecifier> queuedMigrations) { IEnumerable <IDatabaseMigration> moduleMigrations = migrationRegistry.Migrations .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .Where(x => x.Tags.All(tagGroup => tags.Any(tagGroup.Contains))); if (targetVersion != null) { moduleMigrations = moduleMigrations.Where(x => x.Version.CompareTo(targetVersion) <= 0); } moduleMigrations = moduleMigrations .OrderBy(x => x.Version) .ToArray(); if (targetVersion != null) { if (moduleMigrations.Any() && moduleMigrations.Last().IsRepeatable) { throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}' version {targetVersion} because it is a repeatable migration module, which means it is versioned only by checksums"); } } else if (!moduleMigrations.Any()) { // TODO maybe return without errors if there are migrations for this module with different tags? throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}': no migrations for specified module were found"); } List <IDatabaseMigration> result = new List <IDatabaseMigration>(); if (moduleMigrations.Any() && moduleMigrations.Last().IsRepeatable) // repeatable-migration module { if (!queuedMigrations.Any(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase))) { history = history ?? await migrationProvider.GetMigrationHistoryAsync(); string lastMigrationChecksum = history .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.TimeApplied) .Select(x => x.Checksum).FirstOrDefault(); var migration = moduleMigrations.Last(); if (lastMigrationChecksum != migration.Checksum) { result.Add(migration); } } } else { var version = queuedMigrations .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.Version) .Select(x => x.Version) .FirstOrDefault(); if (version == null) { history = history ?? await migrationProvider.GetMigrationHistoryAsync(); var lastMigration = history .Where(x => string.Equals(x.ModuleName, moduleName, StringComparison.InvariantCultureIgnoreCase)) .OrderByDescending(x => x.Version).FirstOrDefault(); version = lastMigration?.Version; } if (version == null) { var baseline = moduleMigrations.FirstOrDefault(x => x.IsBaseline); if (baseline != null) { result.Add(baseline); result.AddRange(moduleMigrations .Where(x => x.Version.CompareTo(baseline.Version) > 0)); } else { result.AddRange(moduleMigrations); } } else { result.AddRange(moduleMigrations .Where(x => !x.IsBaseline && x.Version.CompareTo(version) > 0)); } if (targetVersion != null) { if ((version == null || version.CompareTo(targetVersion) < 0) && (!result.Any() || !Equals(result.LastOrDefault()?.Version, targetVersion))) { throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}': migration for required version {targetVersion} from {version?.ToString() ?? "nothing"} was not found"); } } else { var maxVersion = moduleMigrations.Select(x => x.Version).Max(); if ((!result.Any() && (version == null || version.CompareTo(maxVersion) < 0)) || (result.Any() && !result.Last().Version.Equals(maxVersion))) { throw new DatabaseMigrationException($"Cannot select database migrations for module '{moduleName}': migration path to required version 'latest' ({maxVersion}) from {version?.ToString() ?? "nothing"} was not found"); } } } foreach (var migration in result) { queuedMigrations.Add(new DatabaseMigrationSpecifier(migration.ModuleName, migration.Version)); } await AddRequiredDependenciesAsync(result, queuedMigrations, tags); return(result); }