From d577452aff647006e01cd8742cfb60357afcbd08 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 24 Mar 2020 18:18:25 +0100 Subject: [PATCH 01/14] AB#5820 - Introduced WebProfiler for asp.net core --- .../Net/IUmbracoApplicationLifetime.cs | 4 + src/Umbraco.Core/UriExtensions.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../AspNetCoreUmbracoApplicationLifetime.cs | 8 ++ ...racoRequestApplicationBuilderExtensions.cs | 21 +++ ...bracoRequestServiceCollectionExtensions.cs | 20 +++ .../Middleware/UmbracoRequestMiddleware.cs | 52 ++++++++ .../Runtime/Profiler/WebProfiler.cs | 83 ++++++++++++ .../Runtime/Profiler/WebProfilerComponent.cs | 55 ++++++++ .../Runtime/Profiler/WebProfilerComposer.cs | 8 ++ .../Runtime/Profiler/WebProfilerProvider.cs | 122 ++++++++++++++++++ .../Umbraco.Web.Common.csproj | 22 ++++ src/Umbraco.Web.UI.NetCore/Startup.cs | 4 + .../Umbraco.Web.UI.NetCore.csproj | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../AspNetUmbracoApplicationLifetime.cs | 5 + .../Install/InstallSteps/NewInstallStep.cs | 35 +++-- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/umbraco.sln | 6 + 19 files changed, 436 insertions(+), 18 deletions(-) create mode 100644 src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs create mode 100644 src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs create mode 100644 src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs create mode 100644 src/Umbraco.Web.Common/Umbraco.Web.Common.csproj diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs index 10d5b10955..4010252571 100644 --- a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -1,3 +1,5 @@ +using System; + namespace Umbraco.Net { public interface IUmbracoApplicationLifetime @@ -10,5 +12,7 @@ namespace Umbraco.Net /// Terminates the current application. The application restarts the next time a request is received for it. /// void Restart(); + + event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 4321aefd7f..e7be0b6500 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -146,7 +146,7 @@ namespace Umbraco.Core /// /// /// - internal static bool IsClientSideRequest(this Uri url) + public static bool IsClientSideRequest(this Uri url) { try { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 29d69db0d2..7dd0d6592b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -99,7 +99,7 @@ - + diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index 20cfef352d..f597c40252 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; @@ -14,6 +15,11 @@ namespace Umbraco.Web.AspNet { _httpContextAccessor = httpContextAccessor; _hostApplicationLifetime = hostApplicationLifetime; + + hostApplicationLifetime.ApplicationStarted.Register(() => + { + ApplicationInit?.Invoke(this, EventArgs.Empty); + }); } public bool IsRestarting { get; set; } @@ -32,5 +38,7 @@ namespace Umbraco.Web.AspNet Thread.CurrentPrincipal = null; _hostApplicationLifetime.StopApplication(); } + + public event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..110c09e1a9 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Extensions +{ + public static class UmbracoRequestApplicationBuilderExtensions + { + public static IApplicationBuilder UseUmbracoRequest(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + app.UseMiddleware(); + + return app; + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs new file mode 100644 index 0000000000..2a16b8b4f9 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Extensions +{ + public static class UmbracoRequestServiceCollectionExtensions + { + public static IServiceCollection AddUmbracoRequest(this IServiceCollection services) + { + var umbracoRequestLifetime = new UmbracoRequestLifetime(); + + services.AddSingleton(umbracoRequestLifetime); + services.AddSingleton(umbracoRequestLifetime); + + return services; + } + + } + +} diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs new file mode 100644 index 0000000000..60b6463152 --- /dev/null +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.Middleware +{ + public class UmbracoRequestMiddleware + { + private readonly RequestDelegate _next; + private readonly IUmbracoRequestLifetimeManager _umbracoRequestLifetimeManager; + public UmbracoRequestMiddleware(RequestDelegate next, IUmbracoRequestLifetimeManager umbracoRequestLifetimeManager) + { + _next = next; + _umbracoRequestLifetimeManager = umbracoRequestLifetimeManager; + } + + public async Task InvokeAsync(HttpContext context) + { + _umbracoRequestLifetimeManager.InitRequest(context); + await _next(context); + _umbracoRequestLifetimeManager.EndRequest(context); + } + } + + public interface IUmbracoRequestLifetime + { + event EventHandler RequestStart; + event EventHandler RequestEnd; + } + + public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager + { + public event EventHandler RequestStart; + public event EventHandler RequestEnd; + + public void InitRequest(HttpContext context) + { + RequestStart?.Invoke(this, context); + } + + public void EndRequest(HttpContext context) + { + RequestEnd?.Invoke(this, context); + } + } + + public interface IUmbracoRequestLifetimeManager + { + void InitRequest(HttpContext context); + void EndRequest(HttpContext context); + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs new file mode 100644 index 0000000000..e9fe7ea332 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using StackExchange.Profiling; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public class WebProfiler : IProfiler + { + private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest"; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly WebProfilerProvider _provider; + private readonly IUmbracoRequestLifetime _umbracoRequestLifetime; + private int _first; + + //TODO register and inject WebProfilerProvider? + public WebProfiler(IHttpContextAccessor httpContextAccessor, IRequestCache requestCache) + { + // create our own provider, which can provide a profiler even during boot + _provider = new WebProfilerProvider(requestCache); + _httpContextAccessor = httpContextAccessor; + } + + public string Render() => MiniProfiler.Current + .RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString(); + + public IDisposable Step(string name) => MiniProfiler.Current?.Step(name); + + public void Start() + { + MiniProfiler.StartNew(); + } + + public void Stop(bool discardResults = false) + { + MiniProfiler.Current?.Stop(discardResults); + } + + public void UmbracoApplicationBeginRequest(HttpContext context) + { + // if this is the first request, notify our own provider that this request is the boot request + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first) + { + _provider.BeginBootRequest(); + context.Items[BootRequestItemKey] = true; + // and no need to start anything, profiler is already there + } + // else start a profiler, the normal way + else if (ShouldProfile(context.Request)) + Start(); + } + + public void UmbracoApplicationEndRequest(HttpContext context) + { + // if this is the boot request, or if we should profile this request, stop + // (the boot request is always profiled, no matter what) + if (context.Items.TryGetValue(BootRequestItemKey, out var isBoot) && isBoot.Equals(true)) + { + _provider.EndBootRequest(); + Stop(); + } + else if (ShouldProfile(context.Request)) + { + Stop(); + } + } + + private static bool ShouldProfile(HttpRequest request) + { + if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false; + if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug; + if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug; + if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; + return false; + } + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs new file mode 100644 index 0000000000..425996a765 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -0,0 +1,55 @@ +using System; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Net; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + internal sealed class WebProfilerComponent : IComponent + { + private readonly bool _profile; + private readonly WebProfiler _profiler; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IUmbracoRequestLifetime _umbracoRequestLifetime; + + public WebProfilerComponent(IProfiler profiler, ILogger logger, IUmbracoRequestLifetime umbracoRequestLifetime, + IUmbracoApplicationLifetime umbracoApplicationLifetime) + { + _umbracoRequestLifetime = umbracoRequestLifetime; + _umbracoApplicationLifetime = umbracoApplicationLifetime; + _profile = true; + + // although registered in WebRuntime.Compose, ensure that we have not + // been replaced by another component, and we are still "the" profiler + _profiler = profiler as WebProfiler; + if (_profiler != null) return; + + // if VoidProfiler was registered, let it be known + if (profiler is VoidProfiler) + logger.Info( + "Profiler is VoidProfiler, not profiling (must run debug mode to profile)."); + _profile = false; + } + + public void Initialize() + { + if (!_profile) return; + + // bind to ApplicationInit - ie execute the application initialization for *each* application + // it would be a mistake to try and bind to the current application events + _umbracoApplicationLifetime.ApplicationInit += InitializeApplication; + } + + public void Terminate() + { + } + + private void InitializeApplication(object sender, EventArgs args) + { + _umbracoRequestLifetime.RequestStart += + (sender, context) => _profiler.UmbracoApplicationBeginRequest(context); + _umbracoRequestLifetime.RequestEnd += (sender, context) => _profiler.UmbracoApplicationEndRequest(context); + } + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs new file mode 100644 index 0000000000..688a3e5c28 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs @@ -0,0 +1,8 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + internal class WebProfilerComposer : ComponentComposer, ICoreComposer + { + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs new file mode 100644 index 0000000000..3ba5f762fe --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs @@ -0,0 +1,122 @@ +using System; +using System.Threading; +using StackExchange.Profiling; +using StackExchange.Profiling.Internal; +using Umbraco.Core.Cache; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public class WebProfilerProvider : DefaultProfilerProvider + { + private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); + private readonly IRequestCache _requestCache; + private volatile BootPhase _bootPhase; + private int _first; + private MiniProfiler _startupProfiler; + + public WebProfilerProvider(IRequestCache requestCache) + { + _requestCache = requestCache; + // booting... + _bootPhase = BootPhase.Boot; + } + + + /// + /// Gets the current profiler. + /// + /// + /// If the boot phase is not Booted, then this will return the startup profiler (this), otherwise + /// returns the base class + /// + public override MiniProfiler CurrentProfiler + { + get + { + // if not booting then just use base (fast) + // no lock, _bootPhase is volatile + if (_bootPhase == BootPhase.Booted) + return base.CurrentProfiler; + + // else + try + { + var current = base.CurrentProfiler; + return current ?? _startupProfiler; + } + catch + { + return _startupProfiler; + } + } + } + + public void BeginBootRequest() + { + _locker.EnterWriteLock(); + try + { + if (_bootPhase != BootPhase.Boot) + throw new InvalidOperationException("Invalid boot phase."); + _bootPhase = BootPhase.BootRequest; + + //TODO is this necessary? :mini-profiler: seems like a magic string + // assign the profiler to be the current MiniProfiler for the request + // is's already active, starting and all + _requestCache.Set(":mini-profiler:", _startupProfiler); + } + finally + { + _locker.ExitWriteLock(); + } + } + + public void EndBootRequest() + { + _locker.EnterWriteLock(); + try + { + if (_bootPhase != BootPhase.BootRequest) + throw new InvalidOperationException("Invalid boot phase."); + _bootPhase = BootPhase.Booted; + + _startupProfiler = null; + } + finally + { + _locker.ExitWriteLock(); + } + } + + /// + /// Starts a new MiniProfiler. + /// + /// + /// + /// This is called when WebProfiler calls MiniProfiler.Start() so, + /// - as a result of WebRuntime starting the WebProfiler, and + /// - assuming profiling is enabled, on every BeginRequest that should be profiled, + /// - except for the very first one which is the boot request. + /// + /// + public override MiniProfiler Start(string profilerName, MiniProfilerBaseOptions options) + { + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first == false) return base.Start(profilerName, options); + + _startupProfiler = new MiniProfiler("StartupProfiler", options); + CurrentProfiler = _startupProfiler; + return _startupProfiler; + } + + /// + /// Indicates the boot phase. + /// + private enum BootPhase + { + Boot = 0, // boot phase (before the 1st request even begins) + BootRequest = 1, // request boot phase (during the 1st request) + Booted = 2 // done booting + } + } +} diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj new file mode 100644 index 0000000000..cb9ca9156a --- /dev/null +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + Library + 8 + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 9ef4985aea..be81261d2a 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.Extensions; using Umbraco.Web.Website.AspNetCore; @@ -21,7 +22,9 @@ namespace Umbraco.Web.UI.BackOffice // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { + services.AddUmbracoConfiguration(); + services.AddUmbracoRequest(); services.AddUmbracoWebsite(); services.AddUmbracoBackOffice(); } @@ -29,6 +32,7 @@ namespace Umbraco.Web.UI.BackOffice // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + app.UseUmbracoRequest(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index ca7c3e26fa..abfb172763 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index fe11a8d9aa..0c0e6aee50 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -101,7 +101,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + 3.4.0 diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs index 245e8ea374..f5ebc03cd0 100644 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Web; using Umbraco.Net; @@ -11,6 +12,8 @@ namespace Umbraco.Web.AspNet public AspNetUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; + + UmbracoApplicationBase.ApplicationInit += ApplicationInit; } public bool IsRestarting { get; set; } @@ -30,5 +33,7 @@ namespace Umbraco.Web.AspNet Thread.CurrentPrincipal = null; HttpRuntime.UnloadAppDomain(); } + + public event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index f5a2ca8ac8..3f81c0d589 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -56,20 +56,7 @@ namespace Umbraco.Web.Install.InstallSteps throw new InvalidOperationException("Could not find the super user!"); } - var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); - var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); - if (membershipUser == null) - { - throw new InvalidOperationException($"No user found in membership provider with id of {Constants.Security.SuperUserId}."); - } - - //To change the password here we actually need to reset it since we don't have an old one to use to change - var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); - var resetResult = await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim()); - if (!resetResult.Succeeded) - { - throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); - } + await ResetAdminPassword(user.Password); admin.Email = user.Email.Trim(); admin.Name = user.Name.Trim(); @@ -95,6 +82,26 @@ namespace Umbraco.Web.Install.InstallSteps return null; } + private async Task ResetAdminPassword(string clearTextPassword) + { + var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); + var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); + if (membershipUser == null) + { + throw new InvalidOperationException( + $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); + } + + //To change the password here we actually need to reset it since we don't have an old one to use to change + var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); + var resetResult = + await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, clearTextPassword.Trim()); + if (!resetResult.Succeeded) + { + throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); + } + } + /// /// Return a custom view model for this step /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e8aaecdf4d..b9892e1c31 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -92,7 +92,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/umbraco.sln b/src/umbraco.sln index be363ef2e6..11098abdec 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -123,6 +123,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Website", "Umbr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Common", "Umbraco.Tests.Common\Umbraco.Tests.Common.csproj", "{A499779C-1B3B-48A8-B551-458E582E6E96}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.Common", "Umbraco.Web.Common\Umbraco.Web.Common.csproj", "{839D3048-9718-4907-BDE0-7CD63D364383}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -195,6 +197,10 @@ Global {A499779C-1B3B-48A8-B551-458E582E6E96}.Debug|Any CPU.Build.0 = Debug|Any CPU {A499779C-1B3B-48A8-B551-458E582E6E96}.Release|Any CPU.ActiveCfg = Release|Any CPU {A499779C-1B3B-48A8-B551-458E582E6E96}.Release|Any CPU.Build.0 = Release|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Release|Any CPU.ActiveCfg = Release|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9339e1d999e00221925a8b8514ca21bb1d37c6e7 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 24 Mar 2020 18:18:25 +0100 Subject: [PATCH 02/14] AB#5820 - Introduced WebProfiler for asp.net core --- .../Net/IUmbracoApplicationLifetime.cs | 4 + src/Umbraco.Core/UriExtensions.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../AspNetCoreUmbracoApplicationLifetime.cs | 8 ++ ...racoRequestApplicationBuilderExtensions.cs | 21 ++++ ...bracoRequestServiceCollectionExtensions.cs | 20 +++ .../Middleware/UmbracoRequestMiddleware.cs | 52 ++++++++ .../Runtime/Profiler/WebProfiler.cs | 81 ++++++++++++ .../Runtime/Profiler/WebProfilerComponent.cs | 55 ++++++++ .../Runtime/Profiler/WebProfilerComposer.cs | 8 ++ .../Runtime/Profiler/WebProfilerProvider.cs | 119 ++++++++++++++++++ .../Umbraco.Web.Common.csproj | 22 ++++ src/Umbraco.Web.UI.NetCore/Startup.cs | 4 + .../Umbraco.Web.UI.NetCore.csproj | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../AspNetUmbracoApplicationLifetime.cs | 5 + .../Install/InstallSteps/NewInstallStep.cs | 35 +++--- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/umbraco.sln | 6 + 19 files changed, 431 insertions(+), 18 deletions(-) create mode 100644 src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs create mode 100644 src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs create mode 100644 src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs create mode 100644 src/Umbraco.Web.Common/Umbraco.Web.Common.csproj diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs index 10d5b10955..4010252571 100644 --- a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -1,3 +1,5 @@ +using System; + namespace Umbraco.Net { public interface IUmbracoApplicationLifetime @@ -10,5 +12,7 @@ namespace Umbraco.Net /// Terminates the current application. The application restarts the next time a request is received for it. /// void Restart(); + + event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 4321aefd7f..e7be0b6500 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -146,7 +146,7 @@ namespace Umbraco.Core /// /// /// - internal static bool IsClientSideRequest(this Uri url) + public static bool IsClientSideRequest(this Uri url) { try { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 29d69db0d2..7dd0d6592b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -99,7 +99,7 @@ - + diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index 20cfef352d..f597c40252 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; @@ -14,6 +15,11 @@ namespace Umbraco.Web.AspNet { _httpContextAccessor = httpContextAccessor; _hostApplicationLifetime = hostApplicationLifetime; + + hostApplicationLifetime.ApplicationStarted.Register(() => + { + ApplicationInit?.Invoke(this, EventArgs.Empty); + }); } public bool IsRestarting { get; set; } @@ -32,5 +38,7 @@ namespace Umbraco.Web.AspNet Thread.CurrentPrincipal = null; _hostApplicationLifetime.StopApplication(); } + + public event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..110c09e1a9 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Extensions +{ + public static class UmbracoRequestApplicationBuilderExtensions + { + public static IApplicationBuilder UseUmbracoRequest(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + app.UseMiddleware(); + + return app; + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs new file mode 100644 index 0000000000..2a16b8b4f9 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Extensions +{ + public static class UmbracoRequestServiceCollectionExtensions + { + public static IServiceCollection AddUmbracoRequest(this IServiceCollection services) + { + var umbracoRequestLifetime = new UmbracoRequestLifetime(); + + services.AddSingleton(umbracoRequestLifetime); + services.AddSingleton(umbracoRequestLifetime); + + return services; + } + + } + +} diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs new file mode 100644 index 0000000000..60b6463152 --- /dev/null +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.Middleware +{ + public class UmbracoRequestMiddleware + { + private readonly RequestDelegate _next; + private readonly IUmbracoRequestLifetimeManager _umbracoRequestLifetimeManager; + public UmbracoRequestMiddleware(RequestDelegate next, IUmbracoRequestLifetimeManager umbracoRequestLifetimeManager) + { + _next = next; + _umbracoRequestLifetimeManager = umbracoRequestLifetimeManager; + } + + public async Task InvokeAsync(HttpContext context) + { + _umbracoRequestLifetimeManager.InitRequest(context); + await _next(context); + _umbracoRequestLifetimeManager.EndRequest(context); + } + } + + public interface IUmbracoRequestLifetime + { + event EventHandler RequestStart; + event EventHandler RequestEnd; + } + + public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager + { + public event EventHandler RequestStart; + public event EventHandler RequestEnd; + + public void InitRequest(HttpContext context) + { + RequestStart?.Invoke(this, context); + } + + public void EndRequest(HttpContext context) + { + RequestEnd?.Invoke(this, context); + } + } + + public interface IUmbracoRequestLifetimeManager + { + void InitRequest(HttpContext context); + void EndRequest(HttpContext context); + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs new file mode 100644 index 0000000000..4e1a7c7d84 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -0,0 +1,81 @@ +using System; +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using StackExchange.Profiling; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public class WebProfiler : IProfiler + { + private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest"; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly WebProfilerProvider _provider; + private int _first; + + public WebProfiler(IHttpContextAccessor httpContextAccessor) + { + // create our own provider, which can provide a profiler even during boot + _provider = new WebProfilerProvider(); + _httpContextAccessor = httpContextAccessor; + } + + public string Render() => MiniProfiler.Current + .RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString(); + + public IDisposable Step(string name) => MiniProfiler.Current?.Step(name); + + public void Start() + { + MiniProfiler.StartNew(); + } + + public void Stop(bool discardResults = false) + { + MiniProfiler.Current?.Stop(discardResults); + } + + public void UmbracoApplicationBeginRequest(HttpContext context) + { + // if this is the first request, notify our own provider that this request is the boot request + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first) + { + _provider.BeginBootRequest(); + context.Items[BootRequestItemKey] = true; + // and no need to start anything, profiler is already there + } + // else start a profiler, the normal way + else if (ShouldProfile(context.Request)) + Start(); + } + + public void UmbracoApplicationEndRequest(HttpContext context) + { + // if this is the boot request, or if we should profile this request, stop + // (the boot request is always profiled, no matter what) + if (context.Items.TryGetValue(BootRequestItemKey, out var isBoot) && isBoot.Equals(true)) + { + _provider.EndBootRequest(); + Stop(); + } + else if (ShouldProfile(context.Request)) + { + Stop(); + } + } + + private static bool ShouldProfile(HttpRequest request) + { + if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false; + if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug; + if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug; + if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; + return false; + } + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs new file mode 100644 index 0000000000..425996a765 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -0,0 +1,55 @@ +using System; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Net; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + internal sealed class WebProfilerComponent : IComponent + { + private readonly bool _profile; + private readonly WebProfiler _profiler; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IUmbracoRequestLifetime _umbracoRequestLifetime; + + public WebProfilerComponent(IProfiler profiler, ILogger logger, IUmbracoRequestLifetime umbracoRequestLifetime, + IUmbracoApplicationLifetime umbracoApplicationLifetime) + { + _umbracoRequestLifetime = umbracoRequestLifetime; + _umbracoApplicationLifetime = umbracoApplicationLifetime; + _profile = true; + + // although registered in WebRuntime.Compose, ensure that we have not + // been replaced by another component, and we are still "the" profiler + _profiler = profiler as WebProfiler; + if (_profiler != null) return; + + // if VoidProfiler was registered, let it be known + if (profiler is VoidProfiler) + logger.Info( + "Profiler is VoidProfiler, not profiling (must run debug mode to profile)."); + _profile = false; + } + + public void Initialize() + { + if (!_profile) return; + + // bind to ApplicationInit - ie execute the application initialization for *each* application + // it would be a mistake to try and bind to the current application events + _umbracoApplicationLifetime.ApplicationInit += InitializeApplication; + } + + public void Terminate() + { + } + + private void InitializeApplication(object sender, EventArgs args) + { + _umbracoRequestLifetime.RequestStart += + (sender, context) => _profiler.UmbracoApplicationBeginRequest(context); + _umbracoRequestLifetime.RequestEnd += (sender, context) => _profiler.UmbracoApplicationEndRequest(context); + } + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs new file mode 100644 index 0000000000..688a3e5c28 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs @@ -0,0 +1,8 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + internal class WebProfilerComposer : ComponentComposer, ICoreComposer + { + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs new file mode 100644 index 0000000000..99ad6f724c --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs @@ -0,0 +1,119 @@ +using System; +using System.Threading; +using StackExchange.Profiling; +using StackExchange.Profiling.Internal; +using Umbraco.Core.Cache; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public class WebProfilerProvider : DefaultProfilerProvider + { + private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); + private volatile BootPhase _bootPhase; + private int _first; + private MiniProfiler _startupProfiler; + + public WebProfilerProvider() + { + // booting... + _bootPhase = BootPhase.Boot; + } + + + /// + /// Gets the current profiler. + /// + /// + /// If the boot phase is not Booted, then this will return the startup profiler (this), otherwise + /// returns the base class + /// + public override MiniProfiler CurrentProfiler + { + get + { + // if not booting then just use base (fast) + // no lock, _bootPhase is volatile + if (_bootPhase == BootPhase.Booted) + return base.CurrentProfiler; + + // else + try + { + var current = base.CurrentProfiler; + return current ?? _startupProfiler; + } + catch + { + return _startupProfiler; + } + } + } + + public void BeginBootRequest() + { + _locker.EnterWriteLock(); + try + { + if (_bootPhase != BootPhase.Boot) + throw new InvalidOperationException("Invalid boot phase."); + _bootPhase = BootPhase.BootRequest; + + // assign the profiler to be the current MiniProfiler for the request + // is's already active, starting and all + CurrentProfiler = _startupProfiler; + } + finally + { + _locker.ExitWriteLock(); + } + } + + public void EndBootRequest() + { + _locker.EnterWriteLock(); + try + { + if (_bootPhase != BootPhase.BootRequest) + throw new InvalidOperationException("Invalid boot phase."); + _bootPhase = BootPhase.Booted; + + _startupProfiler = null; + } + finally + { + _locker.ExitWriteLock(); + } + } + + /// + /// Starts a new MiniProfiler. + /// + /// + /// + /// This is called when WebProfiler calls MiniProfiler.Start() so, + /// - as a result of WebRuntime starting the WebProfiler, and + /// - assuming profiling is enabled, on every BeginRequest that should be profiled, + /// - except for the very first one which is the boot request. + /// + /// + public override MiniProfiler Start(string profilerName, MiniProfilerBaseOptions options) + { + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first == false) return base.Start(profilerName, options); + + _startupProfiler = new MiniProfiler("StartupProfiler", options); + CurrentProfiler = _startupProfiler; + return _startupProfiler; + } + + /// + /// Indicates the boot phase. + /// + private enum BootPhase + { + Boot = 0, // boot phase (before the 1st request even begins) + BootRequest = 1, // request boot phase (during the 1st request) + Booted = 2 // done booting + } + } +} diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj new file mode 100644 index 0000000000..cb9ca9156a --- /dev/null +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + Library + 8 + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 9ef4985aea..be81261d2a 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.Extensions; using Umbraco.Web.Website.AspNetCore; @@ -21,7 +22,9 @@ namespace Umbraco.Web.UI.BackOffice // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { + services.AddUmbracoConfiguration(); + services.AddUmbracoRequest(); services.AddUmbracoWebsite(); services.AddUmbracoBackOffice(); } @@ -29,6 +32,7 @@ namespace Umbraco.Web.UI.BackOffice // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + app.UseUmbracoRequest(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index ca7c3e26fa..abfb172763 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index fe11a8d9aa..0c0e6aee50 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -101,7 +101,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + 3.4.0 diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs index 245e8ea374..f5ebc03cd0 100644 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Web; using Umbraco.Net; @@ -11,6 +12,8 @@ namespace Umbraco.Web.AspNet public AspNetUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; + + UmbracoApplicationBase.ApplicationInit += ApplicationInit; } public bool IsRestarting { get; set; } @@ -30,5 +33,7 @@ namespace Umbraco.Web.AspNet Thread.CurrentPrincipal = null; HttpRuntime.UnloadAppDomain(); } + + public event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index f5a2ca8ac8..3f81c0d589 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -56,20 +56,7 @@ namespace Umbraco.Web.Install.InstallSteps throw new InvalidOperationException("Could not find the super user!"); } - var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); - var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); - if (membershipUser == null) - { - throw new InvalidOperationException($"No user found in membership provider with id of {Constants.Security.SuperUserId}."); - } - - //To change the password here we actually need to reset it since we don't have an old one to use to change - var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); - var resetResult = await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim()); - if (!resetResult.Succeeded) - { - throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); - } + await ResetAdminPassword(user.Password); admin.Email = user.Email.Trim(); admin.Name = user.Name.Trim(); @@ -95,6 +82,26 @@ namespace Umbraco.Web.Install.InstallSteps return null; } + private async Task ResetAdminPassword(string clearTextPassword) + { + var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); + var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); + if (membershipUser == null) + { + throw new InvalidOperationException( + $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); + } + + //To change the password here we actually need to reset it since we don't have an old one to use to change + var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); + var resetResult = + await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, clearTextPassword.Trim()); + if (!resetResult.Succeeded) + { + throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); + } + } + /// /// Return a custom view model for this step /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e8aaecdf4d..b9892e1c31 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -92,7 +92,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/umbraco.sln b/src/umbraco.sln index be363ef2e6..11098abdec 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -123,6 +123,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Website", "Umbr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Common", "Umbraco.Tests.Common\Umbraco.Tests.Common.csproj", "{A499779C-1B3B-48A8-B551-458E582E6E96}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.Common", "Umbraco.Web.Common\Umbraco.Web.Common.csproj", "{839D3048-9718-4907-BDE0-7CD63D364383}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -195,6 +197,10 @@ Global {A499779C-1B3B-48A8-B551-458E582E6E96}.Debug|Any CPU.Build.0 = Debug|Any CPU {A499779C-1B3B-48A8-B551-458E582E6E96}.Release|Any CPU.ActiveCfg = Release|Any CPU {A499779C-1B3B-48A8-B551-458E582E6E96}.Release|Any CPU.Build.0 = Release|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Release|Any CPU.ActiveCfg = Release|Any CPU + {839D3048-9718-4907-BDE0-7CD63D364383}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9cca16d31b574604b57814a0eb9233ed3967056a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 24 Mar 2020 19:37:04 +0100 Subject: [PATCH 03/14] Rollback of changes not related to this issue --- .../Install/InstallSteps/NewInstallStep.cs | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 3f81c0d589..fdec5bfa2e 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -56,7 +56,22 @@ namespace Umbraco.Web.Install.InstallSteps throw new InvalidOperationException("Could not find the super user!"); } - await ResetAdminPassword(user.Password); + var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); + var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); + if (membershipUser == null) + { + throw new InvalidOperationException( + $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); + } + + //To change the password here we actually need to reset it since we don't have an old one to use to change + var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); + var resetResult = + await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim()); + if (!resetResult.Succeeded) + { + throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); + } admin.Email = user.Email.Trim(); admin.Name = user.Name.Trim(); @@ -82,26 +97,6 @@ namespace Umbraco.Web.Install.InstallSteps return null; } - private async Task ResetAdminPassword(string clearTextPassword) - { - var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); - var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId); - if (membershipUser == null) - { - throw new InvalidOperationException( - $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); - } - - //To change the password here we actually need to reset it since we don't have an old one to use to change - var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser.Id); - var resetResult = - await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, clearTextPassword.Trim()); - if (!resetResult.Succeeded) - { - throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors)); - } - } - /// /// Return a custom view model for this step /// From 92afbfc6fc7fc8e95569222e23022903f098354c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 25 Mar 2020 05:39:25 +0100 Subject: [PATCH 04/14] AB#5820 - Initialize the webprofiler in asp.net core --- .../Models/HostingSettings.cs | 2 +- .../DefaultUmbracoAssemblyProvider.cs | 3 +++ ...coBackOfficeServiceCollectionExtensions.cs | 20 ++++++++++++++-- .../Umbraco.Web.BackOffice.csproj | 1 + .../AspNetCoreUmbracoApplicationLifetime.cs | 2 +- ...bracoRequestServiceCollectionExtensions.cs | 20 ---------------- .../Runtime/Profiler/WebInitialComponent.cs | 18 ++++++++++++++ .../Runtime/Profiler/WebInitialComposer.cs | 24 +++++++++++++++++++ src/Umbraco.Web.UI.NetCore/Startup.cs | 1 - src/Umbraco.Web.UI.NetCore/appsettings.json | 3 +++ 10 files changed, 69 insertions(+), 25 deletions(-) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs (97%) delete mode 100644 src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs create mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs diff --git a/src/Umbraco.Configuration/Models/HostingSettings.cs b/src/Umbraco.Configuration/Models/HostingSettings.cs index 4a156cee6a..7cfc08d2d4 100644 --- a/src/Umbraco.Configuration/Models/HostingSettings.cs +++ b/src/Umbraco.Configuration/Models/HostingSettings.cs @@ -22,6 +22,6 @@ namespace Umbraco.Configuration.Models /// Gets a value indicating whether umbraco is running in [debug mode]. /// /// true if [debug mode]; otherwise, false. - public bool DebugMode => _configuration.GetValue(Prefix+":Debug", false); + public bool DebugMode => _configuration.GetValue(Prefix+"Debug", false); } } diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 61d7cff240..4d153d8922 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -22,6 +22,9 @@ namespace Umbraco.Core.Composing "Umbraco.PublishedCache.NuCache", "Umbraco.ModelsBuilder.Embedded", "Umbraco.Examine.Lucene", + "Umbraco.Web.Common", + "Umbraco.Web.BackOffice", + "Umbraco.Web.Website", }; public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs index 135ba90b97..b63988d4e5 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Logging.Serilog; using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; +using Umbraco.Web.Common.Runtime.Profiler; namespace Umbraco.Web.BackOffice.AspNetCore { @@ -118,7 +119,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore private static void CreateCompositionRoot(IServiceCollection services) { // TODO: This isn't the best to have to resolve the services now but to avoid this will - // require quite a lot of re-work. + // require quite a lot of re-work. var serviceProvider = services.BuildServiceProvider(); var httpContextAccessor = serviceProvider.GetRequiredService(); @@ -142,11 +143,26 @@ namespace Umbraco.Web.BackOffice.AspNetCore new AspNetCoreMarchal()); var backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings); - var profiler = new LogProfiler(logger); + var profiler = GetWebProfiler(hostingEnvironment, httpContextAccessor); Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); } + private static IProfiler GetWebProfiler(Umbraco.Core.Hosting.IHostingEnvironment hostingEnvironment, IHttpContextAccessor httpContextAccessor) + { + // create and start asap to profile boot + if (!hostingEnvironment.IsDebugMode) + { + // should let it be null, that's how MiniProfiler is meant to work, + // but our own IProfiler expects an instance so let's get one + return new VoidProfiler(); + } + + var webProfiler = new WebProfiler(httpContextAccessor); + webProfiler.Start(); + + return webProfiler; + } private class AspNetCoreBootPermissionsChecker : IUmbracoBootPermissionChecker { public void ThrowIfNotPermissions() diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index bd20769d45..5b031c095e 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs similarity index 97% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index f597c40252..45cfb7d570 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Umbraco.Net; -namespace Umbraco.Web.AspNet +namespace Umbraco.Web.Common.AspNetCore { public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime { diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs deleted file mode 100644 index 2a16b8b4f9..0000000000 --- a/src/Umbraco.Web.Common/Extensions/UmbracoRequestServiceCollectionExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Web.Common.Middleware; - -namespace Umbraco.Web.Common.Extensions -{ - public static class UmbracoRequestServiceCollectionExtensions - { - public static IServiceCollection AddUmbracoRequest(this IServiceCollection services) - { - var umbracoRequestLifetime = new UmbracoRequestLifetime(); - - services.AddSingleton(umbracoRequestLifetime); - services.AddSingleton(umbracoRequestLifetime); - - return services; - } - - } - -} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs new file mode 100644 index 0000000000..2194b3c038 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs @@ -0,0 +1,18 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public sealed class WebInitialComponent : IComponent + { + + + public void Initialize() + { + } + + public void Terminate() + { + } + + } +} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs new file mode 100644 index 0000000000..472d8bf8e9 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Net; +using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Common.Middleware; + +namespace Umbraco.Web.Common.Runtime.Profiler +{ + public class WebInitialComposer : ComponentComposer, ICoreComposer + { + + public override void Compose(Composition composition) + { + base.Compose(composition); + + var umbracoRequestLifetime = new UmbracoRequestLifetime(); + + composition.RegisterUnique(factory => umbracoRequestLifetime); + composition.RegisterUnique(factory => umbracoRequestLifetime); + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 444b9a835d..8a04b0b17b 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -24,7 +24,6 @@ namespace Umbraco.Web.UI.BackOffice { services.AddUmbracoConfiguration(); - services.AddUmbracoRequest(); services.AddUmbracoCore(); services.AddUmbracoWebsite(); } diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 1c89647efa..2448ae0aa9 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -12,6 +12,9 @@ "AllowedHosts": "*", "Umbraco": { "CMS": { + "Hosting": { + "Debug": true + }, "Imaging": { "Resize": { "MaxWidth": 5000, From 681a25b861f632aae867c496de54ee35c0b774ea Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 27 Mar 2020 11:39:17 +0100 Subject: [PATCH 05/14] Moved files from backoffice into Common + Introduced AspNetCoreComponent to invoke the Umbraco Application Init event --- .../Net/IUmbracoApplicationLifetime.cs | 2 ++ .../Implementations/TestHelper.cs | 1 + .../Implementations/TestHostingEnvironment.cs | 4 +-- src/Umbraco.Tests.Integration/RuntimeTests.cs | 11 +++---- .../AspNetCoreApplicationShutdownRegistry.cs | 2 +- .../AspNetCore/AspNetCoreBackOfficeInfo.cs | 2 +- .../AspNetCoreHostingEnvironment.cs | 8 ++--- .../AspNetCore/AspNetCoreIpResolver.cs | 2 +- .../AspNetCore/AspNetCoreMarchal.cs | 2 +- .../AspNetCore/AspNetCoreSessionIdResolver.cs | 4 +-- .../AspNetCoreUmbracoApplicationLifetime.cs | 9 +++--- .../UmbracoCoreServiceCollectionExtensions.cs | 8 ++--- .../Lifetime/IUmbracoRequestLifetime.cs | 11 +++++++ .../IUmbracoRequestLifetimeManager.cs | 10 +++++++ .../Lifetime/UmbracoRequestLifetime.cs | 21 ++++++++++++++ .../Middleware/UmbracoRequestMiddleware.cs | 28 +----------------- .../Runtime/AspNetCoreComponent.cs | 29 +++++++++++++++++++ .../Runtime}/AspNetCoreComposer.cs | 15 ++++++++-- .../Runtime/Profiler/WebInitialComponent.cs | 18 ------------ .../Runtime/Profiler/WebInitialComposer.cs | 24 --------------- .../Runtime/Profiler/WebProfiler.cs | 9 ++++-- .../Runtime/Profiler/WebProfilerComponent.cs | 1 + .../Umbraco.Web.Common.csproj | 1 + src/Umbraco.Web.UI.NetCore/Startup.cs | 8 ++++- .../AspNetUmbracoApplicationLifetime.cs | 4 +++ src/Umbraco.Web/Logging/WebProfiler.cs | 2 +- 26 files changed, 134 insertions(+), 102 deletions(-) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs (97%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreBackOfficeInfo.cs (88%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreHostingEnvironment.cs (98%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreIpResolver.cs (91%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreMarchal.cs (88%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/AspNetCoreSessionIdResolver.cs (94%) rename src/{Umbraco.Web.BackOffice => Umbraco.Web.Common}/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs (97%) create mode 100644 src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs create mode 100644 src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs create mode 100644 src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs create mode 100644 src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs rename src/{Umbraco.Web.BackOffice/AspNetCore => Umbraco.Web.Common/Runtime}/AspNetCoreComposer.cs (54%) delete mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs delete mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs index 8bf3f115cd..c84d475e90 100644 --- a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -15,5 +15,7 @@ namespace Umbraco.Net void Restart(); event EventHandler ApplicationInit; + + void InvokeApplicationInit(); } } diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 4a986fa35a..f10586b94b 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Runtime; using Umbraco.Tests.Common; using Umbraco.Web.BackOffice; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.AspNetCore; using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment; namespace Umbraco.Tests.Integration.Implementations diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs index 491b7e5480..652aad8928 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs @@ -1,11 +1,11 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Umbraco.Core.Configuration; -using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.AspNetCore; namespace Umbraco.Tests.Integration.Implementations { - + public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Umbraco.Core.Hosting.IHostingEnvironment { public TestHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) : base(hostingSettings, webHostEnvironment, httpContextAccessor) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 0e11a29b95..c7bb22f135 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -22,6 +22,7 @@ using Umbraco.Tests.Common; using Umbraco.Tests.Integration.Implementations; using Umbraco.Tests.Integration.Testing; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.AspNetCore; using static Umbraco.Core.Migrations.Install.DatabaseBuilder; namespace Umbraco.Tests.Integration @@ -52,11 +53,11 @@ namespace Umbraco.Tests.Integration // LightInject / Umbraco var container = UmbracoServiceProviderFactory.CreateServiceContainer(); var serviceProviderFactory = new UmbracoServiceProviderFactory(container); - var umbracoContainer = serviceProviderFactory.GetContainer(); + var umbracoContainer = serviceProviderFactory.GetContainer(); // Special case since we are not using the Generic Host, we need to manually add an AspNetCore service to the container umbracoContainer.Register(x => Mock.Of()); - + var testHelper = new TestHelper(); // Create the core runtime @@ -70,7 +71,7 @@ namespace Umbraco.Tests.Integration Assert.IsTrue(coreRuntime.MainDom.IsMainDom); Assert.IsNull(coreRuntime.State.BootFailedException); - Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); + Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); Assert.IsTrue(MyComposer.IsComposed); Assert.IsFalse(MyComponent.IsInit); Assert.IsFalse(MyComponent.IsTerminated); @@ -88,7 +89,7 @@ namespace Umbraco.Tests.Integration } /// - /// Calling AddUmbracoCore to configure the container + /// Calling AddUmbracoCore to configure the container /// [Test] public async Task AddUmbracoCore() @@ -280,5 +281,5 @@ namespace Umbraco.Tests.Integration } } - + } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs similarity index 97% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs index 92af822836..57ad83d4ba 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreApplicationShutdownRegistry.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting; using Umbraco.Core; using Umbraco.Core.Hosting; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { public class AspNetCoreApplicationShutdownRegistry : IApplicationShutdownRegistry { diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs similarity index 88% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs index ba12b64dfe..375028132e 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreBackOfficeInfo.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs @@ -1,7 +1,7 @@ using Umbraco.Core; using Umbraco.Core.Configuration; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { public class AspNetCoreBackOfficeInfo : IBackOfficeInfo { diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs similarity index 98% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs index 6f1298918d..c24c2c4698 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -6,16 +6,16 @@ using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Configuration; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { public class AspNetCoreHostingEnvironment : Umbraco.Core.Hosting.IHostingEnvironment { - + private readonly IHostingSettings _hostingSettings; private readonly IWebHostEnvironment _webHostEnvironment; private readonly IHttpContextAccessor _httpContextAccessor; - + private string _localTempPath; public AspNetCoreHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) @@ -103,7 +103,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore return applicationPath.Add(segment).Value; } - + } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreIpResolver.cs similarity index 91% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreIpResolver.cs index cee43757d8..863d545066 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreIpResolver.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Http; using Umbraco.Net; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { public class AspNetIpResolver : IIpResolver { diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreMarchal.cs similarity index 88% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreMarchal.cs index 247666090e..af23d092e9 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreMarchal.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreMarchal.cs @@ -2,7 +2,7 @@ using System; using System.Runtime.InteropServices; using Umbraco.Core.Diagnostics; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { public class AspNetCoreMarchal : IMarchal diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs similarity index 94% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs rename to src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs index cafb02d367..818a39fac5 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http.Features; using Umbraco.Net; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { internal class AspNetCoreSessionIdResolver : ISessionIdResolver { @@ -13,7 +13,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore _httpContextAccessor = httpContextAccessor; } - + public string SessionId { get diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index 45cfb7d570..d73b9bebd4 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -15,11 +15,6 @@ namespace Umbraco.Web.Common.AspNetCore { _httpContextAccessor = httpContextAccessor; _hostApplicationLifetime = hostApplicationLifetime; - - hostApplicationLifetime.ApplicationStarted.Register(() => - { - ApplicationInit?.Invoke(this, EventArgs.Empty); - }); } public bool IsRestarting { get; set; } @@ -39,6 +34,10 @@ namespace Umbraco.Web.Common.AspNetCore _hostApplicationLifetime.StopApplication(); } + public void InvokeApplicationInit() + { + ApplicationInit?.Invoke(this, EventArgs.Empty); + } public event EventHandler ApplicationInit; } } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs similarity index 97% rename from src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs rename to src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs index c14b164e9c..eb320c459f 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs @@ -19,9 +19,8 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; using Umbraco.Web.Common.Runtime.Profiler; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.AspNetCore { - // TODO: Move to Umbraco.Web.Common public static class UmbracoCoreServiceCollectionExtensions { /// @@ -71,7 +70,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, IRegister umbContainer, Assembly entryAssembly) { if (services is null) throw new ArgumentNullException(nameof(services)); - if (umbContainer is null) throw new ArgumentNullException(nameof(umbContainer)); + var container = umbContainer; + if (container is null) throw new ArgumentNullException(nameof(container)); if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly)); // Special case! The generic host adds a few default services but we need to manually add this one here NOW because @@ -97,7 +97,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore backOfficeInfo, typeFinder); - var factory = coreRuntime.Configure(umbContainer); + var factory = coreRuntime.Configure(container); return services; } diff --git a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs new file mode 100644 index 0000000000..616a75bfe7 --- /dev/null +++ b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs @@ -0,0 +1,11 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.Lifetime +{ + public interface IUmbracoRequestLifetime + { + event EventHandler RequestStart; + event EventHandler RequestEnd; + } +} diff --git a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs new file mode 100644 index 0000000000..e4c671c2d3 --- /dev/null +++ b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.Lifetime +{ + public interface IUmbracoRequestLifetimeManager + { + void InitRequest(HttpContext context); + void EndRequest(HttpContext context); + } +} diff --git a/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs new file mode 100644 index 0000000000..43810a9e66 --- /dev/null +++ b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.Lifetime +{ + public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager + { + public event EventHandler RequestStart; + public event EventHandler RequestEnd; + + public void InitRequest(HttpContext context) + { + RequestStart?.Invoke(this, context); + } + + public void EndRequest(HttpContext context) + { + RequestEnd?.Invoke(this, context); + } + } +} diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index 60b6463152..93461fc1d5 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Umbraco.Web.Common.Lifetime; namespace Umbraco.Web.Common.Middleware { @@ -22,31 +23,4 @@ namespace Umbraco.Web.Common.Middleware } } - public interface IUmbracoRequestLifetime - { - event EventHandler RequestStart; - event EventHandler RequestEnd; - } - - public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager - { - public event EventHandler RequestStart; - public event EventHandler RequestEnd; - - public void InitRequest(HttpContext context) - { - RequestStart?.Invoke(this, context); - } - - public void EndRequest(HttpContext context) - { - RequestEnd?.Invoke(this, context); - } - } - - public interface IUmbracoRequestLifetimeManager - { - void InitRequest(HttpContext context); - void EndRequest(HttpContext context); - } } diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs new file mode 100644 index 0000000000..04ae022fa5 --- /dev/null +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Hosting; +using Umbraco.Core.Composing; +using Umbraco.Net; + +namespace Umbraco.Web.Common.Runtime +{ + public sealed class AspNetCoreComponent : IComponent + { + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + + public AspNetCoreComponent(IHostApplicationLifetime hostApplicationLifetime, IUmbracoApplicationLifetime umbracoApplicationLifetime) + { + _hostApplicationLifetime = hostApplicationLifetime; + _umbracoApplicationLifetime = umbracoApplicationLifetime; + } + + public void Initialize() + { + _hostApplicationLifetime.ApplicationStarted.Register(() => { + _umbracoApplicationLifetime.InvokeApplicationInit(); + }); + } + + public void Terminate() + { + } + } +} diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs similarity index 54% rename from src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs rename to src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index 463af87849..f9a6a770e2 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -5,24 +5,33 @@ using Umbraco.Core.Hosting; using Umbraco.Net; using Umbraco.Core.Runtime; using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Common.Lifetime; -namespace Umbraco.Web.BackOffice.AspNetCore +namespace Umbraco.Web.Common.Runtime { /// /// Adds/replaces AspNetCore specific services /// [ComposeBefore(typeof(ICoreComposer))] [ComposeAfter(typeof(CoreInitialComposer))] - public class AspNetCoreComposer : IComposer + public class AspNetCoreComposer : ComponentComposer, IComposer { - public void Compose(Composition composition) + public new void Compose(Composition composition) { + base.Compose(composition); + // AspNetCore specific services composition.RegisterUnique(); // Our own netcore implementations composition.RegisterUnique(); composition.RegisterUnique(); + + // The umbraco request lifetime + var umbracoRequestLifetime = new UmbracoRequestLifetime(); + composition.RegisterUnique(factory => umbracoRequestLifetime); + composition.RegisterUnique(factory => umbracoRequestLifetime); + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs deleted file mode 100644 index 2194b3c038..0000000000 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Umbraco.Core.Composing; - -namespace Umbraco.Web.Common.Runtime.Profiler -{ - public sealed class WebInitialComponent : IComponent - { - - - public void Initialize() - { - } - - public void Terminate() - { - } - - } -} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs deleted file mode 100644 index 472d8bf8e9..0000000000 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebInitialComposer.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Net; -using Umbraco.Web.Common.AspNetCore; -using Umbraco.Web.Common.Middleware; - -namespace Umbraco.Web.Common.Runtime.Profiler -{ - public class WebInitialComposer : ComponentComposer, ICoreComposer - { - - public override void Compose(Composition composition) - { - base.Compose(composition); - - var umbracoRequestLifetime = new UmbracoRequestLifetime(); - - composition.RegisterUnique(factory => umbracoRequestLifetime); - composition.RegisterUnique(factory => umbracoRequestLifetime); - composition.RegisterUnique(); - } - } -} diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs index 4e1a7c7d84..8baf8790c3 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -22,10 +22,15 @@ namespace Umbraco.Web.Common.Runtime.Profiler // create our own provider, which can provide a profiler even during boot _provider = new WebProfilerProvider(); _httpContextAccessor = httpContextAccessor; + + MiniProfiler.DefaultOptions.ProfilerProvider = _provider; } - public string Render() => MiniProfiler.Current - .RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString(); + public string Render() + { + return MiniProfiler.Current + .RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString(); + } public IDisposable Step(string name) => MiniProfiler.Current?.Step(name); diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs index 425996a765..9a656ae4f3 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -2,6 +2,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Net; +using Umbraco.Web.Common.Lifetime; using Umbraco.Web.Common.Middleware; namespace Umbraco.Web.Common.Runtime.Profiler diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index cb9ca9156a..2cf6790484 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index fe77dfabda..b38333bb9a 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -8,7 +8,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Umbraco.Composing; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Extensions; using Umbraco.Web.Website.AspNetCore; @@ -52,6 +54,7 @@ namespace Umbraco.Web.UI.BackOffice app.UseDeveloperExceptionPage(); } + app.UseUmbracoCore(); app.UseUmbracoWebsite(); app.UseUmbracoBackOffice(); @@ -59,7 +62,10 @@ namespace Umbraco.Web.UI.BackOffice app.UseEndpoints(endpoints => { - endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync($"Hello World!{Current.Profiler.Render()}"); + }); }); } } diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs index f5ebc03cd0..9818a510b2 100644 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -35,5 +35,9 @@ namespace Umbraco.Web.AspNet } public event EventHandler ApplicationInit; + public void InvokeApplicationInit() + { + + } } } diff --git a/src/Umbraco.Web/Logging/WebProfiler.cs b/src/Umbraco.Web/Logging/WebProfiler.cs index 512edb2296..e390950c0b 100755 --- a/src/Umbraco.Web/Logging/WebProfiler.cs +++ b/src/Umbraco.Web/Logging/WebProfiler.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Logging MiniProfiler.Configure(new MiniProfilerOptions { SqlFormatter = new SqlServerFormatter(), - StackMaxLength = 5000, + StackMaxLength = 5000, ProfilerProvider = _provider }); } From b2799ef90147f125061919bc3d462750bfcbfa4e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Sun, 29 Mar 2020 22:35:52 +0200 Subject: [PATCH 06/14] AB#5820 - Added simple controller and view to test the miniprofiler --- .../Controllers/BackOfficeController.cs | 14 +++ .../AspNetCore/AspNetCoreBackOfficeInfo.cs | 2 +- .../UmbracoCoreServiceCollectionExtensions.cs | 2 +- ...racoRequestApplicationBuilderExtensions.cs | 5 +- .../Runtime/Profiler/WebProfiler.cs | 104 +++++++++++---- .../Runtime/Profiler/WebProfilerComponent.cs | 3 + .../Runtime/Profiler/WebProfilerProvider.cs | 119 ------------------ .../Properties/launchSettings.json | 11 +- src/Umbraco.Web.UI.NetCore/Startup.cs | 45 ++++++- .../Views/BackOffice/Index.cshtml | 20 +++ .../Editors/BackOfficeController.cs | 7 +- 11 files changed, 176 insertions(+), 156 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs delete mode 100644 src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs create mode 100644 src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs new file mode 100644 index 0000000000..83f6eda24a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.Composing; + +namespace Umbraco.Web.BackOffice.Controllers +{ + public class BackOfficeController : Controller + { + // GET + public IActionResult Index() + { + return View(); + } + } +} diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs index 375028132e..39b1d7ff4e 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.Common.AspNetCore GetAbsoluteUrl = globalSettings.UmbracoPath; } - public string GetAbsoluteUrl { get; } + public string GetAbsoluteUrl { get; } // TODO make absolute } } diff --git a/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs index eb320c459f..99a274364b 100644 --- a/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs @@ -169,7 +169,7 @@ namespace Umbraco.Web.Common.AspNetCore } var webProfiler = new WebProfiler(httpContextAccessor); - webProfiler.Start(); + webProfiler.StartBoot(); return webProfiler; } diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs index 110c09e1a9..8278c99bf5 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoRequestApplicationBuilderExtensions.cs @@ -1,5 +1,6 @@ using System; using Microsoft.AspNetCore.Builder; +using StackExchange.Profiling; using Umbraco.Web.Common.Middleware; namespace Umbraco.Web.Common.Extensions @@ -13,9 +14,11 @@ namespace Umbraco.Web.Common.Extensions throw new ArgumentNullException(nameof(app)); } - app.UseMiddleware(); + app.UseMiddleware(); + app.UseMiddleware(); return app; } } + } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs index 8baf8790c3..a05483633d 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -1,76 +1,118 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using StackExchange.Profiling; +using StackExchange.Profiling.Internal; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Logging; -using Umbraco.Web.Common.Middleware; namespace Umbraco.Web.Common.Runtime.Profiler { public class WebProfiler : IProfiler { + private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); + private volatile BootPhase _bootPhase; + private MiniProfiler _startupProfiler; + + private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest"; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly WebProfilerProvider _provider; private int _first; public WebProfiler(IHttpContextAccessor httpContextAccessor) { // create our own provider, which can provide a profiler even during boot - _provider = new WebProfilerProvider(); _httpContextAccessor = httpContextAccessor; - - MiniProfiler.DefaultOptions.ProfilerProvider = _provider; + _bootPhase = BootPhase.Boot; } + /// + /// + /// + /// + /// Normally we would call MiniProfiler.Current.RenderIncludes(...), but because the requeststate is not set, this method does not work. + /// We fake the requestIds from the RequestState here. + /// public string Render() { - return MiniProfiler.Current - .RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString(); + + var profiler = MiniProfiler.Current; + if (profiler == null) return string.Empty; + + var context = _httpContextAccessor.HttpContext; + + var path = (profiler.Options as MiniProfilerOptions)?.RouteBasePath.Value.EnsureTrailingSlash(); + + var result = StackExchange.Profiling.Internal.Render.Includes( + profiler, + path: context.Request.PathBase + path, + isAuthorized: true, + requestIDs: new List{ profiler.Id }, + position: RenderPosition.Right, + showTrivial: profiler.Options.PopupShowTrivial, + showTimeWithChildren: profiler.Options.PopupShowTimeWithChildren, + maxTracesToShow: profiler.Options.PopupMaxTracesToShow, + showControls:profiler.Options.ShowControls, + startHidden: profiler.Options.PopupStartHidden); + + return result; } - public IDisposable Step(string name) => MiniProfiler.Current?.Step(name); + public IDisposable Step(string name) + { + return MiniProfiler.Current?.Step(name); + } public void Start() { MiniProfiler.StartNew(); } + public void StartBoot() + { + _startupProfiler = MiniProfiler.StartNew("Startup Profiler"); + } + + public void StopBoot() + { + _startupProfiler.Stop(); + } + public void Stop(bool discardResults = false) { MiniProfiler.Current?.Stop(discardResults); } + public void UmbracoApplicationBeginRequest(HttpContext context) { - // if this is the first request, notify our own provider that this request is the boot request - var first = Interlocked.Exchange(ref _first, 1) == 0; - if (first) + if (ShouldProfile(context.Request)) { - _provider.BeginBootRequest(); - context.Items[BootRequestItemKey] = true; - // and no need to start anything, profiler is already there - } - // else start a profiler, the normal way - else if (ShouldProfile(context.Request)) Start(); + } } public void UmbracoApplicationEndRequest(HttpContext context) { - // if this is the boot request, or if we should profile this request, stop - // (the boot request is always profiled, no matter what) - if (context.Items.TryGetValue(BootRequestItemKey, out var isBoot) && isBoot.Equals(true)) - { - _provider.EndBootRequest(); - Stop(); - } - else if (ShouldProfile(context.Request)) + if (ShouldProfile(context.Request)) { Stop(); + + // if this is the first request, append the startup profiler + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first) + { + + var startupDuration = _startupProfiler.Root.DurationMilliseconds.GetValueOrDefault(); + MiniProfiler.Current.DurationMilliseconds += startupDuration; + MiniProfiler.Current.GetTimingHierarchy().First().DurationMilliseconds += startupDuration; + MiniProfiler.Current.Root.AddChild(_startupProfiler.Root); + + _startupProfiler = null; + } } } @@ -82,5 +124,15 @@ namespace Umbraco.Web.Common.Runtime.Profiler if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; return false; } + + /// + /// Indicates the boot phase. + /// + private enum BootPhase + { + Boot = 0, // boot phase (before the 1st request even begins) + BootRequest = 1, // request boot phase (during the 1st request) + Booted = 2 // done booting + } } } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs index 9a656ae4f3..a36753e634 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -51,6 +51,9 @@ namespace Umbraco.Web.Common.Runtime.Profiler _umbracoRequestLifetime.RequestStart += (sender, context) => _profiler.UmbracoApplicationBeginRequest(context); _umbracoRequestLifetime.RequestEnd += (sender, context) => _profiler.UmbracoApplicationEndRequest(context); + + // Stop the profiling of the booting process + _profiler.StopBoot(); } } } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs deleted file mode 100644 index 99ad6f724c..0000000000 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerProvider.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Threading; -using StackExchange.Profiling; -using StackExchange.Profiling.Internal; -using Umbraco.Core.Cache; - -namespace Umbraco.Web.Common.Runtime.Profiler -{ - public class WebProfilerProvider : DefaultProfilerProvider - { - private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); - private volatile BootPhase _bootPhase; - private int _first; - private MiniProfiler _startupProfiler; - - public WebProfilerProvider() - { - // booting... - _bootPhase = BootPhase.Boot; - } - - - /// - /// Gets the current profiler. - /// - /// - /// If the boot phase is not Booted, then this will return the startup profiler (this), otherwise - /// returns the base class - /// - public override MiniProfiler CurrentProfiler - { - get - { - // if not booting then just use base (fast) - // no lock, _bootPhase is volatile - if (_bootPhase == BootPhase.Booted) - return base.CurrentProfiler; - - // else - try - { - var current = base.CurrentProfiler; - return current ?? _startupProfiler; - } - catch - { - return _startupProfiler; - } - } - } - - public void BeginBootRequest() - { - _locker.EnterWriteLock(); - try - { - if (_bootPhase != BootPhase.Boot) - throw new InvalidOperationException("Invalid boot phase."); - _bootPhase = BootPhase.BootRequest; - - // assign the profiler to be the current MiniProfiler for the request - // is's already active, starting and all - CurrentProfiler = _startupProfiler; - } - finally - { - _locker.ExitWriteLock(); - } - } - - public void EndBootRequest() - { - _locker.EnterWriteLock(); - try - { - if (_bootPhase != BootPhase.BootRequest) - throw new InvalidOperationException("Invalid boot phase."); - _bootPhase = BootPhase.Booted; - - _startupProfiler = null; - } - finally - { - _locker.ExitWriteLock(); - } - } - - /// - /// Starts a new MiniProfiler. - /// - /// - /// - /// This is called when WebProfiler calls MiniProfiler.Start() so, - /// - as a result of WebRuntime starting the WebProfiler, and - /// - assuming profiling is enabled, on every BeginRequest that should be profiled, - /// - except for the very first one which is the boot request. - /// - /// - public override MiniProfiler Start(string profilerName, MiniProfilerBaseOptions options) - { - var first = Interlocked.Exchange(ref _first, 1) == 0; - if (first == false) return base.Start(profilerName, options); - - _startupProfiler = new MiniProfiler("StartupProfiler", options); - CurrentProfiler = _startupProfiler; - return _startupProfiler; - } - - /// - /// Indicates the boot phase. - /// - private enum BootPhase - { - Boot = 0, // boot phase (before the 1st request even begins) - BootRequest = 1, // request boot phase (during the 1st request) - Booted = 2 // done booting - } - } -} diff --git a/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json index b145249bb5..27f36121ac 100644 --- a/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json +++ b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json @@ -1,7 +1,7 @@ { "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:36804", "sslPort": 44354 @@ -15,10 +15,11 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Umbraco.Web.UI.BackOffice": { + "Umbraco.Web.UI.NetCore": { "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", + "launchBrowser": false, + "applicationUrl": "https://localhost:44354;http://localhost:9000", + "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index b38333bb9a..e0440e39ea 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -5,13 +5,19 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using StackExchange.Profiling; using Umbraco.Composing; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Web.BackOffice.AspNetCore; using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Extensions; +using Umbraco.Web.Common.Runtime.Profiler; using Umbraco.Web.Website.AspNetCore; @@ -43,11 +49,19 @@ namespace Umbraco.Web.UI.BackOffice services.AddUmbracoConfiguration(_config); services.AddUmbracoCore(_webHostEnvironment); services.AddUmbracoWebsite(); + + services.AddMvc(); + services.AddMiniProfiler(options => + { + options.ShouldProfile = request => false; // WebProfiler determine and start profiling. We should not use the MiniProfilerMiddleware to also profile + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + + // app.UseMiniProfiler(); app.UseUmbracoRequest(); if (env.IsDevelopment()) { @@ -59,9 +73,17 @@ namespace Umbraco.Web.UI.BackOffice app.UseUmbracoBackOffice(); app.UseRouting(); - app.UseEndpoints(endpoints => { + endpoints.MapControllerRoute( + "backoffice-spa", + "/umbraco", + new + { + Controller = "BackOffice", + Action = "Index" + } + ); endpoints.MapGet("/", async context => { await context.Response.WriteAsync($"Hello World!{Current.Profiler.Render()}"); @@ -69,4 +91,25 @@ namespace Umbraco.Web.UI.BackOffice }); } } + + public class UmbracoMiniProfilerOptions : MiniProfilerOptions + { + public UmbracoMiniProfilerOptions() + { + } + + public new MiniProfiler StartProfiler(string profilerName = null) + { + return base.StartProfiler(); + } + + public new Func ShouldProfile => (request) => + { + if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false; + if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug; + if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug; + if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; + return false; + }; + } } diff --git a/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml b/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml new file mode 100644 index 0000000000..1b5007c005 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml @@ -0,0 +1,20 @@ +@using Umbraco.Composing + +@{ + Layout = null; +} + + + + + + title + + +
+ Hello World + @Html.Raw(Current.Profiler.Render()) +
+ + + diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index e39d1658a2..09076400de 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -104,8 +104,11 @@ namespace Umbraco.Web.Editors public async Task Default() { return await RenderDefaultOrProcessExternalLoginAsync( - () => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings,_ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)), - () => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings))); + () => + View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings,_ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)), + () => + View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)) + ); } [HttpGet] From ff5d6770d929e52e7fb2d756f213b8610f1e9902 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 30 Mar 2020 06:34:56 +0200 Subject: [PATCH 07/14] AB#5820 - Clean up and remove unnecessary stuff for this task --- .../Controllers/BackOfficeController.cs | 14 ------------- src/Umbraco.Web.UI.NetCore/Startup.cs | 9 --------- .../Umbraco.Web.UI.NetCore.csproj | 1 + .../Views/BackOffice/Index.cshtml | 20 ------------------- 4 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs delete mode 100644 src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs deleted file mode 100644 index 83f6eda24a..0000000000 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Umbraco.Composing; - -namespace Umbraco.Web.BackOffice.Controllers -{ - public class BackOfficeController : Controller - { - // GET - public IActionResult Index() - { - return View(); - } - } -} diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index e0440e39ea..f674832952 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -75,15 +75,6 @@ namespace Umbraco.Web.UI.BackOffice app.UseRouting(); app.UseEndpoints(endpoints => { - endpoints.MapControllerRoute( - "backoffice-spa", - "/umbraco", - new - { - Controller = "BackOffice", - Action = "Index" - } - ); endpoints.MapGet("/", async context => { await context.Response.WriteAsync($"Hello World!{Current.Profiler.Render()}"); diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index abfb172763..0e6f2cf6b0 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -18,6 +18,7 @@ <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.hash" /> <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.list" /> + <_ContentIncludedByDefault Remove="Views\BackOffice\Index.cshtml" /> diff --git a/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml b/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml deleted file mode 100644 index 1b5007c005..0000000000 --- a/src/Umbraco.Web.UI.NetCore/Views/BackOffice/Index.cshtml +++ /dev/null @@ -1,20 +0,0 @@ -@using Umbraco.Composing - -@{ - Layout = null; -} - - - - - - title - - -
- Hello World - @Html.Raw(Current.Profiler.Render()) -
- - - From 8ed7ae470ad541986c9dff64ede3c099f4d19701 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 30 Mar 2020 06:38:39 +0200 Subject: [PATCH 08/14] AB#5820 - Clean up --- src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs index a05483633d..431c195d90 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -13,12 +13,8 @@ namespace Umbraco.Web.Common.Runtime.Profiler { public class WebProfiler : IProfiler { - private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); - private volatile BootPhase _bootPhase; private MiniProfiler _startupProfiler; - - private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest"; private readonly IHttpContextAccessor _httpContextAccessor; private int _first; @@ -26,7 +22,6 @@ namespace Umbraco.Web.Common.Runtime.Profiler { // create our own provider, which can provide a profiler even during boot _httpContextAccessor = httpContextAccessor; - _bootPhase = BootPhase.Boot; } /// From 2fc21667672d66ad755e8291af058deecaca3a97 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 30 Mar 2020 07:14:22 +0200 Subject: [PATCH 09/14] AB#5820 - Clean up --- src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs | 4 ++++ .../AspNetCoreUmbracoApplicationLifetime.cs | 2 +- .../Lifetime/IUmbracoRequestLifetimeManager.cs | 2 +- .../Runtime/AspNetCoreComponent.cs | 8 ++++---- .../Runtime/AspNetCoreComposer.cs | 13 ++++++++----- .../Runtime/Profiler/WebProfilerComponent.cs | 4 ++-- .../AspNet/AspNetUmbracoApplicationLifetime.cs | 4 ++-- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs index c84d475e90..f594e45652 100644 --- a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -15,7 +15,11 @@ namespace Umbraco.Net void Restart(); event EventHandler ApplicationInit; + } + + public interface IUmbracoApplicationLifetimeManager: IUmbracoApplicationLifetime + { void InvokeApplicationInit(); } } diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index d73b9bebd4..38d78860b0 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -6,7 +6,7 @@ using Umbraco.Net; namespace Umbraco.Web.Common.AspNetCore { - public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime + public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHostApplicationLifetime _hostApplicationLifetime; diff --git a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs index e4c671c2d3..7ee8fac1e9 100644 --- a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs +++ b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs @@ -2,7 +2,7 @@ namespace Umbraco.Web.Common.Lifetime { - public interface IUmbracoRequestLifetimeManager + public interface IUmbracoRequestLifetimeManager : IUmbracoRequestLifetime { void InitRequest(HttpContext context); void EndRequest(HttpContext context); diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs index 04ae022fa5..991dce55e6 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs @@ -7,18 +7,18 @@ namespace Umbraco.Web.Common.Runtime public sealed class AspNetCoreComponent : IComponent { private readonly IHostApplicationLifetime _hostApplicationLifetime; - private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IUmbracoApplicationLifetimeManager _umbracoApplicationLifetimeManager; - public AspNetCoreComponent(IHostApplicationLifetime hostApplicationLifetime, IUmbracoApplicationLifetime umbracoApplicationLifetime) + public AspNetCoreComponent(IHostApplicationLifetime hostApplicationLifetime, IUmbracoApplicationLifetimeManager umbracoApplicationLifetimeManager) { _hostApplicationLifetime = hostApplicationLifetime; - _umbracoApplicationLifetime = umbracoApplicationLifetime; + _umbracoApplicationLifetimeManager = umbracoApplicationLifetimeManager; } public void Initialize() { _hostApplicationLifetime.ApplicationStarted.Register(() => { - _umbracoApplicationLifetime.InvokeApplicationInit(); + _umbracoApplicationLifetimeManager.InvokeApplicationInit(); }); } diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index f9a6a770e2..a0b2fb9dd8 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -24,14 +24,17 @@ namespace Umbraco.Web.Common.Runtime composition.RegisterUnique(); // Our own netcore implementations - composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(); // The umbraco request lifetime - var umbracoRequestLifetime = new UmbracoRequestLifetime(); - composition.RegisterUnique(factory => umbracoRequestLifetime); - composition.RegisterUnique(factory => umbracoRequestLifetime); - composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs index a36753e634..7f0b5071c5 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -11,11 +11,11 @@ namespace Umbraco.Web.Common.Runtime.Profiler { private readonly bool _profile; private readonly WebProfiler _profiler; - private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IUmbracoApplicationLifetimeManager _umbracoApplicationLifetime; private readonly IUmbracoRequestLifetime _umbracoRequestLifetime; public WebProfilerComponent(IProfiler profiler, ILogger logger, IUmbracoRequestLifetime umbracoRequestLifetime, - IUmbracoApplicationLifetime umbracoApplicationLifetime) + IUmbracoApplicationLifetimeManager umbracoApplicationLifetime) { _umbracoRequestLifetime = umbracoRequestLifetime; _umbracoApplicationLifetime = umbracoApplicationLifetime; diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs index 9818a510b2..f0ff6e3cad 100644 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -5,7 +5,7 @@ using Umbraco.Net; namespace Umbraco.Web.AspNet { - public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime + public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager { private readonly IHttpContextAccessor _httpContextAccessor; @@ -37,7 +37,7 @@ namespace Umbraco.Web.AspNet public event EventHandler ApplicationInit; public void InvokeApplicationInit() { - + ApplicationInit?.Invoke(this, EventArgs.Empty); } } } From 49da01c65579e1aec575a30bd27c81b8e937c59c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 30 Mar 2020 07:16:55 +0200 Subject: [PATCH 10/14] AB#5820 - Clean up --- src/Umbraco.Web.UI.NetCore/Startup.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index f674832952..3906f1c8e9 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -82,25 +82,4 @@ namespace Umbraco.Web.UI.BackOffice }); } } - - public class UmbracoMiniProfilerOptions : MiniProfilerOptions - { - public UmbracoMiniProfilerOptions() - { - } - - public new MiniProfiler StartProfiler(string profilerName = null) - { - return base.StartProfiler(); - } - - public new Func ShouldProfile => (request) => - { - if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false; - if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug; - if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug; - if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; - return false; - }; - } } From 1e33b3df7a5558d8f9cfb253afc71631b3a6a33f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 30 Mar 2020 07:17:56 +0200 Subject: [PATCH 11/14] AB#5820 - Clean up --- src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 0e6f2cf6b0..abfb172763 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -18,7 +18,6 @@ <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.hash" /> <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.list" /> - <_ContentIncludedByDefault Remove="Views\BackOffice\Index.cshtml" /> From 4ac8ae3b1520acd0f7281239d8dc7296ca3b3892 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 30 Mar 2020 15:29:53 +0200 Subject: [PATCH 12/14] Removing specific to each machine file paths from project --- src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index abfb172763..dc148d8d1f 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -15,9 +15,4 @@ - - <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.hash" /> - <_ContentIncludedByDefault Remove="wwwroot\~\App_Data\TEMP\TypesCache\umbraco-types.DESKTOP-2016.list" /> - - From 2a2903f932645256ad36fdaa90fec547a8f9a8b1 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 31 Mar 2020 12:22:11 +0200 Subject: [PATCH 13/14] AB#5820 - Review fixes / Clean up --- src/Umbraco.Tests.Integration/RuntimeTests.cs | 7 ++----- .../Testing/UmbracoIntegrationTest.cs | 3 +-- .../Umbraco.Tests.Integration.csproj | 1 + .../AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs | 2 +- .../UmbracoCoreServiceCollectionExtensions.cs | 3 ++- .../Lifetime/UmbracoRequestLifetime.cs | 2 +- src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs | 10 ---------- .../AspNet/AspNetUmbracoApplicationLifetime.cs | 2 +- 8 files changed, 9 insertions(+), 21 deletions(-) rename src/Umbraco.Web.Common/{AspNetCore => Extensions}/UmbracoCoreServiceCollectionExtensions.cs (99%) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 52a4ac1062..2974a01669 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -1,10 +1,7 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Moq; using NUnit.Framework; -using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Umbraco.Core; @@ -16,7 +13,7 @@ using Umbraco.Tests.Integration.Extensions; using Umbraco.Tests.Integration.Implementations; using Umbraco.Tests.Integration.Testing; using Umbraco.Web.BackOffice.AspNetCore; -using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Common.Extensions; namespace Umbraco.Tests.Integration { diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 3d94e52860..43504e908a 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data.Common; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; @@ -20,8 +19,8 @@ using Umbraco.Core.Strings; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Integration.Extensions; using Umbraco.Tests.Integration.Implementations; -using Umbraco.Tests.Testing; using Umbraco.Web.BackOffice.AspNetCore; +using Umbraco.Web.Common.Extensions; namespace Umbraco.Tests.Integration.Testing { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 320db33568..59b7de4cf4 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -4,6 +4,7 @@ Exe netcoreapp3.1 false + 8 diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index 38d78860b0..c8d8f40a00 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -6,7 +6,7 @@ using Umbraco.Net; namespace Umbraco.Web.Common.AspNetCore { - public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager + public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetimeManager { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHostApplicationLifetime _hostApplicationLifetime; diff --git a/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs similarity index 99% rename from src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs rename to src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs index 99a274364b..6f2b31bac1 100644 --- a/src/Umbraco.Web.Common/AspNetCore/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs @@ -17,9 +17,10 @@ using Umbraco.Core.Logging; using Umbraco.Core.Logging.Serilog; using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; +using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Runtime.Profiler; -namespace Umbraco.Web.Common.AspNetCore +namespace Umbraco.Web.Common.Extensions { public static class UmbracoCoreServiceCollectionExtensions { diff --git a/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs index 43810a9e66..0d858679a6 100644 --- a/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs +++ b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http; namespace Umbraco.Web.Common.Lifetime { - public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager + public class UmbracoRequestLifetime : IUmbracoRequestLifetimeManager { public event EventHandler RequestStart; public event EventHandler RequestEnd; diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs index 431c195d90..bdbc6f164d 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs @@ -119,15 +119,5 @@ namespace Umbraco.Web.Common.Runtime.Profiler if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug; return false; } - - /// - /// Indicates the boot phase. - /// - private enum BootPhase - { - Boot = 0, // boot phase (before the 1st request even begins) - BootRequest = 1, // request boot phase (during the 1st request) - Booted = 2 // done booting - } } } diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs index f0ff6e3cad..8c43293ad7 100644 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -5,7 +5,7 @@ using Umbraco.Net; namespace Umbraco.Web.AspNet { - public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager + public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetimeManager { private readonly IHttpContextAccessor _httpContextAccessor; From b9ad6a0b7c386bdeac51c9aabd7bf6a01bb8f1a4 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 31 Mar 2020 15:02:02 +0200 Subject: [PATCH 14/14] AB#5820 - Cleanup --- src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs | 2 +- .../AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs | 2 +- .../Lifetime/IUmbracoRequestLifetimeManager.cs | 2 +- src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs | 2 +- src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs | 1 - .../Runtime/Profiler/WebProfilerComponent.cs | 4 ++-- src/Umbraco.Web/Logging/WebProfilerComposer.cs | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs index f594e45652..a032720d46 100644 --- a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -18,7 +18,7 @@ namespace Umbraco.Net } - public interface IUmbracoApplicationLifetimeManager: IUmbracoApplicationLifetime + public interface IUmbracoApplicationLifetimeManager { void InvokeApplicationInit(); } diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index c8d8f40a00..38d78860b0 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -6,7 +6,7 @@ using Umbraco.Net; namespace Umbraco.Web.Common.AspNetCore { - public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetimeManager + public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHostApplicationLifetime _hostApplicationLifetime; diff --git a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs index 7ee8fac1e9..e4c671c2d3 100644 --- a/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs +++ b/src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetimeManager.cs @@ -2,7 +2,7 @@ namespace Umbraco.Web.Common.Lifetime { - public interface IUmbracoRequestLifetimeManager : IUmbracoRequestLifetime + public interface IUmbracoRequestLifetimeManager { void InitRequest(HttpContext context); void EndRequest(HttpContext context); diff --git a/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs index 0d858679a6..43810a9e66 100644 --- a/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs +++ b/src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http; namespace Umbraco.Web.Common.Lifetime { - public class UmbracoRequestLifetime : IUmbracoRequestLifetimeManager + public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager { public event EventHandler RequestStart; public event EventHandler RequestEnd; diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index a0b2fb9dd8..6c5aeb1b56 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -34,7 +34,6 @@ namespace Umbraco.Web.Common.Runtime composition.RegisterUnique(); composition.RegisterUnique(factory => factory.GetInstance()); composition.RegisterUnique(factory => factory.GetInstance()); - composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs index 7f0b5071c5..a36753e634 100644 --- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs +++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComponent.cs @@ -11,11 +11,11 @@ namespace Umbraco.Web.Common.Runtime.Profiler { private readonly bool _profile; private readonly WebProfiler _profiler; - private readonly IUmbracoApplicationLifetimeManager _umbracoApplicationLifetime; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IUmbracoRequestLifetime _umbracoRequestLifetime; public WebProfilerComponent(IProfiler profiler, ILogger logger, IUmbracoRequestLifetime umbracoRequestLifetime, - IUmbracoApplicationLifetimeManager umbracoApplicationLifetime) + IUmbracoApplicationLifetime umbracoApplicationLifetime) { _umbracoRequestLifetime = umbracoRequestLifetime; _umbracoApplicationLifetime = umbracoApplicationLifetime; diff --git a/src/Umbraco.Web/Logging/WebProfilerComposer.cs b/src/Umbraco.Web/Logging/WebProfilerComposer.cs index 5834dd9dd4..283c519b44 100644 --- a/src/Umbraco.Web/Logging/WebProfilerComposer.cs +++ b/src/Umbraco.Web/Logging/WebProfilerComposer.cs @@ -1,4 +1,4 @@ -using Umbraco.Core.Composing; + using Umbraco.Core.Composing; namespace Umbraco.Web.Logging {