From 12bb68100a3fad8c7ac9849a39d49cb431ea1361 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 8 May 2020 17:30:30 +1000 Subject: [PATCH] Remove ununsed and unneeded `DbProviderFactory CreateFactory();` from IDbProviderFactoryCreator, ensure the app doesn't explode if exceptions are thrown and boot fails --- .../Persistence/DbProviderFactoryCreator.cs | 6 +-- .../Persistence/IDbProviderFactoryCreator.cs | 1 - .../SqlServerDbProviderFactoryCreator.cs | 6 +-- .../Runtime/CoreRuntime.cs | 3 ++ .../Implementations/TestHelper.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 2 +- .../UmbracoApplicationBuilderExtensions.cs | 40 ++++++++++++++----- .../UmbracoCoreServiceCollectionExtensions.cs | 5 ++- ...racoRequestApplicationBuilderExtensions.cs | 13 +++--- src/Umbraco.Web.UI.NetCore/Startup.cs | 16 ++------ ...racoWebsiteApplicationBuilderExtensions.cs | 11 +++-- src/Umbraco.Web/UmbracoApplication.cs | 2 +- .../UmbracoDbProviderFactoryCreator.cs | 11 +---- 13 files changed, 63 insertions(+), 55 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs index 8ec45d8403..9ad33d7d88 100644 --- a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs @@ -8,27 +8,23 @@ namespace Umbraco.Core.Persistence { public class DbProviderFactoryCreator : IDbProviderFactoryCreator { - private readonly string _defaultProviderName; private readonly Func _getFactory; private readonly IDictionary _embeddedDatabaseCreators; private readonly IDictionary _syntaxProviders; private readonly IDictionary _bulkSqlInsertProviders; - public DbProviderFactoryCreator(string defaultProviderName, + public DbProviderFactoryCreator( Func getFactory, IEnumerable syntaxProviders, IEnumerable bulkSqlInsertProviders, IEnumerable embeddedDatabaseCreators) { - _defaultProviderName = defaultProviderName; _getFactory = getFactory; _embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x=>x.ProviderName); _syntaxProviders = syntaxProviders.ToDictionary(x=>x.ProviderName); _bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x=>x.ProviderName); } - public DbProviderFactory CreateFactory() => CreateFactory(_defaultProviderName); - public DbProviderFactory CreateFactory(string providerName) { if (string.IsNullOrEmpty(providerName)) return null; diff --git a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs index 2474b1df40..4059997abc 100644 --- a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs @@ -6,7 +6,6 @@ namespace Umbraco.Core.Persistence public interface IDbProviderFactoryCreator { - DbProviderFactory CreateFactory(); DbProviderFactory CreateFactory(string providerName); ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName); IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs index d81067f23c..68d0ec3d90 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs @@ -6,17 +6,13 @@ namespace Umbraco.Core.Persistence { public class SqlServerDbProviderFactoryCreator : IDbProviderFactoryCreator { - private readonly string _defaultProviderName; private readonly Func _getFactory; - public SqlServerDbProviderFactoryCreator(string defaultProviderName, Func getFactory) + public SqlServerDbProviderFactoryCreator(Func getFactory) { - _defaultProviderName = defaultProviderName; _getFactory = getFactory; } - public DbProviderFactory CreateFactory() => CreateFactory(_defaultProviderName); - public DbProviderFactory CreateFactory(string providerName) { if (string.IsNullOrEmpty(providerName)) return null; diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 32e646afca..1b400c3506 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -241,6 +241,9 @@ namespace Umbraco.Core.Runtime public void Start() { + if (_state.Level <= RuntimeLevel.BootFailed) + throw new InvalidOperationException($"Cannot start the runtime if the runtime level is less than or equal to {RuntimeLevel.BootFailed}"); + // throws if not full-trust _umbracoBootPermissionChecker.ThrowIfNotPermissions(); diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 8a660b490b..7b6d8c864c 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -93,7 +93,7 @@ namespace Umbraco.Tests.Integration.Implementations public IWebHostEnvironment GetWebHostEnvironment() => _hostEnvironment; public override IDbProviderFactoryCreator DbProviderFactoryCreator => - new SqlServerDbProviderFactoryCreator(Constants.DbProviderNames.SqlServer, DbProviderFactories.GetFactory); + new SqlServerDbProviderFactoryCreator(DbProviderFactories.GetFactory); public override IBulkSqlInsertProvider BulkSqlInsertProvider => new SqlServerBulkSqlInsertProvider(); diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index cd4461bd24..5512f50254 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.TestHelpers } - public override IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(Constants.DbProviderNames.SqlCe); + public override IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(); public override IBulkSqlInsertProvider BulkSqlInsertProvider { get; } = new SqlCeBulkSqlInsertProvider(); diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs index a27113e881..4fddcb032a 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs @@ -17,6 +17,12 @@ namespace Umbraco.Web.BackOffice.AspNetCore { if (app == null) throw new ArgumentNullException(nameof(app)); + var runtime = app.ApplicationServices.GetRequiredService(); + // can't continue if boot failed + if (runtime.State.Level <= RuntimeLevel.BootFailed) return app; + + // TODO: start the back office + return app; } @@ -29,18 +35,26 @@ namespace Umbraco.Web.BackOffice.AspNetCore { if (app == null) throw new ArgumentNullException(nameof(app)); - // Register a listener for application shutdown in order to terminate the runtime - var hostLifetime = app.ApplicationServices.GetRequiredService(); var runtime = app.ApplicationServices.GetRequiredService(); - var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime); - hostLifetime.RegisterObject(runtimeShutdown); - // Register our global threadabort enricher for logging - var threadAbortEnricher = app.ApplicationServices.GetRequiredService(); - LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context + if (runtime.State.Level > RuntimeLevel.BootFailed) + { + // Register a listener for application shutdown in order to terminate the runtime + var hostLifetime = app.ApplicationServices.GetRequiredService(); + var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime); + hostLifetime.RegisterObject(runtimeShutdown); - // Start the runtime! - runtime.Start(); + // Register our global threadabort enricher for logging + var threadAbortEnricher = app.ApplicationServices.GetRequiredService(); + LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context + + // Start the runtime! + runtime.Start(); + } + else + { + // TODO: Register simple middleware to show the error like we used to in UmbracoModule? + } return app; } @@ -76,6 +90,10 @@ namespace Umbraco.Web.BackOffice.AspNetCore { if (app == null) throw new ArgumentNullException(nameof(app)); + var runtime = app.ApplicationServices.GetRequiredService(); + // can't continue if boot failed + if (runtime.State.Level <= RuntimeLevel.BootFailed) return app; + app.UseMiddleware(); return app; @@ -85,6 +103,10 @@ namespace Umbraco.Web.BackOffice.AspNetCore { if (app == null) throw new ArgumentNullException(nameof(app)); + var runtime = app.ApplicationServices.GetRequiredService(); + // can't continue if boot failed + if (runtime.State.Level <= RuntimeLevel.BootFailed) return app; + app.UseSmidge(); return app; diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs index 220ff2be6b..03b54f350b 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs @@ -173,9 +173,10 @@ namespace Umbraco.Extensions if (container is null) throw new ArgumentNullException(nameof(container)); if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly)); - + services.AddUmbracoSqlCeSupport(); + services.AddUmbracoSqlServerSupport(); + services.AddSingleton(x => new DbProviderFactoryCreator( - x.GetService().ConnectionStrings()[Core.Constants.System.UmbracoConnectionName]?.ProviderName, DbProviderFactories.GetFactory, x.GetServices(), x.GetServices(), diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs index 524fffb7d8..f6f381fd4f 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs @@ -1,19 +1,22 @@ using System; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using StackExchange.Profiling; +using Umbraco.Core; using Umbraco.Web.Common.Middleware; namespace Umbraco.Extensions { public static class UmbracoRequestApplicationBuilderExtensions { - public static IApplicationBuilder UseUmbracoRequest(this IApplicationBuilder app) + // TODO: Could be internal or part of another call - this is a required system so should't be 'opt-in' + public static IApplicationBuilder UseUmbracoRequestLifetime(this IApplicationBuilder app) { - if (app == null) - { - throw new ArgumentNullException(nameof(app)); - } + if (app == null) throw new ArgumentNullException(nameof(app)); + var runtime = app.ApplicationServices.GetRequiredService(); + // can't continue if boot failed + if (runtime.State.Level <= RuntimeLevel.BootFailed) return app; app.UseMiddleware(); app.UseMiddleware(); diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index d8164e7aa8..f74208bac6 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -50,9 +50,7 @@ namespace Umbraco.Web.UI.BackOffice // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) - { - services.AddUmbracoSqlCeSupport(); - services.AddUmbracoSqlServerSupport(); + { services.AddUmbracoConfiguration(_config); services.AddUmbracoCore(_env, out var factory); services.AddUmbracoWebsite(); @@ -92,8 +90,8 @@ namespace Umbraco.Web.UI.BackOffice public void Configure(IApplicationBuilder app) { - // app.UseMiniProfiler(); - app.UseUmbracoRequest(); + //app.UseMiniProfiler(); + app.UseUmbracoRequestLifetime(); if (_env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -121,13 +119,7 @@ namespace Umbraco.Web.UI.BackOffice endpoints.MapControllerRoute( name: "default", - pattern: "{controller}/{action=Index}/{id?}"); - - endpoints.MapGet("/", async context => - { - var profilerHtml = app.ApplicationServices.GetRequiredService(); - await context.Response.WriteAsync($"Hello World!{profilerHtml.Render()}"); - }); + pattern: "{controller}/{action=Index}/{id?}"); }); } } diff --git a/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs index 0fa911da00..0c9bc1b200 100644 --- a/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/AspNetCore/UmbracoWebsiteApplicationBuilderExtensions.cs @@ -1,6 +1,8 @@ using System; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using SixLabors.ImageSharp.Web.DependencyInjection; +using Umbraco.Core; namespace Umbraco.Web.Website.AspNetCore { @@ -8,11 +10,12 @@ namespace Umbraco.Web.Website.AspNetCore { public static IApplicationBuilder UseUmbracoWebsite(this IApplicationBuilder app) { - if (app == null) - { - throw new ArgumentNullException(nameof(app)); - } + if (app == null) throw new ArgumentNullException(nameof(app)); + var runtime = app.ApplicationServices.GetRequiredService(); + + // can't continue if boot failed + if (runtime.State.Level <= RuntimeLevel.BootFailed) return app; // Important we handle image manipulations before the static files, otherwise the querystring is just ignored. app.UseImageSharp(); diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 7679da2e2e..a1ed85e50a 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web var connectionStringConfig = configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; - var dbProviderFactoryCreator = new UmbracoDbProviderFactoryCreator(connectionStringConfig?.ProviderName); + var dbProviderFactoryCreator = new UmbracoDbProviderFactoryCreator(); var globalSettings = configs.Global(); var connectionStrings = configs.ConnectionStrings(); diff --git a/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs b/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs index e0bfd2c639..989b86d622 100644 --- a/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs +++ b/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs @@ -11,18 +11,11 @@ namespace Umbraco.Web { public class UmbracoDbProviderFactoryCreator : IDbProviderFactoryCreator { - private readonly string _defaultProviderName; - - public UmbracoDbProviderFactoryCreator(string defaultProviderName) + public UmbracoDbProviderFactoryCreator() { - _defaultProviderName = defaultProviderName; - } - - public DbProviderFactory CreateFactory() - { - return CreateFactory(_defaultProviderName); } + public DbProviderFactory CreateFactory(string providerName) { if (string.IsNullOrEmpty(providerName)) return null;