/// <summary>
        /// Determines and applies migrations to the target system. This is done by getting the
        /// latest applied migration info from the target system, picking the migrations
        /// that must be executed, and applying them to the target system.
        /// PLEASE NOTE: when migrations are applied, all exceptions are caught by the migration engine.
        /// Exceptions are not caught in the analysis phase beforehand (when the latest migration info is retrieved).
        /// This means that you must analyze the summary for errors (e.g. via <see cref="MigrationSummary{TMigrationInfo}.EnsureSuccess" />)
        /// to ensure that no errors occurred during a run.
        /// </summary>
        /// <param name="now">
        /// The current time when the migration engine starts to execute (optional). Please use a UTC time stamp if possible. If
        /// you do not provide a value, <see cref="DateTime.UtcNow" /> will be used.
        /// </param>
        /// <param name="assembliesContainingMigrations">
        /// The assemblies that will be searched for migration types (optional). If you do not provide any assemblies,
        /// the calling assembly will be searched.
        /// </param>
        /// <param name="approach">
        /// The approach that should be taken to apply migrations (optional). The default is to determine the latest migration version
        /// of the target system and then apply all newer migrations. See the <see cref="MigrationApproach" /> enum for detailed infos.
        /// </param>
        /// <param name="cancellationToken">The token to cancel this asynchronous operation (optional).</param>
        /// <returns>A summary of all migrations that have been applied in this run and an optional error that might have occurred during a migration.</returns>
        /// <exception cref="MigrationException">Thrown when the migration infos in your target system are invalid, or when there are several migrations with the same version.</exception>
        /// <exception cref="Exception">A system-specific exception might occur when there are errors with the connection to the target system (e.g. a SqlException).</exception>
        public virtual Task <MigrationSummary <TMigrationInfo> > MigrateAsync(DateTime?now = null,
                                                                              Assembly[]?assembliesContainingMigrations = null,
                                                                              MigrationApproach approach          = MigrationApproach.MigrationsWithNewerVersions,
                                                                              CancellationToken cancellationToken = default)
        {
            if (assembliesContainingMigrations.IsNullOrEmpty())
            {
                assembliesContainingMigrations = new[] { Assembly.GetCallingAssembly() }
            }
            ;

            return(MigrateInternalAsync(now, assembliesContainingMigrations, approach, cancellationToken));

            // ReSharper disable VariableHidesOuterVariable
            async Task <MigrationSummary <TMigrationInfo> > MigrateInternalAsync(DateTime?now,
                                                                                 Assembly[] assembliesContainingMigrations,
                                                                                 MigrationApproach approach,
                                                                                 CancellationToken cancellationToken)
            // ReSharper restore VariableHidesOuterVariable
            {
                var migrationPlan = approach == MigrationApproach.AllNonAppliedMigrations ?
                                    await GetPlanForNonAppliedMigrationsInternal(assembliesContainingMigrations, cancellationToken) :
                                    await GetPlanForNewMigrationsInternal(assembliesContainingMigrations, cancellationToken);

                return(await ApplyMigrationsAsync(migrationPlan.PendingMigrations, now, cancellationToken));
            }
        }
Example #2
0
    public static async Task <MigrationSummary <MigrationInfo> > ApplyMigrationsFromTestClass <T>(
        this MigrationEngine <DatabaseContext> migrationEngine,
        MigrationApproach approach = MigrationApproach.MigrationsWithNewerVersions)
    {
        var testMigrations = new List <PendingMigration <long> >();

        foreach (var nestedType in typeof(T).GetNestedTypes())
        {
            var versionAttribute = nestedType.GetCustomAttribute <MigrationVersionAttribute>();
            if (nestedType.IsClass && versionAttribute is not null)
            {
                var migration = new PendingMigration <long>(versionAttribute.Version, nestedType);
                testMigrations.Add(migration);
            }
        }

        var migrationPlan = approach == MigrationApproach.MigrationsWithNewerVersions ?
                            await migrationEngine.GetPlanForNewMigrationsAsync() :
                            await migrationEngine.GetPlanForNonAppliedMigrationsAsync();

        var pendingMigrations = migrationPlan.PendingMigrations !
                                .Intersect(testMigrations)
                                .ToList();

        return(await migrationEngine.ApplyMigrationsAsync(pendingMigrations));
    }