public async Task UpdateAsync(string featureId) { if (_processedFeatures.Contains(featureId)) { return; } _processedFeatures.Add(featureId); if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Updating feature: {0}", featureId); } // proceed with dependent features first, whatever the module it's in var dependencies = _extensionManager .GetDependentFeatures( featureId) .Where(x => x.Id != featureId) .Select(x => x.Id); await UpdateAsync(dependencies); var migrations = GetDataMigrations(featureId); // apply update methods to each migration class for the module foreach (var migration in migrations) { var schemaBuilder = new SchemaBuilder(_session); migration.SchemaBuilder = schemaBuilder; // copy the object for the Linq query var tempMigration = migration; // get current version for this migration var dataMigrationRecord = GetDataMigrationRecordAsync(tempMigration).Result; var current = 0; if (dataMigrationRecord != null) { current = dataMigrationRecord.Version.Value; } else { dataMigrationRecord = new Records.DataMigration { DataMigrationClass = migration.GetType().FullName }; _dataMigrationRecord.DataMigrations.Add(dataMigrationRecord); } try { // do we need to call Create() ? if (current == 0) { // try to resolve a Create method var createMethod = GetCreateMethod(migration); if (createMethod != null) { current = (int)createMethod.Invoke(migration, new object[0]); } } var lookupTable = CreateUpgradeLookupTable(migration); while (lookupTable.ContainsKey(current)) { try { if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Applying migration for {0} from version {1}.", featureId, current); } current = (int)lookupTable[current].Invoke(migration, new object[0]); } catch (Exception ex) { if (ex.IsFatal()) { throw; } _logger.LogError(0, "An unexpected error occurred while applying migration on {0} from version {1}.", featureId, current); throw; } } // if current is 0, it means no upgrade/create method was found or succeeded if (current == 0) { return; } dataMigrationRecord.Version = current; } catch (Exception ex) { if (ex.IsFatal()) { throw; } _logger.LogError(0, "Error while running migration version {0} for {1}.", current, featureId); _session.Cancel(); throw new Exception(T["Error while running migration version {0} for {1}.", current, featureId], ex); } finally { // Persist data migrations _session.Save(_dataMigrationRecord); } } }
public async Task UpdateAsync(string featureId) { if (_processedFeatures.Contains(featureId)) { return; } _processedFeatures.Add(featureId); if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Updating feature '{FeatureName}'", featureId); } // proceed with dependent features first, whatever the module it's in var dependencies = _extensionManager .GetFeatureDependencies( featureId) .Where(x => x.Id != featureId) .Select(x => x.Id); await UpdateAsync(dependencies); var migrations = GetDataMigrations(featureId); // apply update methods to each migration class for the module foreach (var migration in migrations) { var schemaBuilder = new SchemaBuilder(_store.Configuration, await _session.DemandAsync()); migration.SchemaBuilder = schemaBuilder; // copy the object for the Linq query var tempMigration = migration; // get current version for this migration var dataMigrationRecord = await GetDataMigrationRecordAsync(tempMigration); var current = 0; if (dataMigrationRecord != null) { // This can be null if a failed create migration has occured and the data migration record was saved. current = dataMigrationRecord.Version.HasValue ? dataMigrationRecord.Version.Value : current; } else { dataMigrationRecord = new Records.DataMigration { DataMigrationClass = migration.GetType().FullName }; _dataMigrationRecord.DataMigrations.Add(dataMigrationRecord); } try { // do we need to call Create() ? if (current == 0) { // try to resolve a Create method var createMethod = GetCreateMethod(migration); if (createMethod != null) { current = (int)createMethod.Invoke(migration, new object[0]); } // try to resolve a CreateAsync method var createAsyncMethod = GetCreateAsyncMethod(migration); if (createAsyncMethod != null) { current = await(Task <int>) createAsyncMethod.Invoke(migration, new object[0]); } } var lookupTable = CreateUpgradeLookupTable(migration); while (lookupTable.TryGetValue(current, out var methodInfo)) { if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Applying migration for '{FeatureName}' from version {Version}.", featureId, current); } var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null; if (isAwaitable) { current = await(Task <int>) methodInfo.Invoke(migration, new object[0]); } else { current = (int)methodInfo.Invoke(migration, new object[0]); } } // if current is 0, it means no upgrade/create method was found or succeeded if (current == 0) { return; } dataMigrationRecord.Version = current; } catch (Exception ex) { _logger.LogError(ex, "Error while running migration version {Version} for '{FeatureName}'.", current, featureId); _session.Cancel(); } finally { // Persist data migrations _session.Save(_dataMigrationRecord); } } }