/// <summary> /// Applies the provided migrations to the target system. The engine does not check /// if the specified migrations were already applied. /// PLEASE NOTE: this method will not throw. You must check the resulting summary for errors that might have occurred. /// </summary> /// <param name="pendingMigrations">The list of migrations that should be applied.</param> /// <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="cancellationToken">The token to cancel this asynchronous operation (optional).</param> /// <returns>A summary of all migrations that have been applied in this run.</returns> public virtual async Task <MigrationSummary <TMigrationInfo> > ApplyMigrationsAsync(List <PendingMigration <TMigrationVersion> >?pendingMigrations, DateTime?now = null, CancellationToken cancellationToken = default) { if (pendingMigrations.IsNullOrEmpty()) { return(MigrationSummary <TMigrationInfo> .Empty); } var timeStamp = now ?? DateTime.UtcNow; var appliedMigrations = new List <TMigrationInfo>(pendingMigrations.Count); for (var i = 0; i < pendingMigrations.Count; i++) { var pendingMigration = pendingMigrations[i]; IMigrationSession <TMigrationContext, TMigrationInfo>?session = null; TMigration?migration = default; try { migration = MigrationFactory.CreateMigration(pendingMigration.MigrationType); session = await SessionFactory.CreateSessionForMigrationAsync(migration, cancellationToken); await migration.ApplyAsync(session.Context, cancellationToken); var migrationInfo = CreateMigrationInfo(migration, timeStamp); await session.StoreMigrationInfoAsync(migrationInfo, cancellationToken); await session.SaveChangesAsync(cancellationToken); appliedMigrations.Add(migrationInfo); } catch (Exception exception) { var error = new MigrationError <TMigrationVersion>(pendingMigration.MigrationVersion, exception); return(new MigrationSummary <TMigrationInfo>(error, appliedMigrations)); } finally { // ReSharper disable SuspiciousTypeConversion.Global -- clients of the library should be allowed to write disposable migrations if (migration is IAsyncDisposable asyncDisposableMigration) { await asyncDisposableMigration.DisposeAsync(); } else if (migration is IDisposable disposableMigration) { disposableMigration.Dispose(); } // ReSharper restore SuspiciousTypeConversion.Global if (session != null) { await session.DisposeAsync(); } } } return(new MigrationSummary <TMigrationInfo>(appliedMigrations)); }