Ejemplo n.º 1
0
        public async Task <IReadOnlyCollection <SelectedModuleMigrations> > SelectMigrationsAsync(DatabaseMigrationSpecifier[] modules, string[] tags)
        {
            try
            {
                var queuedMigrations = new List <DatabaseMigrationSpecifier>();
                var result           = new List <SelectedModuleMigrations>();
                history = await migrationProvider.GetMigrationHistoryAsync();

                // select migrations for modules, repeatable and unversioned last
                var sortedModules = modules.OrderBy(x => IsRepeatableMigration(x) ? 1 : 0);

                foreach (var module in sortedModules)
                {
                    var migrations = await DoSelectMigrationsAsync(module, tags, queuedMigrations);

                    if (migrations.Count > 0)
                    {
                        result.Add(new SelectedModuleMigrations(module, migrations));
                    }
                }

                return(result);
            }
            finally
            {
                history = null;
            }
        }
        public async Task SelectMigrationsAsync_WithHistory()
        {
            migrationProvider.GetMigrationHistoryAsync()
            .Returns(new[]
            {
                new FakeDatabaseMigrationRecord()
                {
                    ModuleName = "appModule1",
                    Version    = DatabaseVersion.Parse("1.0.1")
                },
                new FakeDatabaseMigrationRecord()
                {
                    ModuleName = "appModule1",
                    Version    = DatabaseVersion.Parse("1.0.0")
                },
                new FakeDatabaseMigrationRecord()
                {
                    ModuleName = "appModule2",
                    Version    = DatabaseVersion.Parse("1.0.2")
                }
            });

            migrationRegistry.Migrations.Returns(new List <IDatabaseMigration>()
            {
                new FakeDatabaseMigration()
                {
                    ModuleName = "appModule1",
                    Version    = DatabaseVersion.Parse("1.0.2")
                },
                new FakeDatabaseMigration()
                {
                    ModuleName = "appModule1",
                    Version    = DatabaseVersion.Parse("1.0.1")
                },
                new FakeDatabaseMigration()
                {
                    ModuleName = "appModule1",
                    Version    = DatabaseVersion.Parse("1.0.0")
                }
            });

            var migrations = await sut.SelectMigrationsAsync(
                new[] { new DatabaseMigrationSpecifier("appModule1", null) },
                new string[0]);

            migrations.Should().HaveCount(1);
            migrations.First().Specifier.Should().Be(new DatabaseMigrationSpecifier("appModule1", null));
            migrations.First().Migrations.Should().Equal(
                migrationRegistry.Migrations.ElementAt(0));
        }
Ejemplo n.º 3
0
        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);
        }
        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);
        }