public virtual async Task <InstallationResult> InstallAsync(InstallationModel model, ILifetimeScope scope, CancellationToken cancelToken = default) { Guard.NotNull(model, nameof(model)); UpdateResult(x => { x.ProgressMessage = GetResource("Progress.CheckingRequirements"); x.Completed = false; Logger.Info(x.ProgressMessage); }); if (DataSettings.DatabaseIsInstalled()) { return(UpdateResult(x => { x.Success = true; x.RedirectUrl = _urlHelper.Action("Index", "Home"); Logger.Info("Application already installed"); })); } DbFactory dbFactory = null; try { dbFactory = DbFactory.Load(model.DataProvider, _appContext.TypeScanner); } catch (Exception ex) { return(UpdateResult(x => { x.Errors.Add(ex.Message); Logger.Error(ex); })); } model.DbRawConnectionString = model.DbRawConnectionString?.Trim(); DbConnectionStringBuilder conStringBuilder = null; try { // Try to create connection string if (model.UseRawConnectionString) { conStringBuilder = dbFactory.CreateConnectionStringBuilder(model.DbRawConnectionString); } else { // Structural connection string var userId = model.DbUserId; var password = model.DbPassword; if (model.DataProvider == "sqlserver" && model.DbAuthType == "windows") { userId = null; password = null; } conStringBuilder = dbFactory.CreateConnectionStringBuilder(model.DbServer, model.DbName, userId, password); } } catch (Exception ex) { return(UpdateResult(x => { x.Errors.Add(GetResource("ConnectionStringWrongFormat")); Logger.Error(ex, x.Errors.Last()); })); } // Check FS access rights CheckFileSystemAccessRights(GetInstallResult().Errors); if (GetInstallResult().HasErrors) { return(UpdateResult(x => { x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error("Aborting installation."); })); } ILifetimeScope richScope = null; SmartDbContext dbContext = null; var shouldDeleteDbOnFailure = false; try { cancelToken.ThrowIfCancellationRequested(); var conString = conStringBuilder.ConnectionString; var settings = DataSettings.Instance; settings.AppVersion = SmartstoreVersion.Version; settings.DbFactory = dbFactory; settings.ConnectionString = conString; // So that DataSettings.DatabaseIsInstalled() returns false during installation. DataSettings.SetTestMode(true); // Resolve SeedData instance from primary language var lazyLanguage = GetAppLanguage(model.PrimaryLanguage); if (lazyLanguage == null) { return(UpdateResult(x => { x.Errors.Add(GetResource("Install.LanguageNotRegistered").FormatInvariant(model.PrimaryLanguage)); x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error(x.Errors.Last()); })); } // Create the DataContext dbContext = (SmartDbContext)dbFactory.CreateApplicationDbContext( conString, _appContext.AppConfiguration.DbMigrationCommandTimeout, SmartDbContext.MigrationHistoryTableName); // Delete only on failure if WE created the database. var canConnectDatabase = await dbContext.Database.CanConnectAsync(cancelToken); shouldDeleteDbOnFailure = !canConnectDatabase; cancelToken.ThrowIfCancellationRequested(); // Create Language domain object from lazyLanguage var languages = dbContext.Languages; var primaryLanguage = new Language { Name = lazyLanguage.Metadata.Name, LanguageCulture = lazyLanguage.Metadata.Culture, UniqueSeoCode = lazyLanguage.Metadata.UniqueSeoCode, FlagImageFileName = lazyLanguage.Metadata.FlagImageFileName }; // Build the seed configuration model var seedConfiguration = new SeedDataConfiguration { DefaultUserName = model.AdminEmail, DefaultUserPassword = model.AdminPassword, SeedSampleData = model.InstallSampleData, Data = lazyLanguage.Value, Language = primaryLanguage, StoreMediaInDB = model.MediaStorage == "db", ProgressMessageCallback = msg => UpdateResult(x => x.ProgressMessage = GetResource(msg)) }; var seeder = new InstallationDataSeeder(seedConfiguration, Logger, _httpContextAccessor); UpdateResult(x => { x.ProgressMessage = GetResource("Progress.BuildingDatabase"); Logger.Info(x.ProgressMessage); }); //// TEST //return UpdateResult(x => //{ // x.Completed = true; // x.Success = true; // //x.RedirectUrl = _urlHelper.Action("Index", "Home"); // Logger.Info("Installation completed successfully"); //}); // ===>>> Actually performs database creation. await dbContext.Database.MigrateAsync(cancelToken); cancelToken.ThrowIfCancellationRequested(); // ===>>> Seeds data. await seeder.SeedAsync(dbContext); cancelToken.ThrowIfCancellationRequested(); // ... Install modules // Detect media file tracks (must come after plugins installation) UpdateResult(x => { x.ProgressMessage = GetResource("Progress.ProcessingMedia"); Logger.Info(x.ProgressMessage); }); richScope = scope.BeginLifetimeScope(c => { // At this stage (after the database has been created and seeded completely) we can create a richer service scope // to minimize the risk of dependency resolution exceptions during more complex install operations. c.RegisterInstance(dbContext); c.Register <IStoreContext>(cc => new StoreContext(cc.Resolve <ICacheFactory>(), null, _httpContextAccessor, cc.Resolve <IActionContextAccessor>())); c.Register <ISettingFactory>(cc => new SettingFactory(cc.Resolve <ICacheManager>(), null, _httpContextAccessor)); }); var mediaTracker = richScope.Resolve <IMediaTracker>(); foreach (var album in richScope.Resolve <IAlbumRegistry>().GetAlbumNames(true)) { await mediaTracker.DetectAllTracksAsync(album, cancelToken); } cancelToken.ThrowIfCancellationRequested(); UpdateResult(x => { x.ProgressMessage = GetResource("Progress.Finalizing"); Logger.Info(x.ProgressMessage); }); // Now persist settings settings.Save(); // SUCCESS: Redirect to home page return(UpdateResult(x => { x.Completed = true; x.Success = true; x.RedirectUrl = _urlHelper.Action("Index", "Home"); Logger.Info("Installation completed successfully"); })); } catch (Exception ex) { Logger.Error(ex); // Delete Db if it was auto generated if (dbContext != null && shouldDeleteDbOnFailure) { try { Logger.Debug("Deleting database"); await dbContext.Database.EnsureDeletedAsync(cancelToken); } catch { } } // Clear provider settings if something got wrong DataSettings.Delete(); var msg = ex.Message; var realException = ex; while (realException.InnerException != null) { realException = realException.InnerException; } if (!object.Equals(ex, realException)) { msg += " (" + realException.Message + ")"; } return(UpdateResult(x => { x.Errors.Add(string.Format(GetResource("SetupFailed"), msg)); x.Success = false; x.Completed = true; x.RedirectUrl = null; })); } finally { if (dbContext != null) { dbContext.Dispose(); } if (richScope != null) { richScope.Dispose(); } } }
protected virtual InstallationResult InstallCore(ILifetimeScope scope, InstallModel model) { UpdateResult(x => { x.ProgressMessage = _locService.GetResource("Progress.CheckingRequirements"); x.Completed = false; Logger.Info(x.ProgressMessage); }); if (DataSettings.DatabaseIsInstalled()) { return(UpdateResult(x => { x.Success = true; x.RedirectUrl = Url.Action("Index", "Home"); Logger.Info("Application already installed"); })); } // set page timeout to 5 minutes this.Server.ScriptTimeout = 300; if (model.DatabaseConnectionString != null) { model.DatabaseConnectionString = model.DatabaseConnectionString.Trim(); } // SQL Server if (model.DataProvider.Equals("sqlserver", StringComparison.InvariantCultureIgnoreCase)) { if (model.SqlConnectionInfo.Equals("sqlconnectioninfo_raw", StringComparison.InvariantCultureIgnoreCase)) { // raw connection string if (string.IsNullOrEmpty(model.DatabaseConnectionString)) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("ConnectionStringRequired")); Logger.Error(x.Errors.Last()); }); } try { // try to create connection string new SqlConnectionStringBuilder(model.DatabaseConnectionString); } catch (Exception ex) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("ConnectionStringWrongFormat")); Logger.Error(ex, x.Errors.Last()); }); } } else { // values if (string.IsNullOrEmpty(model.SqlServerName)) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("SqlServerNameRequired")); Logger.Error(x.Errors.Last()); }); } if (string.IsNullOrEmpty(model.SqlDatabaseName)) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("DatabaseNameRequired")); Logger.Error(x.Errors.Last()); }); } // authentication type if (model.SqlAuthenticationType.Equals("sqlauthentication", StringComparison.InvariantCultureIgnoreCase)) { // SQL authentication if (string.IsNullOrEmpty(model.SqlServerUsername)) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("SqlServerUsernameRequired")); Logger.Error(x.Errors.Last()); }); } if (string.IsNullOrEmpty(model.SqlServerPassword)) { UpdateResult(x => { x.Errors.Add(_locService.GetResource("SqlServerPasswordRequired")); Logger.Error(x.Errors.Last()); }); } } } } // Consider granting access rights to the resource to the ASP.NET request identity. // ASP.NET has a base process identity // (typically {MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6 and IIS 7, // and the configured application pool identity on IIS 7.5) that is used if the application is not impersonating. // If the application is impersonating via <identity impersonate="true"/>, // the identity will be the anonymous user (typically IUSR_MACHINENAME) or the authenticated request user. var webHelper = scope.Resolve <IWebHelper>(); // validate permissions var dirsToCheck = FilePermissionHelper.GetDirectoriesWrite(webHelper); foreach (string dir in dirsToCheck) { if (!FilePermissionHelper.CheckPermissions(dir, false, true, true, false)) { UpdateResult(x => { x.Errors.Add(string.Format(_locService.GetResource("ConfigureDirectoryPermissions"), WindowsIdentity.GetCurrent().Name, dir)); Logger.Error(x.Errors.Last()); }); } } var filesToCheck = FilePermissionHelper.GetFilesWrite(webHelper); foreach (string file in filesToCheck) { if (!FilePermissionHelper.CheckPermissions(file, false, true, true, true)) { UpdateResult(x => { x.Errors.Add(string.Format(_locService.GetResource("ConfigureFilePermissions"), WindowsIdentity.GetCurrent().Name, file)); Logger.Error(x.Errors.Last()); }); } } if (GetInstallResult().HasErrors) { return(UpdateResult(x => { x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error("Aborting installation."); })); } else { SmartObjectContext dbContext = null; var shouldDeleteDbOnFailure = false; try { string connectionString = null; if (model.DataProvider.Equals("sqlserver", StringComparison.InvariantCultureIgnoreCase)) { //SQL Server if (model.SqlConnectionInfo.Equals("sqlconnectioninfo_raw", StringComparison.InvariantCultureIgnoreCase)) { //raw connection string //we know that MARS option is required when using Entity Framework //let's ensure that it's specified var sqlCsb = new SqlConnectionStringBuilder(model.DatabaseConnectionString); sqlCsb.MultipleActiveResultSets = true; connectionString = sqlCsb.ToString(); } else { // values connectionString = CreateConnectionString( model.SqlAuthenticationType == "windowsauthentication", model.SqlServerName, model.SqlDatabaseName, model.SqlServerUsername, model.SqlServerPassword); } if (model.SqlServerCreateDatabase) { if (!SqlServerDatabaseExists(connectionString)) { //create database var collation = model.UseCustomCollation ? model.Collation : ""; var errorCreatingDatabase = CreateDatabase(connectionString, collation); if (errorCreatingDatabase.HasValue()) { return(UpdateResult(x => { x.Errors.Add(errorCreatingDatabase); x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error(errorCreatingDatabase); })); } else { // Database cannot be created sometimes. Weird! Seems to be Entity Framework issue // that's just wait 3 seconds Thread.Sleep(3000); shouldDeleteDbOnFailure = true; } } } else { // check whether database exists if (!SqlServerDatabaseExists(connectionString)) { return(UpdateResult(x => { x.Errors.Add(_locService.GetResource("DatabaseNotExists")); x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error(x.Errors.Last()); })); } } } else { // SQL CE string databaseFileName = "SmartStore.Db.sdf"; string databasePath = @"|DataDirectory|\Tenants\{0}\{1}".FormatInvariant(DataSettings.Current.TenantName, databaseFileName); connectionString = "Data Source=" + databasePath + "; Persist Security Info=False"; // drop database if exists string databaseFullPath = HostingEnvironment.MapPath(DataSettings.Current.TenantPath.EnsureEndsWith("/")) + databaseFileName; if (System.IO.File.Exists(databaseFullPath)) { System.IO.File.Delete(databaseFullPath); } shouldDeleteDbOnFailure = true; } // save settings var dataProvider = model.DataProvider; var settings = DataSettings.Current; settings.AppVersion = SmartStoreVersion.Version; settings.DataProvider = dataProvider; settings.DataConnectionString = connectionString; settings.Save(); // init data provider var dataProviderInstance = scope.Resolve <IEfDataProvider>(); // Although obsolete we have no other chance than using this here. // Delegating this to DbConfiguration is not possible during installation. #pragma warning disable 618 Database.DefaultConnectionFactory = dataProviderInstance.GetConnectionFactory(); #pragma warning restore 618 // resolve SeedData instance from primary language var lazyLanguage = _locService.GetAppLanguage(model.PrimaryLanguage); if (lazyLanguage == null) { return(UpdateResult(x => { x.Errors.Add(_locService.GetResource("Install.LanguageNotRegistered").FormatInvariant(model.PrimaryLanguage)); x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error(x.Errors.Last()); })); } // create the DataContext dbContext = new SmartObjectContext(); // AuditableHook must run during install dbContext.DbHookHandler = new DefaultDbHookHandler(new[] { new Lazy <IDbHook, HookMetadata>(() => new AuditableHook(), HookMetadata.Create <AuditableHook>(typeof(IAuditable), true), false) }); // IMPORTANT: Migration would run way too early otherwise Database.SetInitializer <SmartObjectContext>(null); // create Language domain object from lazyLanguage var languages = dbContext.Set <Language>(); var primaryLanguage = languages.Create(); // create a proxied type, resources cannot be saved otherwise primaryLanguage.Name = lazyLanguage.Metadata.Name; primaryLanguage.LanguageCulture = lazyLanguage.Metadata.Culture; primaryLanguage.UniqueSeoCode = lazyLanguage.Metadata.UniqueSeoCode; primaryLanguage.FlagImageFileName = lazyLanguage.Metadata.FlagImageFileName; // Build the seed configuration model var seedConfiguration = new SeedDataConfiguration { DefaultUserName = model.AdminEmail, DefaultUserPassword = model.AdminPassword, SeedSampleData = model.InstallSampleData, Data = lazyLanguage.Value, Language = primaryLanguage, StoreMediaInDB = model.MediaStorage == "db", ProgressMessageCallback = msg => UpdateResult(x => x.ProgressMessage = _locService.GetResource(msg)) }; var seeder = new InstallDataSeeder(seedConfiguration, Logger); Database.SetInitializer(new InstallDatabaseInitializer() { DataSeeders = new[] { seeder } }); UpdateResult(x => { x.ProgressMessage = _locService.GetResource("Progress.BuildingDatabase"); Logger.Info(x.ProgressMessage); }); // ===>>> actually performs the installation by calling "InstallDataSeeder.Seed()" internally. dbContext.Database.Initialize(true); // Install plugins. PluginManager.MarkAllPluginsAsUninstalled(); var pluginFinder = scope.Resolve <IPluginFinder>(); var plugins = pluginFinder.GetPlugins <IPlugin>(false) //.ToList() .OrderBy(x => x.PluginDescriptor.Group) .ThenBy(x => x.PluginDescriptor.DisplayOrder) .ToList(); var ignoredPluginsSetting = CommonHelper.GetAppSetting <string>("sm:PluginsIgnoredDuringInstallation"); var pluginsIgnoredDuringInstallation = String.IsNullOrEmpty(ignoredPluginsSetting) ? new List <string>() : ignoredPluginsSetting .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .ToList(); if (pluginsIgnoredDuringInstallation.Count > 0) { plugins = plugins.Where(x => !pluginsIgnoredDuringInstallation.Contains(x.PluginDescriptor.SystemName, StringComparer.OrdinalIgnoreCase)).ToList(); } var pluginsCount = plugins.Count; var idx = 0; using (var dbScope = new DbContextScope(autoDetectChanges: false, hooksEnabled: false)) { foreach (var plugin in plugins) { try { idx++; UpdateResult(x => { x.ProgressMessage = _locService.GetResource("Progress.InstallingPlugins").FormatInvariant(idx, pluginsCount); Logger.InfoFormat("Installing plugin '{0}'.", plugin.PluginDescriptor.FriendlyName ?? plugin.PluginDescriptor.SystemName); }); plugin.Install(); dbScope.Commit(); } catch (Exception ex) { Logger.Error(ex); if (plugin.PluginDescriptor.Installed) { PluginManager.MarkPluginAsUninstalled(plugin.PluginDescriptor.SystemName); } } } } // Detect media file tracks (must come after plugins installation) UpdateResult(x => { x.ProgressMessage = _locService.GetResource("Progress.ProcessingMedia"); Logger.Info(x.ProgressMessage); }); var mediaTracker = scope.Resolve <IMediaTracker>(); foreach (var album in scope.Resolve <IAlbumRegistry>().GetAlbumNames(true)) { mediaTracker.DetectAllTracks(album, false); } UpdateResult(x => { x.ProgressMessage = _locService.GetResource("Progress.Finalizing"); Logger.Info(x.ProgressMessage); }); // Do not ignore settings migrated by data seeder (e.g. default media storage provider). scope.Resolve <ISettingService>().ClearCache(); // SUCCESS: Redirect to home page return(UpdateResult(x => { x.Completed = true; x.Success = true; x.RedirectUrl = Url.Action("Index", "Home"); Logger.Info("Installation completed successfully"); })); } catch (Exception ex) { Logger.Error(ex); // Clear provider settings if something got wrong DataSettings.Delete(); // Delete Db if it was auto generated if (dbContext != null && shouldDeleteDbOnFailure) { try { Logger.Debug("Deleting database"); dbContext.Database.Delete(); } catch { } } var msg = ex.Message; var realException = ex; while (realException.InnerException != null) { realException = realException.InnerException; } if (!Object.Equals(ex, realException)) { msg += " (" + realException.Message + ")"; } return(UpdateResult(x => { x.Errors.Add(string.Format(_locService.GetResource("SetupFailed"), msg)); x.Success = false; x.Completed = true; x.RedirectUrl = null; })); } finally { if (dbContext != null) { dbContext.Dispose(); } } } }
public virtual async Task <InstallationResult> InstallAsync(InstallationModel model, ILifetimeScope scope) { // TODO: (core) CancellationToken Guard.NotNull(model, nameof(model)); UpdateResult(x => { x.ProgressMessage = GetResource("Progress.CheckingRequirements"); x.Completed = false; Logger.Info(x.ProgressMessage); }); if (DataSettings.DatabaseIsInstalled()) { return(UpdateResult(x => { x.Success = true; x.RedirectUrl = _urlHelper.Action("Index", "Home"); Logger.Info("Application already installed"); })); } //// set page timeout to 5 minutes //this.Server.ScriptTimeout = 300; DbFactory dbFactory = null; try { dbFactory = DbFactory.Load(model.DataProvider, _appContext.TypeScanner); } catch { return(UpdateResult(x => { x.Errors.Add(GetResource("ConnectionStringRequired")); // TODO: (core) ErrMessage Logger.Error(x.Errors.Last()); })); } model.DatabaseConnectionString = model.DatabaseConnectionString?.Trim(); DbConnectionStringBuilder conStringBuilder = null; if (model.SqlConnectionInfo.EqualsNoCase("sqlconnectioninfo_raw")) { // Raw connection string if (string.IsNullOrEmpty(model.DatabaseConnectionString)) { return(UpdateResult(x => { x.Errors.Add(GetResource("ConnectionStringRequired")); Logger.Error(x.Errors.Last()); })); } try { // Try to create connection string conStringBuilder = dbFactory.CreateConnectionStringBuilder(model.DatabaseConnectionString); } catch (Exception ex) { return(UpdateResult(x => { x.Errors.Add(GetResource("ConnectionStringWrongFormat")); Logger.Error(ex, x.Errors.Last()); })); } } else { // Structural connection string // TODO: (core) ... } // TODO: (core) Access rights check ... if (GetInstallResult().HasErrors) { return(UpdateResult(x => { x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error("Aborting installation."); })); } SmartDbContext dbContext = null; var shouldDeleteDbOnFailure = false; try { var conString = conStringBuilder.ConnectionString; var settings = DataSettings.Instance; settings.AppVersion = SmartstoreVersion.Version; settings.DbFactory = dbFactory; settings.ConnectionString = conString; // So that DataSettings.DatabaseIsInstalled() returns false during installation. DataSettings.SetTestMode(true); // resolve SeedData instance from primary language var lazyLanguage = GetAppLanguage(model.PrimaryLanguage); if (lazyLanguage == null) { return(UpdateResult(x => { x.Errors.Add(GetResource("Install.LanguageNotRegistered").FormatInvariant(model.PrimaryLanguage)); x.Completed = true; x.Success = false; x.RedirectUrl = null; Logger.Error(x.Errors.Last()); })); } // Create the DataContext dbContext = (SmartDbContext)dbFactory.CreateApplicationDbContext( conString, _appContext.AppConfiguration.DbMigrationCommandTimeout, HistoryRepository.DefaultTableName + "_Core"); // TODO: (core) Make const for core migration table name // Delete only on failure if WE created the database. shouldDeleteDbOnFailure = !await dbContext.Database.CanConnectAsync(); // Create Language domain object from lazyLanguage var languages = dbContext.Languages; var primaryLanguage = new Language { Name = lazyLanguage.Metadata.Name, LanguageCulture = lazyLanguage.Metadata.Culture, UniqueSeoCode = lazyLanguage.Metadata.UniqueSeoCode, FlagImageFileName = lazyLanguage.Metadata.FlagImageFileName }; // Build the seed configuration model var seedConfiguration = new SeedDataConfiguration { DefaultUserName = model.AdminEmail, DefaultUserPassword = model.AdminPassword, SeedSampleData = model.InstallSampleData, Data = lazyLanguage.Value, Language = primaryLanguage, StoreMediaInDB = model.MediaStorage == "db", ProgressMessageCallback = msg => UpdateResult(x => x.ProgressMessage = GetResource(msg)) }; var seeder = new InstallationDataSeeder(seedConfiguration, Logger, _httpContextAccessor); UpdateResult(x => { x.ProgressMessage = GetResource("Progress.BuildingDatabase"); Logger.Info(x.ProgressMessage); }); // ===>>> Actually performs database creation. await dbContext.Database.MigrateAsync(); // ===>>> Seeds data. await seeder.SeedAsync(dbContext); // ... // Detect media file tracks (must come after plugins installation) UpdateResult(x => { x.ProgressMessage = GetResource("Progress.ProcessingMedia"); Logger.Info(x.ProgressMessage); }); using (var scope2 = scope.BeginLifetimeScope(c => { c.RegisterInstance(dbContext); c.Register <IStoreContext>(cc => new StoreContext(cc.Resolve <ICacheFactory>(), null, _httpContextAccessor, cc.Resolve <IActionContextAccessor>())); c.Register <ISettingFactory>(cc => new SettingFactory(cc.Resolve <ICacheManager>(), null, _httpContextAccessor)); })) { var mediaTracker = scope2.Resolve <IMediaTracker>(); foreach (var album in scope2.Resolve <IAlbumRegistry>().GetAlbumNames(true)) { await mediaTracker.DetectAllTracksAsync(album); } } UpdateResult(x => { x.ProgressMessage = GetResource("Progress.Finalizing"); Logger.Info(x.ProgressMessage); }); // Now persist settings settings.Save(); // SUCCESS: Redirect to home page return(UpdateResult(x => { x.Completed = true; x.Success = true; x.RedirectUrl = _urlHelper.Action("Index", "Home"); Logger.Info("Installation completed successfully"); })); } catch (Exception ex) { Logger.Error(ex); // Delete Db if it was auto generated if (dbContext != null && shouldDeleteDbOnFailure) { try { Logger.Debug("Deleting database"); await dbContext.Database.EnsureDeletedAsync(); } catch { } } // Clear provider settings if something got wrong DataSettings.Delete(); var msg = ex.Message; var realException = ex; while (realException.InnerException != null) { realException = realException.InnerException; } if (!object.Equals(ex, realException)) { msg += " (" + realException.Message + ")"; } return(UpdateResult(x => { x.Errors.Add(string.Format(GetResource("SetupFailed"), msg)); x.Success = false; x.Completed = true; x.RedirectUrl = null; })); } finally { if (dbContext != null) { dbContext.Dispose(); } } }