internal static Task <ShellContext> GetDatabaseContextAsync(this IShellContextFactory shellContextFactory, DatabaseShellsStorageOptions options) { if (options.DatabaseProvider == null) { throw new ArgumentNullException(nameof(options.DatabaseProvider), "The 'OrchardCore.Shells.Database' configuration section should define a 'DatabaseProvider'"); } var settings = new ShellSettings() { Name = ShellHelper.DefaultShellName, State = TenantState.Running }; settings["DatabaseProvider"] = options.DatabaseProvider; settings["ConnectionString"] = options.ConnectionString; settings["TablePrefix"] = options.TablePrefix; return(shellContextFactory.CreateDescribedContextAsync(settings, new ShellDescriptor())); }
/// <summary> /// Creates a distributed context based on the default tenant settings and descriptor. /// </summary> private async Task <DistributedContext> CreateDistributedContextAsync(ShellContext defaultShell) { // Capture the descriptor as the blueprint may be set to null right after. var descriptor = defaultShell.Blueprint?.Descriptor; if (descriptor != null) { // Using the current shell descritor prevents a database access, and a race condition // when resolving `IStore` while the default tenant is activating and does migrations. try { return(new DistributedContext(await _shellContextFactory.CreateDescribedContextAsync(defaultShell.Settings, descriptor))); } catch { return(null); } } return(await CreateDistributedContextAsync(defaultShell.Settings)); }
public async Task <string> SetupInternalAsync(SetupContext context) { string executionId; if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Running setup for tenant '{0}'.", _shellSettings.Name); } // Features to enable for Setup string[] hardcoded = { "OrchardCore.Commons", "OrchardCore.Features", "OrchardCore.Recipes", "OrchardCore.Scripting" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); // Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up. _shellSettings.State = TenantState.Initializing; var shellSettings = new ShellSettings(_shellSettings.Configuration); if (string.IsNullOrEmpty(shellSettings.DatabaseProvider)) { shellSettings.DatabaseProvider = context.DatabaseProvider; shellSettings.ConnectionString = context.DatabaseConnectionString; shellSettings.TablePrefix = context.DatabaseTablePrefix; } // Creating a standalone environment based on a "minimum shell descriptor". // In theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect // It is used to initialize the database before the recipe is run. var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(id => new ShellFeature { Id = id }).ToList() }; using (var shellContext = await _shellContextFactory.CreateDescribedContextAsync(shellSettings, shellDescriptor)) { using (var scope = shellContext.EnterServiceScope()) { var store = scope.ServiceProvider.GetRequiredService <IStore>(); try { await store.InitializeAsync(); } catch (Exception e) { // Tables already exist or database was not found // The issue is that the user creation needs the tables to be present, // if the user information is not valid, the next POST will try to recreate the // tables. The tables should be rolled back if one of the steps is invalid, // unless the recipe is executing? _logger.LogError("An error occurred while initializing the datastore.", e); context.Errors.Add("DatabaseProvider", T["An error occurred while initializing the datastore: {0}", e.Message]); return(null); } // Create the "minimum shell descriptor" await scope .ServiceProvider .GetService <IShellDescriptorManager>() .UpdateShellDescriptorAsync(0, shellContext.Blueprint.Descriptor.Features, shellContext.Blueprint.Descriptor.Parameters); var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { var taskContext = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(taskContext); } } executionId = Guid.NewGuid().ToString("n"); // Create a new scope for the recipe thread to prevent race issues with other scoped // services from the request. using (var scope = shellContext.EnterServiceScope()) { var recipeExecutor = scope.ServiceProvider.GetService <IRecipeExecutor>(); // Right now we run the recipe in the same thread, later use polling from the setup screen // to query the current execution. //await Task.Run(async () => //{ await recipeExecutor.ExecuteAsync(executionId, context.Recipe, new { SiteName = context.SiteName, AdminUsername = context.AdminUsername, AdminEmail = context.AdminEmail, AdminPassword = context.AdminPassword, DatabaseProvider = context.DatabaseProvider, DatabaseConnectionString = context.DatabaseConnectionString, DatabaseTablePrefix = context.DatabaseTablePrefix }); //}); } } // Reloading the shell context as the recipe has probably updated its features using (var shellContext = await _orchardHost.CreateShellContextAsync(shellSettings)) { using (var scope = shellContext.EnterServiceScope()) { var hasErrors = false; Action <string, string> reportError = (key, message) => { hasErrors = true; context.Errors[key] = message; }; // Invoke modules to react to the setup event var setupEventHandlers = scope.ServiceProvider.GetServices <ISetupEventHandler>(); var logger = scope.ServiceProvider.GetRequiredService <ILogger <SetupService> >(); await setupEventHandlers.InvokeAsync(x => x.Setup( context.SiteName, context.AdminUsername, context.AdminEmail, context.AdminPassword, context.DatabaseProvider, context.DatabaseConnectionString, context.DatabaseTablePrefix, reportError ), logger); if (hasErrors) { return(executionId); } var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { var taskContext = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(taskContext); } } } // Update the shell state shellSettings.State = TenantState.Running; _orchardHost.UpdateShellSettings(shellSettings); return(executionId); }
public async Task <string> SetupInternalAsync(SetupContext context) { string executionId; if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Running setup for tenant '{TenantName}'.", context.ShellSettings.Name); } // Features to enable for Setup string[] hardcoded = { _applicationName, "OrchardCore.Features", "OrchardCore.Scripting", "OrchardCore.Recipes" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); // Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up. context.ShellSettings.State = TenantState.Initializing; var shellSettings = new ShellSettings(context.ShellSettings); if (string.IsNullOrEmpty(shellSettings["DatabaseProvider"])) { shellSettings["DatabaseProvider"] = context.DatabaseProvider; shellSettings["ConnectionString"] = context.DatabaseConnectionString; shellSettings["TablePrefix"] = context.DatabaseTablePrefix; } if (String.IsNullOrWhiteSpace(shellSettings["DatabaseProvider"])) { throw new ArgumentException($"{nameof(context.DatabaseProvider)} is required"); } // Creating a standalone environment based on a "minimum shell descriptor". // In theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect // It is used to initialize the database before the recipe is run. var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(id => new ShellFeature { Id = id }).ToList() }; using (var shellContext = await _shellContextFactory.CreateDescribedContextAsync(shellSettings, shellDescriptor)) { await shellContext.CreateScope().UsingAsync(async scope => { IStore store; try { store = scope.ServiceProvider.GetRequiredService <IStore>(); } catch (Exception e) { // Tables already exist or database was not found // The issue is that the user creation needs the tables to be present, // if the user information is not valid, the next POST will try to recreate the // tables. The tables should be rolled back if one of the steps is invalid, // unless the recipe is executing? _logger.LogError(e, "An error occurred while initializing the datastore."); context.Errors.Add("DatabaseProvider", T["An error occurred while initializing the datastore: {0}", e.Message]); return; } // Create the "minimum shell descriptor" await scope .ServiceProvider .GetService <IShellDescriptorManager>() .UpdateShellDescriptorAsync(0, shellContext.Blueprint.Descriptor.Features, shellContext.Blueprint.Descriptor.Parameters); }); if (context.Errors.Any()) { return(null); } executionId = Guid.NewGuid().ToString("n"); var recipeExecutor = shellContext.ServiceProvider.GetRequiredService <IRecipeExecutor>(); await recipeExecutor.ExecuteAsync(executionId, context.Recipe, new { SiteName = context.SiteName, AdminUsername = context.AdminUsername, AdminEmail = context.AdminEmail, AdminPassword = context.AdminPassword, DatabaseProvider = context.DatabaseProvider, DatabaseConnectionString = context.DatabaseConnectionString, DatabaseTablePrefix = context.DatabaseTablePrefix }, _applicationLifetime.ApplicationStopping); } // Reloading the shell context as the recipe has probably updated its features using (var shellContext = await _shellHost.CreateShellContextAsync(shellSettings)) { await shellContext.CreateScope().UsingAsync(async scope => { void reportError(string key, string message) { context.Errors[key] = message; } // Invoke modules to react to the setup event var setupEventHandlers = scope.ServiceProvider.GetServices <ISetupEventHandler>(); var logger = scope.ServiceProvider.GetRequiredService <ILogger <SetupService> >(); await setupEventHandlers.InvokeAsync(x => x.Setup( context.SiteName, context.AdminUsername, context.AdminEmail, context.AdminPassword, context.DatabaseProvider, context.DatabaseConnectionString, context.DatabaseTablePrefix, context.SiteTimeZone, reportError ), logger); }); if (context.Errors.Any()) { return(executionId); } } // Update the shell state shellSettings.State = TenantState.Running; await _shellHost.UpdateShellSettingsAsync(shellSettings); return(executionId); }
public async Task <string> SetupInternalAsync(SetupContext context) { string executionId; if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Running setup for tenant '{TenantName}'.", context.ShellSettings.Name); } string[] hardcoded = { _applicationName, "SeedModules.Features", "OrchardCore.Scripting", "SeedModules.Recipes", }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList(); context.ShellSettings.State = TenantState.Initializing; var shellSettings = new ShellSettings(context.ShellSettings); if (string.IsNullOrEmpty(shellSettings["DatabaseProvider"])) { shellSettings["DatabaseProvider"] = context.DatabaseProvider; shellSettings["ConnectionString"] = context.DatabaseConnectionString; shellSettings["TablePrefix"] = context.DatabaseTablePrefix; } if (String.IsNullOrWhiteSpace(shellSettings["DatabaseProvider"])) { throw new ArgumentException($"{nameof(context.DatabaseProvider)} is required"); } var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(id => new ShellFeature { Id = id }).ToList() }; using (var shellContext = await _shellContextFactory.CreateDescribedContextAsync(shellSettings, shellDescriptor)) { await shellContext.CreateScope().UsingAsync(async scope => { IStore store; try { store = scope.ServiceProvider.GetRequiredService <IStore>(); // await store.InitializeAsync(scope.ServiceProvider); } catch (Exception e) { // 表已存在或数据库问题 _logger.LogError(e, "An error occurred while initializing the datastore."); context.Errors.Add("DatabaseProvider", T["An error occurred while initializing the datastore: {0}", e.Message]); return; } await scope .ServiceProvider .GetService <IShellDescriptorManager>() .UpdateShellDescriptorAsync(0, shellContext.Blueprint.Descriptor.Features, shellContext.Blueprint.Descriptor.Parameters); }); if (context.Errors.Any()) { return(null); } executionId = Guid.NewGuid().ToString("n"); var recipeExecutor = shellContext.ServiceProvider.GetRequiredService <IRecipeExecutor>(); await recipeExecutor.ExecuteAsync(executionId, context.Recipe, new { SiteName = context.SiteName, AdminUsername = context.AdminUsername, AdminEmail = context.AdminEmail, AdminPassword = context.AdminPassword, DatabaseProvider = context.DatabaseProvider, DatabaseConnectionString = context.DatabaseConnectionString, DatabaseTablePrefix = context.DatabaseTablePrefix }, _applicationLifetime.ApplicationStopping); } await(await _shellHost.GetScopeAsync(shellSettings)).UsingAsync(async scope => { void reportError(string key, string message) { context.Errors[key] = message; } // Invoke modules to react to the setup event var setupEventHandlers = scope.ServiceProvider.GetServices <ISetupEventHandler>(); var logger = scope.ServiceProvider.GetRequiredService <ILogger <SetupService> >(); await setupEventHandlers.InvokeAsync((handler, context) => handler.Setup( context.SiteName, context.AdminUsername, context.AdminEmail, context.AdminPassword, context.DatabaseProvider, context.DatabaseConnectionString, context.DatabaseTablePrefix, context.SiteTimeZone, reportError ), context, logger); }); if (context.Errors.Any()) { return(executionId); } shellSettings.State = TenantState.Running; await _shellHost.UpdateShellSettingsAsync(shellSettings); return(executionId); }
public async Task <string> SetupInternalAsync(SetupContext context) { string executionId; if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Running setup for tenant '{TenantName}'.", context.ShellSettings.Name); } // Features to enable for Setup string[] hardcoded = { _applicationName, //TODO }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToArray(); // Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up. context.ShellSettings.State = TenantState.Initializing; var shellSettings = new ShellSettings(context.ShellSettings); shellSettings["ConnectionString"] = context.DatabaseConnectionString; // Creating a standalone environment based on a "minimum shell descriptor". // In theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect // It is used to initialize the database before the recipe is run. var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(id => new ShellFeature { Id = id }).ToList() }; using (var shellContext = await _shellContextFactory.CreateDescribedContextAsync(shellSettings, shellDescriptor)) { using (var scope = shellContext.CreateScope()) { var hasErrors = false; void reportError(string key, string message) { hasErrors = true; context.SetError(key, message); } try { await this.InitializeDatabaseForTenantAsync(context, scope, reportError); if (hasErrors) { _logger.LogError("An error occurred while initializing the tenant {0}", context.DbName); return(null); } } catch (Exception e) { // Tables already exist or database was not found // The issue is that the user creation needs the tables to be present, // if the user information is not valid, the next POST will try to recreate the // tables. The tables should be rolled back if one of the steps is invalid, // unless the recipe is executing? _logger.LogError(e, "An error occurred while initializing the datastore."); context.SetError("DatabaseProvider", T["An error occurred while initializing the datastore: {0}", e.Message]); return(null); } // Create the "minimum shell descriptor" await scope .ServiceProvider .GetService <IShellDescriptorManager>() .UpdateShellDescriptorAsync(0, shellContext.Blueprint.Descriptor.Features); // TODO FIXME /* * var deferredTaskEngine = scope.ServiceProvider.GetService<IDeferredTaskEngine>(); * * if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { * var taskContext = new DeferredTaskContext(scope.ServiceProvider); * await deferredTaskEngine.ExecuteTasksAsync(taskContext); * } */ } executionId = Guid.NewGuid().ToString("n"); /* * var recipeExecutor = shellContext.ServiceProvider.GetRequiredService<IRecipeExecutor>(); * * await recipeExecutor.ExecuteAsync(executionId, context.Recipe, new * { * SiteName = context.SiteName, * AdminUsername = context.AdminUsername, * AdminEmail = context.AdminEmail, * AdminPassword = context.AdminPassword, * DatabaseProvider = context.DatabaseProvider, * DatabaseConnectionString = context.DatabaseConnectionString, * DatabaseTablePrefix = context.DatabaseTablePrefix * }, * * _applicationLifetime.ApplicationStopping); */ } // Reloading the shell context as the recipe has probably updated its features using (var shellContext = await _shellHost.GetOrCreateShellContextAsync(shellSettings)) { using (var scope = shellContext.CreateScope()) { var hasErrors = false; void reportError(string key, string message) { hasErrors = true; context.SetError(key, message); } // Invoke modules to react to the setup event var setupEventHandlers = scope.ServiceProvider.GetServices <ISetupEventHandler>(); var logger = scope.ServiceProvider.GetRequiredService <ILogger <SetupService> >(); //Notify all registered event handlers await setupEventHandlers.InvokeAsync(x => x.Setup(context, reportError), logger); if (hasErrors) { return(executionId); } /* TODO FIXME * var deferredTaskEngine = scope.ServiceProvider.GetService<IDeferredTaskEngine>(); * * if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { * var taskContext = new DeferredTaskContext(scope.ServiceProvider); * await deferredTaskEngine.ExecuteTasksAsync(taskContext); * } */ } } // Update the shell state shellSettings.State = TenantState.Running; await _shellHost.UpdateShellSettingsAsync(shellSettings); return(executionId); }