/// <summary> /// Migrates the database to the latest version /// </summary> /// <returns>The number of applied migrations</returns> public int RunPendingMigrations(TContext context) { var pendingMigrations = GetPendingMigrations().ToList(); if (!pendingMigrations.Any()) { return(0); } var coreSeeders = new List <SeederEntry>(); var externalSeeders = new List <SeederEntry>(); var isCoreMigration = context is SmartObjectContext; var initialMigration = this.GetDatabaseMigrations().LastOrDefault() ?? "[Initial]"; var lastSuccessfulMigration = initialMigration; IDataSeeder <SmartObjectContext> coreSeeder = null; IDataSeeder <TContext> externalSeeder = null; int result = 0; _isMigrating = true; // Apply migrations foreach (var migrationId in pendingMigrations) { if (MigratorUtils.IsAutomaticMigration(migrationId)) { continue; } if (!MigratorUtils.IsValidMigrationId(migrationId)) { continue; } // Resolve and instantiate the DbMigration instance from the assembly var migration = MigratorUtils.CreateMigrationInstanceByMigrationId(migrationId, Configuration); // Seeders for the core DbContext must be run in any case // (e.g. for Resource or Setting updates even from external plugins) coreSeeder = migration as IDataSeeder <SmartObjectContext>; externalSeeder = null; if (!isCoreMigration) { // Context specific seeders should only be resolved // when origin is external (e.g. a Plugin) externalSeeder = migration as IDataSeeder <TContext>; } try { // Call the actual Update() to execute this migration Update(migrationId); result++; } catch (AutomaticMigrationsDisabledException) { if (context is SmartObjectContext) { _isMigrating = false; throw; } // DbContexts in plugin assemblies tend to produce // this error, but obviously without any negative side-effect. // Therefore catch and forget! // TODO: (MC) investigate this and implement a cleaner solution } catch (Exception ex) { result = 0; _isMigrating = false; throw new DbMigrationException(lastSuccessfulMigration, migrationId, ex.InnerException ?? ex, false); } if (coreSeeder != null) { coreSeeders.Add(new SeederEntry { DataSeeder = coreSeeder, MigrationId = migrationId, PreviousMigrationId = lastSuccessfulMigration, }); } if (externalSeeder != null) { externalSeeders.Add(new SeederEntry { DataSeeder = externalSeeder, MigrationId = migrationId, PreviousMigrationId = lastSuccessfulMigration, }); } lastSuccessfulMigration = migrationId; } // Apply core data seeders first SmartObjectContext coreContext = null; if (coreSeeders.Any()) { coreContext = isCoreMigration ? context as SmartObjectContext : new SmartObjectContext(); RunSeeders <SmartObjectContext>(coreSeeders, coreContext); } // Apply external data seeders RunSeeders <TContext>(externalSeeders, context); _isMigrating = false; Logger.Info("Database migration successful: {0} >> {1}".FormatInvariant(initialMigration, lastSuccessfulMigration)); return(result); }
/// <summary> /// Migrates the database to the latest version /// </summary> /// <returns>The number of applied migrations</returns> public int RunPendingMigrations(TContext context) { if (_lastSeedException != null) { // This can happen when a previous migration attempt failed with a rollback. //return 0; throw _lastSeedException; } var pendingMigrations = GetPendingMigrations().ToList(); if (!pendingMigrations.Any()) { return(0); } // There are pending migrations and the app version is 3.0.0 at least. // Earlier versions are totally blocked by AppUpdater. // We have to ensure that the initial migration - which is '201705281903241_MoreIndexes' // after squashing old 2.x and 1.x migrations - does not run if current head is '201705102339006_V3Final'. DbMigrationContext.Current.SetSuppressInitialCreate <TContext>(true); var coreSeeders = new List <SeederEntry>(); var externalSeeders = new List <SeederEntry>(); var isCoreMigration = context is SmartObjectContext; var databaseMigrations = this.GetDatabaseMigrations().ToArray(); var initialMigration = databaseMigrations.LastOrDefault() ?? "[Initial]"; var lastSuccessfulMigration = databaseMigrations.FirstOrDefault(); int result = 0; _isMigrating = true; // Apply migrations foreach (var migrationId in pendingMigrations) { if (MigratorUtils.IsAutomaticMigration(migrationId)) { continue; } if (!MigratorUtils.IsValidMigrationId(migrationId)) { continue; } // Resolve and instantiate the DbMigration instance from the assembly var migration = MigratorUtils.CreateMigrationInstanceByMigrationId(migrationId, Configuration); // Seeders for the core DbContext must be run in any case // (e.g. for Resource or Setting updates even from external plugins) var coreSeeder = migration as IDataSeeder <SmartObjectContext>; IDataSeeder <TContext> externalSeeder = null; if (!isCoreMigration) { // Context specific seeders should only be resolved // when origin is external (e.g. a Plugin) externalSeeder = migration as IDataSeeder <TContext>; } try { // Call the actual Update() to execute this migration Update(migrationId); result++; } catch (AutomaticMigrationsDisabledException) { if (context is SmartObjectContext) { _isMigrating = false; throw; } // DbContexts in plugin assemblies tend to produce // this error, but obviously without any negative side-effect. // Therefore catch and forget! // TODO: (MC) investigate this and implement a cleaner solution } catch (Exception ex) { result = 0; _isMigrating = false; throw new DbMigrationException(lastSuccessfulMigration, migrationId, ex.InnerException ?? ex, false); } var migrationName = MigratorUtils.GetMigrationClassName(migrationId); if (coreSeeder != null) { coreSeeders.Add(new SeederEntry { DataSeeder = coreSeeder, MigrationId = migrationId, MigrationName = migrationName, PreviousMigrationId = lastSuccessfulMigration, }); } if (externalSeeder != null) { externalSeeders.Add(new SeederEntry { DataSeeder = externalSeeder, MigrationId = migrationId, MigrationName = migrationName, PreviousMigrationId = lastSuccessfulMigration, }); } lastSuccessfulMigration = migrationId; DbMigrationContext.Current.AddAppliedMigration(typeof(TContext), migrationId); } if (coreSeeders.Any()) { // Apply core data seeders first var coreContext = isCoreMigration ? context as SmartObjectContext : new SmartObjectContext(); RunSeeders <SmartObjectContext>(coreSeeders, coreContext); } // Apply external data seeders RunSeeders <TContext>(externalSeeders, context); _isMigrating = false; Logger.Info("Database migration successful: {0} >> {1}".FormatInvariant(initialMigration, lastSuccessfulMigration)); return(result); }