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][];
        }
Esempio n. 2
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);
        }