From da58b7a74391438c8565018504f6883bfa41be29 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Mar 2020 23:28:24 +1100 Subject: [PATCH 01/20] Adds integration test project for netcore3 and poc for crosswiring di container with msdi --- .../LightInject/LightInjectContainer.cs | 9 +- .../Composing/RegisterFactory.cs | 2 + .../CrossWireContainer.cs | 83 +++++++++++++++++++ .../Umbraco.Tests.Integration.csproj | 26 ++++++ src/umbraco.sln | 11 ++- 5 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Tests.Integration/CrossWireContainer.cs create mode 100644 src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj diff --git a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs index 5200dced90..9cd54fe7b8 100644 --- a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs @@ -16,9 +16,9 @@ namespace Umbraco.Core.Composing.LightInject /// /// Initializes a new instance of the with a LightInject container. /// - protected LightInjectContainer(ServiceContainer container) + public LightInjectContainer(ServiceContainer container) { - Container = container; + Container = ConfigureContainer(container); } /// @@ -33,7 +33,12 @@ namespace Umbraco.Core.Composing.LightInject protected static ServiceContainer CreateServiceContainer() { var container = new ServiceContainer(new ContainerOptions { EnablePropertyInjection = false }); + ConfigureContainer(container); + return container; + } + private static ServiceContainer ConfigureContainer(ServiceContainer container) + { // note: the block below is disabled, as it is too LightInject-specific // // supports annotated constructor injections diff --git a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs index 8f842e14fe..4a73795f68 100644 --- a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs @@ -9,6 +9,8 @@ namespace Umbraco.Core.Composing /// public static class RegisterFactory { + //TODO This needs to die + // cannot use typeof().AssemblyQualifiedName on the web container - we don't reference it // a normal Umbraco site should run on the web container, but an app may run on the core one private const string CoreLightInjectContainerTypeName = "Umbraco.Core.Composing.LightInject.LightInjectContainer,Umbraco.Core"; diff --git a/src/Umbraco.Tests.Integration/CrossWireContainer.cs b/src/Umbraco.Tests.Integration/CrossWireContainer.cs new file mode 100644 index 0000000000..1bf5d4fb1c --- /dev/null +++ b/src/Umbraco.Tests.Integration/CrossWireContainer.cs @@ -0,0 +1,83 @@ +using LightInject; +using LightInject.Microsoft.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using System; +using System.IO; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Composing.LightInject; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; + +namespace Umbraco.Tests.Integration +{ + [TestFixture] + public class CrossWireContainer + { + [Test] + public void CrossWire() + { + // MSDI + var services = new ServiceCollection(); + services.AddSingleton(); + var msdiServiceProvider = services.BuildServiceProvider(); + + // LightInject + var liContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); + var liServiceProvider = liContainer.CreateServiceProvider(services); + + // Umbraco + var umbracoContainer = new LightInjectContainer(liContainer); + + // Dependencies needed for creating composition/register essentials + var tempPath = Path.Combine(Path.GetTempPath(), "umbraco-temp-" + Guid.NewGuid()); + if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); + var globalSettings = Mock.Of(); + var typeFinder = Mock.Of(); + var ioHelper = Mock.Of(); + var runtimeCache = NoAppCache.Instance; + var logger = Mock.Of(); + var typeLoader = new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(tempPath), logger, false); + var runtimeState = Mock.Of(); + var configs = new Configs(x => null); + var appCaches = new AppCaches(runtimeCache, NoAppCache.Instance, new IsolatedCaches(type => new ObjectCacheAppCache(typeFinder))); + var profiler = new VoidProfiler(); + var mainDom = Mock.Of(); + var umbracoDatabaseFactory = Mock.Of(); + var umbracoVersion = new UmbracoVersion(); + var dbProviderFactoryCreator = Mock.Of(); + + // Register in the container + var composition = new Composition(umbracoContainer, typeLoader, logger, runtimeState, configs, ioHelper, appCaches); + composition.RegisterEssentials(logger, profiler, logger, mainDom, appCaches, umbracoDatabaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, dbProviderFactoryCreator); + + // Resolve + + // From MSDI + var foo1 = msdiServiceProvider.GetService(); + var foo2 = liServiceProvider.GetService(); + var foo3 = umbracoContainer.GetInstance(); + + Assert.IsNotNull(foo1); + Assert.IsNotNull(foo2); + Assert.IsNotNull(foo3); + + // These are not the same because cross wiring means copying the container, not falling back to a container + Assert.AreNotSame(foo1, foo2); + // These are the same because the umbraco container wraps the light inject container + Assert.AreSame(foo2, foo3); + } + + private class Foo + { + public Foo() + { + } + } + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj new file mode 100644 index 0000000000..5d8ae11ea2 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -0,0 +1,26 @@ + + + + Exe + netcoreapp3.1 + false + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/umbraco.sln b/src/umbraco.sln index f98f7e034e..9f24434789 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -117,9 +117,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Examine.Lucene", "U EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.BackOffice", "Umbraco.Web.BackOffice\Umbraco.Web.BackOffice.csproj", "{9B95EEF7-63FE-4432-8C63-166BE9C1A929}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI.NetCore", "Umbraco.Web.UI.NetCore\Umbraco.Web.UI.NetCore.csproj", "{DCDFE97C-5630-4F6F-855D-8AEEB96556A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.UI.NetCore", "Umbraco.Web.UI.NetCore\Umbraco.Web.UI.NetCore.csproj", "{DCDFE97C-5630-4F6F-855D-8AEEB96556A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.Website", "Umbraco.Web.Website\Umbraco.Web.Website.csproj", "{5A246D54-3109-4D2B-BE7D-FC0787D126AE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Website", "Umbraco.Web.Website\Umbraco.Web.Website.csproj", "{5A246D54-3109-4D2B-BE7D-FC0787D126AE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Integration", "Umbraco.Tests.Integration\Umbraco.Tests.Integration.csproj", "{D6319409-777A-4BD0-93ED-B2DFD805B32C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -189,6 +191,10 @@ Global {5A246D54-3109-4D2B-BE7D-FC0787D126AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A246D54-3109-4D2B-BE7D-FC0787D126AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A246D54-3109-4D2B-BE7D-FC0787D126AE}.Release|Any CPU.Build.0 = Release|Any CPU + {D6319409-777A-4BD0-93ED-B2DFD805B32C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6319409-777A-4BD0-93ED-B2DFD805B32C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6319409-777A-4BD0-93ED-B2DFD805B32C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6319409-777A-4BD0-93ED-B2DFD805B32C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -202,6 +208,7 @@ Global {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {C7311C00-2184-409B-B506-52A5FAEA8736} = {FD962632-184C-4005-A5F3-E705D92FC645} {FB5676ED-7A69-492C-B802-E7B24144C0FC} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} + {D6319409-777A-4BD0-93ED-B2DFD805B32C} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC} From 4cc20b06bc67be76b29f2ad3cc1c97d25c3fd4f2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 11:13:47 +1100 Subject: [PATCH 02/20] updates lightinject, adds method to create an LI container from an MSDI collection, updates a test --- .../Logging}/ConsoleLogger.cs | 3 +- src/Umbraco.Core/Logging/ILogger.cs | 1 + .../LightInject/LightInjectContainer.cs | 2 + .../Composing/RegisterFactory.cs | 20 ++++++- .../Umbraco.Infrastructure.csproj | 8 ++- ...rossWireContainer.cs => ContainerTests.cs} | 55 +++++++++++++++---- .../Infrastructure/TestBackOfficeInfo.cs | 17 ++++++ .../TestDbProviderFactoryCreator.cs | 34 ++++++++++++ .../Infrastructure/TestGlobalSettings.cs | 49 +++++++++++++++++ .../Infrastructure/TestHostingEnvironment.cs | 49 +++++++++++++++++ .../TestUmbracoBootPermissionChecker.cs | 11 ++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 3 +- .../AspNetCoreHostingEnvironment.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 14 files changed, 236 insertions(+), 21 deletions(-) rename src/{Umbraco.Tests/TestHelpers => Umbraco.Core/Logging}/ConsoleLogger.cs (98%) rename src/Umbraco.Tests.Integration/{CrossWireContainer.cs => ContainerTests.cs} (57%) create mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs create mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs create mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs create mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs create mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs diff --git a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs b/src/Umbraco.Core/Logging/ConsoleLogger.cs similarity index 98% rename from src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs rename to src/Umbraco.Core/Logging/ConsoleLogger.cs index 53d6078e4b..275b8d988b 100644 --- a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs +++ b/src/Umbraco.Core/Logging/ConsoleLogger.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Core.Logging; -namespace Umbraco.Tests.TestHelpers +namespace Umbraco.Core.Logging { public class ConsoleLogger : ILogger { diff --git a/src/Umbraco.Core/Logging/ILogger.cs b/src/Umbraco.Core/Logging/ILogger.cs index 4f49d0b3b4..fe7d798ebf 100644 --- a/src/Umbraco.Core/Logging/ILogger.cs +++ b/src/Umbraco.Core/Logging/ILogger.cs @@ -2,6 +2,7 @@ namespace Umbraco.Core.Logging { + /// /// Defines the logging service. /// diff --git a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs index 9cd54fe7b8..ecc74ec61f 100644 --- a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs @@ -21,6 +21,8 @@ namespace Umbraco.Core.Composing.LightInject Container = ConfigureContainer(container); } + //TODO: The Create methods can die when net framework is gone + /// /// Creates a new instance of the class. /// diff --git a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs index 4a73795f68..1bf3b8187f 100644 --- a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs @@ -1,5 +1,9 @@ -using System; +using LightInject; +using LightInject.Microsoft.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Reflection; +using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; namespace Umbraco.Core.Composing @@ -9,7 +13,19 @@ namespace Umbraco.Core.Composing /// public static class RegisterFactory { - //TODO This needs to die + /// + /// Creates a new based on an existing MSDI IServiceCollection + /// + /// + /// + public static IRegister CreateFrom(IServiceCollection services, out IServiceProvider serviceProvider) + { + var liContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); + serviceProvider = liContainer.CreateServiceProvider(services); + return new LightInjectContainer(liContainer); + } + + //TODO: The following can die when net framework is gone // cannot use typeof().AssemblyQualifiedName on the web container - we don't reference it // a normal Umbraco site should run on the web container, but an app may run on the core one diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 1db36a9c09..797c8bb7fa 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -7,10 +7,12 @@ - + + + @@ -65,4 +67,8 @@ + + + + diff --git a/src/Umbraco.Tests.Integration/CrossWireContainer.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs similarity index 57% rename from src/Umbraco.Tests.Integration/CrossWireContainer.cs rename to src/Umbraco.Tests.Integration/ContainerTests.cs index 1bf5d4fb1c..1157e74bbf 100644 --- a/src/Umbraco.Tests.Integration/CrossWireContainer.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -13,11 +13,44 @@ using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; +using Umbraco.Core.Runtime; +using Umbraco.Tests.Integration.Infrastructure; namespace Umbraco.Tests.Integration { [TestFixture] - public class CrossWireContainer + public class RuntimeTests + { + [Test] + public void CoreRuntime() + { + // MSDI + var services = new ServiceCollection(); + var msdiServiceProvider = services.BuildServiceProvider(); + + // LightInject / Umbraco + var umbracoContainer = RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); + + // Dependencies needed for Core Runtime + var profiler = new VoidProfiler(); + var logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), profiler); + var hostingEnvironment = new TestHostingEnvironment(); + var ioHelper = new IOHelper(hostingEnvironment); + var configs = new Configs(x => null); + var umbracoVersion = new UmbracoVersion(); + var testUmbracoBootPermissionChecker = new TestUmbracoBootPermissionChecker(); + var globalSettings = new TestGlobalSettings(); + var backOfficeInfo = new TestBackOfficeInfo(globalSettings); + var dbFactoryProviderCreator = new TestDbProviderFactoryCreator(); + var mainDom = new SimpleMainDom(); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, testUmbracoBootPermissionChecker, hostingEnvironment, backOfficeInfo, dbFactoryProviderCreator, mainDom); + + var factory = coreRuntime.Boot(umbracoContainer); + } + } + + [TestFixture] + public class ContainerTests { [Test] public void CrossWire() @@ -27,26 +60,24 @@ namespace Umbraco.Tests.Integration services.AddSingleton(); var msdiServiceProvider = services.BuildServiceProvider(); - // LightInject - var liContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); - var liServiceProvider = liContainer.CreateServiceProvider(services); - - // Umbraco - var umbracoContainer = new LightInjectContainer(liContainer); + // LightInject / Umbraco + var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); // Dependencies needed for creating composition/register essentials var tempPath = Path.Combine(Path.GetTempPath(), "umbraco-temp-" + Guid.NewGuid()); if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); var globalSettings = Mock.Of(); - var typeFinder = Mock.Of(); - var ioHelper = Mock.Of(); + + var hostingEnvironment = new TestHostingEnvironment(); + var ioHelper = new IOHelper(hostingEnvironment); var runtimeCache = NoAppCache.Instance; - var logger = Mock.Of(); + var profiler = new VoidProfiler(); + var logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), profiler); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typeLoader = new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(tempPath), logger, false); var runtimeState = Mock.Of(); var configs = new Configs(x => null); var appCaches = new AppCaches(runtimeCache, NoAppCache.Instance, new IsolatedCaches(type => new ObjectCacheAppCache(typeFinder))); - var profiler = new VoidProfiler(); var mainDom = Mock.Of(); var umbracoDatabaseFactory = Mock.Of(); var umbracoVersion = new UmbracoVersion(); @@ -60,7 +91,7 @@ namespace Umbraco.Tests.Integration // From MSDI var foo1 = msdiServiceProvider.GetService(); - var foo2 = liServiceProvider.GetService(); + var foo2 = lightInjectServiceProvider.GetService(); var foo3 = umbracoContainer.GetInstance(); Assert.IsNotNull(foo1); diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs new file mode 100644 index 0000000000..a32dc5ac13 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs @@ -0,0 +1,17 @@ +using Umbraco.Core; +using Umbraco.Core.Configuration; + +namespace Umbraco.Tests.Integration.Infrastructure +{ + + public class TestBackOfficeInfo : IBackOfficeInfo + { + public TestBackOfficeInfo(IGlobalSettings globalSettings) + { + GetAbsoluteUrl = globalSettings.UmbracoPath; + } + + public string GetAbsoluteUrl { get; } + + } +} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs new file mode 100644 index 0000000000..f21c86c50e --- /dev/null +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs @@ -0,0 +1,34 @@ +using System.Data.Common; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Tests.Integration.Infrastructure +{ + public class TestDbProviderFactoryCreator : IDbProviderFactoryCreator + { + public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName) + { + throw new System.NotImplementedException(); + } + + public void CreateDatabase() + { + throw new System.NotImplementedException(); + } + + public DbProviderFactory CreateFactory() + { + throw new System.NotImplementedException(); + } + + public DbProviderFactory CreateFactory(string providerName) + { + throw new System.NotImplementedException(); + } + + public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs new file mode 100644 index 0000000000..2123de8c14 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs @@ -0,0 +1,49 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Tests.Integration.Infrastructure +{ + public class TestGlobalSettings : IGlobalSettings + { + public string ReservedUrls => throw new System.NotImplementedException(); + + public string ReservedPaths => throw new System.NotImplementedException(); + + public string Path => throw new System.NotImplementedException(); + + public string ConfigurationStatus { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public int TimeOutInMinutes => throw new System.NotImplementedException(); + + public string DefaultUILanguage => throw new System.NotImplementedException(); + + public bool HideTopLevelNodeFromPath => throw new System.NotImplementedException(); + + public bool UseHttps => throw new System.NotImplementedException(); + + public int VersionCheckPeriod => throw new System.NotImplementedException(); + + public string UmbracoPath => "/Umbraco"; + + public string UmbracoCssPath => throw new System.NotImplementedException(); + + public string UmbracoScriptsPath => throw new System.NotImplementedException(); + + public string UmbracoMediaPath => throw new System.NotImplementedException(); + + public bool IsSmtpServerConfigured => throw new System.NotImplementedException(); + + public ISmtpSettings SmtpSettings => throw new System.NotImplementedException(); + + public bool InstallMissingDatabase => throw new System.NotImplementedException(); + + public bool InstallEmptyDatabase => throw new System.NotImplementedException(); + + public bool DisableElectionForSingleServer => throw new System.NotImplementedException(); + + public string RegisterType => throw new System.NotImplementedException(); + + public string DatabaseFactoryServerVersion => throw new System.NotImplementedException(); + + public string MainDomLock => throw new System.NotImplementedException(); + } +} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs new file mode 100644 index 0000000000..4a05bb93c7 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Umbraco.Core; +using Umbraco.Core.Hosting; + +namespace Umbraco.Tests.Integration.Infrastructure +{ + + public class TestHostingEnvironment : IHostingEnvironment + { + public TestHostingEnvironment() + { + var tempPath = Path.Combine(Path.GetTempPath(), "umbraco-temp-" + Guid.NewGuid()); + if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); + LocalTempPath = tempPath; + ApplicationPhysicalPath = tempPath; // same location for now + } + + public string SiteName => "UmbracoIntegrationTests"; + + public string ApplicationId { get; } = Guid.NewGuid().ToString(); + + public string ApplicationPhysicalPath { get; private set; } + + public string LocalTempPath { get; private set; } + + public string ApplicationVirtualPath => "/"; + + public bool IsDebugMode => true; + + public bool IsHosted => false; + + public Version IISVersion => new Version(0, 0); // TODO not necessary IIS + + public string MapPath(string path) => Path.Combine(ApplicationPhysicalPath, path); + + public string ToAbsolute(string virtualPath, string root) => virtualPath.TrimStart('~'); + + public void RegisterObject(IRegisteredObject registeredObject) + { + } + + public void UnregisterObject(IRegisteredObject registeredObject) + { + } + } +} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs new file mode 100644 index 0000000000..f97e4fe0e0 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs @@ -0,0 +1,11 @@ +using Umbraco.Core.Runtime; + +namespace Umbraco.Tests.Integration.Infrastructure +{ + public class TestUmbracoBootPermissionChecker : IUmbracoBootPermissionChecker + { + public void ThrowIfNotPermissions() + { + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 56d36981b3..1b4f05af67 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -84,7 +84,7 @@ 1.8.14 - + @@ -210,7 +210,6 @@ - diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs index 5cd2b590c8..719b7d1323 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -83,9 +83,10 @@ namespace Umbraco.Web.BackOffice.AspNetCore } } - + // TODO: This may need to take into account ~/ paths which means the ApplicationVirtualPath and is this the content root or web root? public string MapPath(string path) => Path.Combine(_webHostEnvironment.WebRootPath, path); + // TODO: Need to take into account 'root' here public string ToAbsolute(string virtualPath, string root) { if (Uri.TryCreate(virtualPath, UriKind.Absolute, out _)) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e9a4e04e58..6f5764f36e 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -71,7 +71,7 @@ 2.7.0.100 - + From ef99d434d87e691aa67c6163f2026f0b1699eef3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 11:15:02 +1100 Subject: [PATCH 03/20] update ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 12ad3299ad..8ff5a8ef25 100644 --- a/.gitignore +++ b/.gitignore @@ -164,3 +164,5 @@ build/temp/ # eof /src/Umbraco.Web.UI.Client/TESTS-*.xml /src/ApiDocs/api/* +/src/Umbraco.Web.UI.NetCore/wwwroot/Media/* +/src/Umbraco.Web.UI.NetCore/wwwroot/is-cache/* From a01b06fc6478803615cf70dda8a3c226ea9cf8cc Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 11:17:16 +1100 Subject: [PATCH 04/20] fix build after merge --- .../Infrastructure/TestGlobalSettings.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs index 2123de8c14..222e3a5571 100644 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs +++ b/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs @@ -45,5 +45,7 @@ namespace Umbraco.Tests.Integration.Infrastructure public string DatabaseFactoryServerVersion => throw new System.NotImplementedException(); public string MainDomLock => throw new System.NotImplementedException(); + + public string NoNodesViewPath => throw new System.NotImplementedException(); } } From 41163c3c781b3cc29656f41dd2e22bf964348fd3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 14:43:41 +1100 Subject: [PATCH 05/20] Gets CoreRuntime loading/booting in integration project --- .../FindAssembliesWithReferencesTo.cs | 10 ++- src/Umbraco.Core/IO/IOHelper.cs | 2 +- src/Umbraco.Core/Routing/UriUtility.cs | 1 + .../Runtime/CoreRuntime.cs | 38 +-------- src/Umbraco.Tests.Common/TestHelperBase.cs | 44 ++++++---- .../ContainerTests.cs | 62 ++------------ .../TestDbProviderFactoryCreator.cs | 2 +- .../Implementations/TestHelper.cs | 80 +++++++++++++++++++ .../TestUmbracoBootPermissionChecker.cs | 2 +- .../Infrastructure/TestBackOfficeInfo.cs | 17 ---- .../Infrastructure/TestGlobalSettings.cs | 51 ------------ .../Infrastructure/TestHostingEnvironment.cs | 49 ------------ src/Umbraco.Tests.Integration/RuntimeTests.cs | 51 ++++++++++++ .../Umbraco.Tests.Integration.csproj | 1 + .../Routing/RenderRouteHandlerTests.cs | 2 +- .../Runtimes/CoreRuntimeTests.cs | 6 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 5 ++ .../AspNetCore/AspNetCoreIpResolver.cs | 2 +- src/Umbraco.Web/Runtime/WebRuntime.cs | 5 +- src/Umbraco.Web/UmbracoApplication.cs | 2 +- src/Umbraco.Web/UmbracoApplicationBase.cs | 29 ++++++- 21 files changed, 223 insertions(+), 238 deletions(-) rename src/Umbraco.Tests.Integration/{Infrastructure => Implementations}/TestDbProviderFactoryCreator.cs (94%) create mode 100644 src/Umbraco.Tests.Integration/Implementations/TestHelper.cs rename src/Umbraco.Tests.Integration/{Infrastructure => Implementations}/TestUmbracoBootPermissionChecker.cs (78%) delete mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs delete mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs delete mode 100644 src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs create mode 100644 src/Umbraco.Tests.Integration/RuntimeTests.cs diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs index 91225ff973..9378941166 100644 --- a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; @@ -42,7 +43,14 @@ namespace Umbraco.Core.Composing { foreach(var target in _targetAssemblies) { - referenceItems.Add(Assembly.Load(target)); + try + { + referenceItems.Add(Assembly.Load(target)); + } + catch (FileNotFoundException) + { + // occurs if we cannot load this ... for example in a test project where we aren't currently referencing Umbraco.Web, etc... + } } } diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index d5e1335f35..8e7dd500f3 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.IO public IOHelper(IHostingEnvironment hostingEnvironment) { - _hostingEnvironment = hostingEnvironment; + _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); } /// diff --git a/src/Umbraco.Core/Routing/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs index 9cb6bf50f6..0c68580204 100644 --- a/src/Umbraco.Core/Routing/UriUtility.cs +++ b/src/Umbraco.Core/Routing/UriUtility.cs @@ -15,6 +15,7 @@ namespace Umbraco.Web public UriUtility(IHostingEnvironment hostingEnvironment) { + if (hostingEnvironment is null) throw new ArgumentNullException(nameof(hostingEnvironment)); ResetAppDomainAppVirtualPath(hostingEnvironment); } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 7b1aff1a4b..5de4c0d795 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -39,7 +39,8 @@ namespace Umbraco.Core.Runtime IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, IDbProviderFactoryCreator dbProviderFactoryCreator, - IMainDom mainDom) + IMainDom mainDom, + ITypeFinder typeFinder) { IOHelper = ioHelper; Configs = configs; @@ -53,6 +54,7 @@ namespace Umbraco.Core.Runtime Logger = logger; MainDom = mainDom; + TypeFinder = typeFinder; // runtime state // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' @@ -89,7 +91,7 @@ namespace Umbraco.Core.Runtime /// /// Gets the /// - protected ITypeFinder TypeFinder { get; private set; } + protected ITypeFinder TypeFinder { get; } /// /// Gets the @@ -110,11 +112,6 @@ namespace Umbraco.Core.Runtime // create and register the essential services // ie the bare minimum required to boot - - TypeFinder = GetTypeFinder(); - if (TypeFinder == null) - throw new InvalidOperationException($"The object returned from {nameof(GetTypeFinder)} cannot be null"); - // the boot loader boots using a container scope, so anything that is PerScope will // be disposed after the boot loader has booted, and anything else will remain. // note that this REQUIRES that perWebRequestScope has NOT been enabled yet, else @@ -254,11 +251,6 @@ namespace Umbraco.Core.Runtime return _factory; } - private IUmbracoVersion GetUmbracoVersion(IGlobalSettings globalSettings) - { - return new UmbracoVersion(globalSettings); - } - protected virtual void ConfigureUnhandledException() { //take care of unhandled exceptions - there is nothing we can do to @@ -365,28 +357,6 @@ namespace Umbraco.Core.Runtime protected virtual IEnumerable GetComposerTypes(TypeLoader typeLoader) => typeLoader.GetTypes(); - /// - /// Gets a - /// - /// - protected virtual ITypeFinder GetTypeFinder() - // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however - // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( - // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website - // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since - // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() - // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. - // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own - // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting - // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance - // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point - // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. - // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are - // getting rid of netframework. - Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())); - - /// /// Gets the application caches. /// diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 3d6b4617b5..e12c69a596 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Reflection; using Moq; using Umbraco.Core; using Umbraco.Core.Cache; @@ -12,7 +13,6 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; -using Umbraco.Core.Runtime; using Umbraco.Core.Serialization; using Umbraco.Core.Strings; using Umbraco.Core.Sync; @@ -27,21 +27,18 @@ namespace Umbraco.Tests.Common /// public abstract class TestHelperBase { - public TestHelperBase() + private readonly ITypeFinder _typeFinder; + private UriUtility _uriUtility; + private IIOHelper _ioHelper; + + public TestHelperBase(Assembly entryAssembly) { SettingsForTests = new SettingsForTests(); - IOHelper = new IOHelper(GetHostingEnvironment()); - MainDom = new MainDom(Mock.Of(), GetHostingEnvironment(), new MainDomSemaphoreLock(Mock.Of(), GetHostingEnvironment())); - UriUtility = new UriUtility(GetHostingEnvironment()); + MainDom = new SimpleMainDom(); + _typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(entryAssembly)); } - public ITypeFinder GetTypeFinder() - { - - var typeFinder = new TypeFinder(Mock.Of(), - new DefaultUmbracoAssemblyProvider(typeof(TestHelperBase).Assembly)); - return typeFinder; - } + public ITypeFinder GetTypeFinder() => _typeFinder; public TypeLoader GetMockedTypeLoader() { @@ -93,12 +90,29 @@ namespace Umbraco.Tests.Common public abstract IDbProviderFactoryCreator DbProviderFactoryCreator { get; } public abstract IBulkSqlInsertProvider BulkSqlInsertProvider { get; } public abstract IMarchal Marchal { get; } - public ICoreDebug CoreDebug { get; } = new CoreDebug(); + public ICoreDebug CoreDebug { get; } = new CoreDebug(); + public IIOHelper IOHelper + { + get + { + if (_ioHelper == null) + _ioHelper = new IOHelper(GetHostingEnvironment()); + return _ioHelper; + } + } - public IIOHelper IOHelper { get; } public IMainDom MainDom { get; } - public UriUtility UriUtility { get; } + public UriUtility UriUtility + { + get + { + if (_uriUtility == null) + _uriUtility = new UriUtility(GetHostingEnvironment()); + return _uriUtility; + } + } + public SettingsForTests SettingsForTests { get; } public IWebRoutingSettings WebRoutingSettings => SettingsForTests.GenerateMockWebRoutingSettings(); diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index 1157e74bbf..336c8007d5 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -1,53 +1,17 @@ using LightInject; -using LightInject.Microsoft.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Moq; using NUnit.Framework; -using System; -using System.IO; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; using Umbraco.Core.Persistence; -using Umbraco.Core.Runtime; -using Umbraco.Tests.Integration.Infrastructure; +using Umbraco.Tests.Integration.Implementations; namespace Umbraco.Tests.Integration { - [TestFixture] - public class RuntimeTests - { - [Test] - public void CoreRuntime() - { - // MSDI - var services = new ServiceCollection(); - var msdiServiceProvider = services.BuildServiceProvider(); - - // LightInject / Umbraco - var umbracoContainer = RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); - - // Dependencies needed for Core Runtime - var profiler = new VoidProfiler(); - var logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), profiler); - var hostingEnvironment = new TestHostingEnvironment(); - var ioHelper = new IOHelper(hostingEnvironment); - var configs = new Configs(x => null); - var umbracoVersion = new UmbracoVersion(); - var testUmbracoBootPermissionChecker = new TestUmbracoBootPermissionChecker(); - var globalSettings = new TestGlobalSettings(); - var backOfficeInfo = new TestBackOfficeInfo(globalSettings); - var dbFactoryProviderCreator = new TestDbProviderFactoryCreator(); - var mainDom = new SimpleMainDom(); - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, testUmbracoBootPermissionChecker, hostingEnvironment, backOfficeInfo, dbFactoryProviderCreator, mainDom); - - var factory = coreRuntime.Boot(umbracoContainer); - } - } [TestFixture] public class ContainerTests @@ -64,28 +28,18 @@ namespace Umbraco.Tests.Integration var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); // Dependencies needed for creating composition/register essentials - var tempPath = Path.Combine(Path.GetTempPath(), "umbraco-temp-" + Guid.NewGuid()); - if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); - var globalSettings = Mock.Of(); - - var hostingEnvironment = new TestHostingEnvironment(); - var ioHelper = new IOHelper(hostingEnvironment); - var runtimeCache = NoAppCache.Instance; - var profiler = new VoidProfiler(); - var logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), profiler); - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); - var typeLoader = new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(tempPath), logger, false); + var testHelper = new TestHelper(); var runtimeState = Mock.Of(); - var configs = new Configs(x => null); - var appCaches = new AppCaches(runtimeCache, NoAppCache.Instance, new IsolatedCaches(type => new ObjectCacheAppCache(typeFinder))); - var mainDom = Mock.Of(); var umbracoDatabaseFactory = Mock.Of(); - var umbracoVersion = new UmbracoVersion(); var dbProviderFactoryCreator = Mock.Of(); + var typeLoader = testHelper.GetMockedTypeLoader(); // Register in the container - var composition = new Composition(umbracoContainer, typeLoader, logger, runtimeState, configs, ioHelper, appCaches); - composition.RegisterEssentials(logger, profiler, logger, mainDom, appCaches, umbracoDatabaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, dbProviderFactoryCreator); + var composition = new Composition(umbracoContainer, typeLoader, + testHelper.Logger, runtimeState, testHelper.GetConfigs(), testHelper.IOHelper, testHelper.AppCaches); + composition.RegisterEssentials(testHelper.Logger, testHelper.Profiler, testHelper.Logger, testHelper.MainDom, + testHelper.AppCaches, umbracoDatabaseFactory, typeLoader, runtimeState, testHelper.GetTypeFinder(), + testHelper.IOHelper, testHelper.GetUmbracoVersion(), dbProviderFactoryCreator); // Resolve diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs b/src/Umbraco.Tests.Integration/Implementations/TestDbProviderFactoryCreator.cs similarity index 94% rename from src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs rename to src/Umbraco.Tests.Integration/Implementations/TestDbProviderFactoryCreator.cs index f21c86c50e..9081c4ccb4 100644 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestDbProviderFactoryCreator.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestDbProviderFactoryCreator.cs @@ -2,7 +2,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; -namespace Umbraco.Tests.Integration.Infrastructure +namespace Umbraco.Tests.Integration.Implementations { public class TestDbProviderFactoryCreator : IDbProviderFactoryCreator { diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs new file mode 100644 index 0000000000..7fe18843f7 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -0,0 +1,80 @@ + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Hosting; +using Moq; +using System.Net; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Diagnostics; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Runtime; +using Umbraco.Net; +using Umbraco.Tests.Common; +using Umbraco.Web.BackOffice; +using Umbraco.Web.BackOffice.AspNetCore; +using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment; + +namespace Umbraco.Tests.Integration.Implementations +{ + + public class TestHelper : TestHelperBase + { + private IBackOfficeInfo _backOfficeInfo; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IIpResolver _ipResolver; + private readonly IWebHostEnvironment _hostEnvironment; + private readonly IHttpContextAccessor _httpContextAccessor; + + public TestHelper() : base(typeof(TestHelper).Assembly) + { + var httpContext = new DefaultHttpContext(); + httpContext.Connection.RemoteIpAddress = IPAddress.Parse("127.0.0.1"); + _httpContextAccessor = Mock.Of(x => x.HttpContext == httpContext); + _ipResolver = new AspNetIpResolver(_httpContextAccessor); + + _hostEnvironment = Mock.Of(x => + x.ApplicationName == "UmbracoIntegrationTests" + && x.ContentRootPath == CurrentAssemblyDirectory + && x.WebRootPath == CurrentAssemblyDirectory); // same folder for now? + + _hostingEnvironment = new AspNetCoreHostingEnvironment( + SettingsForTests.GetDefaultHostingSettings(), + _hostEnvironment, + _httpContextAccessor, + Mock.Of()); + + Logger = new ProfilingLogger(new ConsoleLogger(new MessageTemplates()), Profiler); + } + + public IUmbracoBootPermissionChecker UmbracoBootPermissionChecker { get; } = new TestUmbracoBootPermissionChecker(); + + public AppCaches AppCaches { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(type => NoAppCache.Instance)); + + public IProfilingLogger Logger { get; private set; } + + public IProfiler Profiler { get; } = new VoidProfiler(); + + public IHttpContextAccessor GetHttpContextAccessor() => _httpContextAccessor; + + public IWebHostEnvironment GetWebHostEnvironment() => _hostEnvironment; + + public override IDbProviderFactoryCreator DbProviderFactoryCreator => new TestDbProviderFactoryCreator(); + + public override IBulkSqlInsertProvider BulkSqlInsertProvider => new SqlServerBulkSqlInsertProvider(); + + public override IMarchal Marchal { get; } = new AspNetCoreMarchal(); + + public override IBackOfficeInfo GetBackOfficeInfo() + { + if (_backOfficeInfo == null) + _backOfficeInfo = new AspNetCoreBackOfficeInfo(SettingsForTests.GetDefaultGlobalSettings(GetUmbracoVersion(), IOHelper)); + return _backOfficeInfo; + } + + public override IHostingEnvironment GetHostingEnvironment() => _hostingEnvironment; + + public override IIpResolver GetIpResolver() => _ipResolver; + } +} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs b/src/Umbraco.Tests.Integration/Implementations/TestUmbracoBootPermissionChecker.cs similarity index 78% rename from src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs rename to src/Umbraco.Tests.Integration/Implementations/TestUmbracoBootPermissionChecker.cs index f97e4fe0e0..b4f876fc66 100644 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestUmbracoBootPermissionChecker.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestUmbracoBootPermissionChecker.cs @@ -1,6 +1,6 @@ using Umbraco.Core.Runtime; -namespace Umbraco.Tests.Integration.Infrastructure +namespace Umbraco.Tests.Integration.Implementations { public class TestUmbracoBootPermissionChecker : IUmbracoBootPermissionChecker { diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs deleted file mode 100644 index a32dc5ac13..0000000000 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestBackOfficeInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Umbraco.Core; -using Umbraco.Core.Configuration; - -namespace Umbraco.Tests.Integration.Infrastructure -{ - - public class TestBackOfficeInfo : IBackOfficeInfo - { - public TestBackOfficeInfo(IGlobalSettings globalSettings) - { - GetAbsoluteUrl = globalSettings.UmbracoPath; - } - - public string GetAbsoluteUrl { get; } - - } -} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs deleted file mode 100644 index 222e3a5571..0000000000 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestGlobalSettings.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Umbraco.Core.Configuration; - -namespace Umbraco.Tests.Integration.Infrastructure -{ - public class TestGlobalSettings : IGlobalSettings - { - public string ReservedUrls => throw new System.NotImplementedException(); - - public string ReservedPaths => throw new System.NotImplementedException(); - - public string Path => throw new System.NotImplementedException(); - - public string ConfigurationStatus { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - - public int TimeOutInMinutes => throw new System.NotImplementedException(); - - public string DefaultUILanguage => throw new System.NotImplementedException(); - - public bool HideTopLevelNodeFromPath => throw new System.NotImplementedException(); - - public bool UseHttps => throw new System.NotImplementedException(); - - public int VersionCheckPeriod => throw new System.NotImplementedException(); - - public string UmbracoPath => "/Umbraco"; - - public string UmbracoCssPath => throw new System.NotImplementedException(); - - public string UmbracoScriptsPath => throw new System.NotImplementedException(); - - public string UmbracoMediaPath => throw new System.NotImplementedException(); - - public bool IsSmtpServerConfigured => throw new System.NotImplementedException(); - - public ISmtpSettings SmtpSettings => throw new System.NotImplementedException(); - - public bool InstallMissingDatabase => throw new System.NotImplementedException(); - - public bool InstallEmptyDatabase => throw new System.NotImplementedException(); - - public bool DisableElectionForSingleServer => throw new System.NotImplementedException(); - - public string RegisterType => throw new System.NotImplementedException(); - - public string DatabaseFactoryServerVersion => throw new System.NotImplementedException(); - - public string MainDomLock => throw new System.NotImplementedException(); - - public string NoNodesViewPath => throw new System.NotImplementedException(); - } -} diff --git a/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs deleted file mode 100644 index 4a05bb93c7..0000000000 --- a/src/Umbraco.Tests.Integration/Infrastructure/TestHostingEnvironment.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Umbraco.Core; -using Umbraco.Core.Hosting; - -namespace Umbraco.Tests.Integration.Infrastructure -{ - - public class TestHostingEnvironment : IHostingEnvironment - { - public TestHostingEnvironment() - { - var tempPath = Path.Combine(Path.GetTempPath(), "umbraco-temp-" + Guid.NewGuid()); - if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); - LocalTempPath = tempPath; - ApplicationPhysicalPath = tempPath; // same location for now - } - - public string SiteName => "UmbracoIntegrationTests"; - - public string ApplicationId { get; } = Guid.NewGuid().ToString(); - - public string ApplicationPhysicalPath { get; private set; } - - public string LocalTempPath { get; private set; } - - public string ApplicationVirtualPath => "/"; - - public bool IsDebugMode => true; - - public bool IsHosted => false; - - public Version IISVersion => new Version(0, 0); // TODO not necessary IIS - - public string MapPath(string path) => Path.Combine(ApplicationPhysicalPath, path); - - public string ToAbsolute(string virtualPath, string root) => virtualPath.TrimStart('~'); - - public void RegisterObject(IRegisteredObject registeredObject) - { - } - - public void UnregisterObject(IRegisteredObject registeredObject) - { - } - } -} diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs new file mode 100644 index 0000000000..2823761afd --- /dev/null +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Runtime; +using Umbraco.Tests.Integration.Implementations; + +namespace Umbraco.Tests.Integration +{ + [TestFixture] + public class RuntimeTests + { + [Test] + public void BootCoreRuntime() + { + // MSDI + var services = new ServiceCollection(); + + // LightInject / Umbraco + var umbracoContainer = RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); + + // Dependencies needed for Core Runtime + var testHelper = new TestHelper(); + + var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(), + testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker, + testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator, + testHelper.MainDom, testHelper.GetTypeFinder()); + + var factory = coreRuntime.Boot(umbracoContainer); + + Assert.IsTrue(coreRuntime.MainDom.IsMainDom); + Assert.IsNull(coreRuntime.State.BootFailedException); + Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); + Assert.IsTrue(MyComposer.IsComposed); + } + } + + [RuntimeLevel(MinLevel = RuntimeLevel.Install)] + public class MyComposer : IUserComposer + { + public void Compose(Composition composition) + { + IsComposed = true; + } + + public static bool IsComposed { get; private set; } + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 5d8ae11ea2..55b3e8cdca 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 5698d41009..2bbdd1534e 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Routing public class TestRuntime : WebRuntime { public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) + : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder()) { } diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 9fb77ef59d..c230976c98 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -120,15 +120,11 @@ namespace Umbraco.Tests.Runtimes public class TestRuntime : CoreRuntime { public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom) + :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder()) { } - // override because we cannot use Assembly.GetEntryAssembly in Nunit tests since that is always null - protected override ITypeFinder GetTypeFinder() - => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); - // must override the database factory // else BootFailedException because U cannot connect to the configured db protected internal override IUmbracoDatabaseFactory GetDatabaseFactory() diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index a6e91cc448..a22d69d68b 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -43,6 +43,11 @@ namespace Umbraco.Tests.TestHelpers private static TestHelperInternal _testHelperInternal = new TestHelperInternal(); private class TestHelperInternal : TestHelperBase { + public TestHelperInternal() : base(typeof(TestHelperInternal).Assembly) + { + + } + public override IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(Constants.DbProviderNames.SqlCe); public override IBulkSqlInsertProvider BulkSqlInsertProvider { get; } = new SqlCeBulkSqlInsertProvider(); diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs index 8f231191f2..cee43757d8 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreIpResolver.cs @@ -3,7 +3,7 @@ using Umbraco.Net; namespace Umbraco.Web.BackOffice.AspNetCore { - internal class AspNetIpResolver : IIpResolver + public class AspNetIpResolver : IIpResolver { private readonly IHttpContextAccessor _httpContextAccessor; diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index ea08dc9135..b4679715f0 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -33,8 +33,9 @@ namespace Umbraco.Web.Runtime IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, IDbProviderFactoryCreator dbProviderFactoryCreator, - IMainDom mainDom): - base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom) + IMainDom mainDom, + ITypeFinder typeFinder): + base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder) { Profiler = GetWebProfiler(); diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 97f06d3c0f..93d3a6210e 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); - return new WebRuntime(configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom); + return new WebRuntime(configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, GetTypeFinder()); } } } diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index a4979777da..6456e8ca1f 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -23,12 +23,10 @@ namespace Umbraco.Web public abstract class UmbracoApplicationBase : HttpApplication { private IRuntime _runtime; - private IFactory _factory; protected UmbracoApplicationBase() { - if (!Umbraco.Composing.Current.IsInitialized) { var configFactory = new ConfigsFactory(); @@ -44,18 +42,41 @@ namespace Umbraco.Web var backOfficeInfo = new AspNetBackOfficeInfo(configs.Global(), ioHelper, logger, configFactory.WebRoutingSettings); var profiler = new LogProfiler(logger); Umbraco.Composing.Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); + Logger = logger; } - } protected UmbracoApplicationBase(ILogger logger, Configs configs, IIOHelper ioHelper, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - { + { if (!Umbraco.Composing.Current.IsInitialized) { + Logger = logger; Umbraco.Composing.Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); } } + protected ILogger Logger { get; } + + /// + /// Gets a + /// + /// + protected virtual ITypeFinder GetTypeFinder() + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( + // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website + // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since + // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() + // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. + // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own + // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting + // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance + // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point + // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. + // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are + // getting rid of netframework. + Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())); /// /// Gets a runtime. From f57a2ec5769c7f3cb9508297a1f160fa1b6daeaf Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 14:45:54 +1100 Subject: [PATCH 06/20] less allocations --- src/Umbraco.Tests.Common/TestHelperBase.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index e12c69a596..7246e9487e 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -28,11 +28,14 @@ namespace Umbraco.Tests.Common public abstract class TestHelperBase { private readonly ITypeFinder _typeFinder; + private readonly IConfigsFactory _configsFactory; private UriUtility _uriUtility; private IIOHelper _ioHelper; + private Configs _configs; public TestHelperBase(Assembly entryAssembly) { + _configsFactory = new ConfigsFactory(); SettingsForTests = new SettingsForTests(); MainDom = new SimpleMainDom(); _typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(entryAssembly)); @@ -47,7 +50,9 @@ namespace Umbraco.Tests.Common public Configs GetConfigs() { - return GetConfigsFactory().Create(IOHelper, Mock.Of()); + if (_configs == null) + _configs = GetConfigsFactory().Create(IOHelper, Mock.Of()); + return _configs; } public IRuntimeState GetRuntimeState() { @@ -64,10 +69,7 @@ namespace Umbraco.Tests.Common public abstract IBackOfficeInfo GetBackOfficeInfo(); - public IConfigsFactory GetConfigsFactory() - { - return new ConfigsFactory(); - } + public IConfigsFactory GetConfigsFactory() => _configsFactory; /// /// Gets the current assembly directory. From c9913f45a0ba024b91c9666c0f9e5aa95396b70c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 15:59:04 +1100 Subject: [PATCH 07/20] Moves LightInjectValidation to common and validates the container as part of new tests --- .../LightInject/LightInjectContainer.cs | 2 +- .../Composing/RegisterFactory.cs | 6 +- src/Umbraco.Tests.Common/Assertions.cs | 35 ++++++ .../Composing/LightInjectValidation.cs | 42 +------ .../Composing/ValidationResultExtensions.cs | 106 +++++++++++++++++ .../ContainerTests.cs | 3 + src/Umbraco.Tests.Integration/RuntimeTests.cs | 28 +++-- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 109 ++---------------- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - 9 files changed, 181 insertions(+), 151 deletions(-) create mode 100644 src/Umbraco.Tests.Common/Assertions.cs rename src/{Umbraco.Tests => Umbraco.Tests.Common}/Composing/LightInjectValidation.cs (92%) create mode 100644 src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs diff --git a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs index ecc74ec61f..a6db0b7b2b 100644 --- a/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Infrastructure/Composing/LightInject/LightInjectContainer.cs @@ -91,7 +91,7 @@ namespace Umbraco.Core.Composing.LightInject /// /// Gets the LightInject container. /// - protected ServiceContainer Container { get; } + public ServiceContainer Container { get; } /// /// diff --git a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs index 1bf3b8187f..9794de733a 100644 --- a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs @@ -20,9 +20,9 @@ namespace Umbraco.Core.Composing /// public static IRegister CreateFrom(IServiceCollection services, out IServiceProvider serviceProvider) { - var liContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); - serviceProvider = liContainer.CreateServiceProvider(services); - return new LightInjectContainer(liContainer); + var lightInjectContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); + serviceProvider = lightInjectContainer.CreateServiceProvider(services); + return new LightInjectContainer(lightInjectContainer); } //TODO: The following can die when net framework is gone diff --git a/src/Umbraco.Tests.Common/Assertions.cs b/src/Umbraco.Tests.Common/Assertions.cs new file mode 100644 index 0000000000..0f99a6a091 --- /dev/null +++ b/src/Umbraco.Tests.Common/Assertions.cs @@ -0,0 +1,35 @@ +using LightInject; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Tests.Common.Composing; + +namespace Umbraco.Tests.Common +{ + public class Assertions + { + public static void AssertContainer(ServiceContainer container, bool reportOnly = false) + { + var results = container.Validate().ToList(); + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) + { + Console.WriteLine($"{resultGroup.Key}: {resultGroup.Count()}"); + } + + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) + { + foreach (var result in resultGroup) + { + Console.WriteLine(); + Console.Write(result.ToText()); + } + } + + if (!reportOnly) + Assert.AreEqual(0, results.Count); + } + + } +} diff --git a/src/Umbraco.Tests/Composing/LightInjectValidation.cs b/src/Umbraco.Tests.Common/Composing/LightInjectValidation.cs similarity index 92% rename from src/Umbraco.Tests/Composing/LightInjectValidation.cs rename to src/Umbraco.Tests.Common/Composing/LightInjectValidation.cs index 75062e613c..4925074b9e 100644 --- a/src/Umbraco.Tests/Composing/LightInjectValidation.cs +++ b/src/Umbraco.Tests.Common/Composing/LightInjectValidation.cs @@ -35,7 +35,7 @@ using ServiceMap = System.Collections.Generic.Dictionary 1; } } diff --git a/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs b/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs new file mode 100644 index 0000000000..10f27418ea --- /dev/null +++ b/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs @@ -0,0 +1,106 @@ +using System; +using System.Text; +using Umbraco.Core; + +namespace Umbraco.Tests.Common.Composing +{ + public static class ValidationResultExtensions + { + public static string ToText(this ValidationResult result) + { + var text = new StringBuilder(); + + text.AppendLine($"{result.Severity}: {WordWrap(result.Message, 120)}"); + var target = result.ValidationTarget; + text.Append("\tsvce: "); + text.Append(target.ServiceName); + text.Append(target.DeclaringService.ServiceType); + if (!target.DeclaringService.ServiceName.IsNullOrWhiteSpace()) + { + text.Append(" '"); + text.Append(target.DeclaringService.ServiceName); + text.Append("'"); + } + + text.Append(" ("); + if (target.DeclaringService.Lifetime == null) + text.Append("Transient"); + else + text.Append(target.DeclaringService.Lifetime.ToString().TrimStart("LightInject.").TrimEnd("Lifetime")); + text.AppendLine(")"); + text.Append("\timpl: "); + text.Append(target.DeclaringService.ImplementingType); + text.AppendLine(); + text.Append("\tparm: "); + text.Append(target.Parameter); + text.AppendLine(); + + return text.ToString(); + } + + private static string WordWrap(string text, int width) + { + int pos, next; + var sb = new StringBuilder(); + var nl = Environment.NewLine; + + // Lucidity check + if (width < 1) + return text; + + // Parse each line of text + for (pos = 0; pos < text.Length; pos = next) + { + // Find end of line + var eol = text.IndexOf(nl, pos, StringComparison.Ordinal); + + if (eol == -1) + next = eol = text.Length; + else + next = eol + nl.Length; + + // Copy this line of text, breaking into smaller lines as needed + if (eol > pos) + { + do + { + var len = eol - pos; + + if (len > width) + len = BreakLine(text, pos, width); + + if (pos > 0) + sb.Append("\t\t"); + sb.Append(text, pos, len); + sb.Append(nl); + + // Trim whitespace following break + pos += len; + + while (pos < eol && char.IsWhiteSpace(text[pos])) + pos++; + + } while (eol > pos); + } + else sb.Append(nl); // Empty line + } + + return sb.ToString(); + } + + private static int BreakLine(string text, int pos, int max) + { + // Find last whitespace in line + var i = max - 1; + while (i >= 0 && !char.IsWhiteSpace(text[pos + i])) + i--; + if (i < 0) + return max; // No whitespace found; break at maximum length + // Find start of whitespace + while (i >= 0 && char.IsWhiteSpace(text[pos + i])) + i--; + // Return length of text before whitespace + return i + 1; + } + } +} diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index 336c8007d5..b231e6def0 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; using Umbraco.Core.Persistence; +using Umbraco.Tests.Common; using Umbraco.Tests.Integration.Implementations; namespace Umbraco.Tests.Integration @@ -56,6 +57,8 @@ namespace Umbraco.Tests.Integration Assert.AreNotSame(foo1, foo2); // These are the same because the umbraco container wraps the light inject container Assert.AreSame(foo2, foo3); + + Assertions.AssertContainer(umbracoContainer.Container); } private class Foo diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 2823761afd..871520d1c6 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -1,10 +1,14 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using System; using System.Linq; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; using Umbraco.Core.Runtime; +using Umbraco.Tests.Common; +using Umbraco.Tests.Common.Composing; using Umbraco.Tests.Integration.Implementations; namespace Umbraco.Tests.Integration @@ -19,7 +23,7 @@ namespace Umbraco.Tests.Integration var services = new ServiceCollection(); // LightInject / Umbraco - var umbracoContainer = RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); + var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); // Dependencies needed for Core Runtime var testHelper = new TestHelper(); @@ -35,17 +39,21 @@ namespace Umbraco.Tests.Integration Assert.IsNull(coreRuntime.State.BootFailedException); Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); Assert.IsTrue(MyComposer.IsComposed); - } - } - [RuntimeLevel(MinLevel = RuntimeLevel.Install)] - public class MyComposer : IUserComposer - { - public void Compose(Composition composition) - { - IsComposed = true; + Assertions.AssertContainer(umbracoContainer.Container, reportOnly: true); // TODO Change that to false eventually when we clean up the container } - public static bool IsComposed { get; private set; } + [RuntimeLevel(MinLevel = RuntimeLevel.Install)] + public class MyComposer : IUserComposer + { + public void Compose(Composition composition) + { + IsComposed = true; + } + + public static bool IsComposed { get; private set; } + } } + + } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index a0486218e4..d261afc0da 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -25,7 +25,6 @@ using Umbraco.Core.Runtime; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Sync; -using Umbraco.Tests.Composing; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Cache; @@ -36,6 +35,7 @@ using Umbraco.Web.Runtime; using File = System.IO.File; using Current = Umbraco.Web.Composing.Current; using Umbraco.Tests.Common; +using Umbraco.Tests.Common.Composing; namespace Umbraco.Tests.Runtimes { @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); @@ -73,6 +73,7 @@ namespace Umbraco.Tests.Runtimes var runtimeState = new RuntimeState(logger, null, new Lazy(() => mainDom), new Lazy(() => factory.GetInstance()), umbracoVersion, hostingEnvironment, backOfficeInfo); var configs = TestHelper.GetConfigs(); var variationContextAccessor = TestHelper.VariationContextAccessor; + // create the register and the composition var register = TestHelper.GetRegister(); @@ -80,7 +81,8 @@ namespace Umbraco.Tests.Runtimes composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom);coreRuntime.Compose(composition); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder); + coreRuntime.Compose(composition); // determine actual runtime level runtimeState.DetermineRuntimeLevel(databaseFactory, logger); @@ -274,7 +276,7 @@ namespace Umbraco.Tests.Runtimes composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder); coreRuntime.Compose(composition); // get the components @@ -313,107 +315,14 @@ namespace Umbraco.Tests.Runtimes foreach (var result in resultGroup) { Console.WriteLine(); - Console.Write(ToText(result)); + Console.Write(result.ToText()); } Assert.AreEqual(0, results.Count); } - private static string ToText(ValidationResult result) - { - var text = new StringBuilder(); + - text.AppendLine($"{result.Severity}: {WordWrap(result.Message, 120)}"); - var target = result.ValidationTarget; - text.Append("\tsvce: "); - text.Append(target.ServiceName); - text.Append(target.DeclaringService.ServiceType); - if (!target.DeclaringService.ServiceName.IsNullOrWhiteSpace()) - { - text.Append(" '"); - text.Append(target.DeclaringService.ServiceName); - text.Append("'"); - } - - text.Append(" ("); - if (target.DeclaringService.Lifetime == null) - text.Append("Transient"); - else - text.Append(target.DeclaringService.Lifetime.ToString().TrimStart("LightInject.").TrimEnd("Lifetime")); - text.AppendLine(")"); - text.Append("\timpl: "); - text.Append(target.DeclaringService.ImplementingType); - text.AppendLine(); - text.Append("\tparm: "); - text.Append(target.Parameter); - text.AppendLine(); - - return text.ToString(); - } - - private static string WordWrap(string text, int width) - { - int pos, next; - var sb = new StringBuilder(); - var nl = Environment.NewLine; - - // Lucidity check - if (width < 1) - return text; - - // Parse each line of text - for (pos = 0; pos < text.Length; pos = next) - { - // Find end of line - var eol = text.IndexOf(nl, pos, StringComparison.Ordinal); - - if (eol == -1) - next = eol = text.Length; - else - next = eol + nl.Length; - - // Copy this line of text, breaking into smaller lines as needed - if (eol > pos) - { - do - { - var len = eol - pos; - - if (len > width) - len = BreakLine(text, pos, width); - - if (pos > 0) - sb.Append("\t\t"); - sb.Append(text, pos, len); - sb.Append(nl); - - // Trim whitespace following break - pos += len; - - while (pos < eol && char.IsWhiteSpace(text[pos])) - pos++; - - } while (eol > pos); - } - else sb.Append(nl); // Empty line - } - - return sb.ToString(); - } - - private static int BreakLine(string text, int pos, int max) - { - // Find last whitespace in line - var i = max - 1; - while (i >= 0 && !char.IsWhiteSpace(text[pos + i])) - i--; - if (i < 0) - return max; // No whitespace found; break at maximum length - // Find start of whitespace - while (i >= 0 && char.IsWhiteSpace(text[pos + i])) - i--; - // Return length of text before whitespace - return i + 1; - } + } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ca150c7c5e..12d3ae111c 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -120,7 +120,6 @@ - From 9ded4c7ddbfb378def319778cffaa110a1083412 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 18:44:58 +1100 Subject: [PATCH 08/20] Wires up DI for cross wiring correctly ensuring that it occurs at the very end of ConfigureServices, updates tests accordingly, fixes a few other things. --- .../Composing/HostBuilderExtensions.cs | 20 +++++ .../Composing/RegisterFactory.cs | 16 +--- .../UmbracoServiceProviderFactory.cs | 72 ++++++++++++++++++ .../Persistence/IDbProviderFactoryCreator.cs | 2 +- .../SqlServerDbProviderFactoryCreator.cs | 55 ++++++++++++++ .../Umbraco.Infrastructure.csproj | 8 +- .../ContainerTests.cs | 10 ++- src/Umbraco.Tests.Integration/RuntimeTests.cs | 12 +-- .../AspNetCore/AspNetCoreSessionIdResolver.cs | 14 +++- ...coBackOfficeServiceCollectionExtensions.cs | 76 +++++++++++++++++-- .../Umbraco.Web.BackOffice.csproj | 4 + src/Umbraco.Web.UI.NetCore/Program.cs | 14 ++-- src/Umbraco.Web.UI.NetCore/Startup.cs | 2 +- 13 files changed, 264 insertions(+), 41 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs create mode 100644 src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs create mode 100644 src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs new file mode 100644 index 0000000000..d5355c136f --- /dev/null +++ b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Hosting; + +namespace Umbraco.Core.Composing +{ + /// + /// Extends the to enable Umbraco to be used as the service container. + /// + public static class HostBuilderExtensions + { + /// + /// Assigns a custom service provider factory to use Umbraco's container + /// + /// + /// + public static IHostBuilder UseUmbraco(this IHostBuilder builder) + { + return builder.UseServiceProviderFactory(new UmbracoServiceProviderFactory()); + } + } +} diff --git a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs index 9794de733a..835bd0b9a8 100644 --- a/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/RegisterFactory.cs @@ -1,6 +1,7 @@ using LightInject; using LightInject.Microsoft.DependencyInjection; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using System; using System.Reflection; using Umbraco.Core.Composing.LightInject; @@ -8,24 +9,13 @@ using Umbraco.Core.Configuration; namespace Umbraco.Core.Composing { + /// /// Creates the container. /// public static class RegisterFactory { - /// - /// Creates a new based on an existing MSDI IServiceCollection - /// - /// - /// - public static IRegister CreateFrom(IServiceCollection services, out IServiceProvider serviceProvider) - { - var lightInjectContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings()); - serviceProvider = lightInjectContainer.CreateServiceProvider(services); - return new LightInjectContainer(lightInjectContainer); - } - - //TODO: The following can die when net framework is gone + //TODO: This can die when net framework is gone // cannot use typeof().AssemblyQualifiedName on the web container - we don't reference it // a normal Umbraco site should run on the web container, but an app may run on the core one diff --git a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs new file mode 100644 index 0000000000..1d79bf9837 --- /dev/null +++ b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs @@ -0,0 +1,72 @@ +using LightInject; +using LightInject.Microsoft.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using Umbraco.Core.Composing.LightInject; + +namespace Umbraco.Core.Composing +{ + /// + /// Used to create Umbraco's container and cross-wire it up before the applicaton starts + /// + public class UmbracoServiceProviderFactory : IServiceProviderFactory + { + public UmbracoServiceProviderFactory(ServiceContainer container) + { + _container = new LightInjectContainer(container); + } + + /// + /// Default ctor for use in Host Builder configuration + /// + public UmbracoServiceProviderFactory() + { + var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + UmbracoContainer = _container = new LightInjectContainer(container); + IsActive = true; + } + + // see here for orig lightinject version https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/blob/412566e3f70625e6b96471db5e1f7cd9e3e1eb18/src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.cs#L263 + // we don't really need all that, we're manually creating our container with the correct options and that + // is what we'll return in CreateBuilder + + IServiceCollection _services; + readonly LightInjectContainer _container; + + internal LightInjectContainer GetContainer() => _container; + + /// + /// When the empty ctor is used this returns if this factory is active + /// + public static bool IsActive { get; private set; } + + /// + /// When the empty ctor is used this returns the created IRegister + /// + public static IRegister UmbracoContainer { get; private set; } + + /// + /// Create the container with the required settings for aspnetcore3 + /// + /// + /// + public IServiceContainer CreateBuilder(IServiceCollection services) + { + _services = services; + return _container.Container; + } + + /// + /// This cross-wires the container just before the application calls "Configure" + /// + /// + /// + public IServiceProvider CreateServiceProvider(IServiceContainer containerBuilder) + { + var provider = containerBuilder.CreateServiceProvider(_services); + return provider; + } + + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs index 23ef0bfda5..5806bb90ec 100644 --- a/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/IDbProviderFactoryCreator.cs @@ -1,9 +1,9 @@ using System.Data.Common; -using StackExchange.Profiling.Internal; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence { + public interface IDbProviderFactoryCreator { DbProviderFactory CreateFactory(); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs new file mode 100644 index 0000000000..9c2c6273c2 --- /dev/null +++ b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs @@ -0,0 +1,55 @@ +using System; +using System.Data.Common; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence +{ + public class SqlServerDbProviderFactoryCreator : IDbProviderFactoryCreator + { + private readonly string _defaultProviderName; + private readonly Func _getFactory; + + public SqlServerDbProviderFactoryCreator(string defaultProviderName, Func getFactory) + { + _defaultProviderName = defaultProviderName; + _getFactory = getFactory; + } + + public DbProviderFactory CreateFactory() => CreateFactory(_defaultProviderName); + + public DbProviderFactory CreateFactory(string providerName) + { + if (string.IsNullOrEmpty(providerName)) return null; + return _getFactory(providerName); + } + + // gets the sql syntax provider that corresponds, from attribute + public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) + { + return providerName switch + { + Constants.DbProviderNames.SqlCe => throw new NotSupportedException("SqlCe is not supported"), + Constants.DbProviderNames.SqlServer => new SqlServerSyntaxProvider(), + _ => throw new InvalidOperationException($"Unknown provider name \"{providerName}\""), + }; + } + + public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName) + { + switch (providerName) + { + case Constants.DbProviderNames.SqlCe: + throw new NotSupportedException("SqlCe is not supported"); + case Constants.DbProviderNames.SqlServer: + return new SqlServerBulkSqlInsertProvider(); + default: + return new BasicBulkSqlInsertProvider(); + } + } + + public void CreateDatabase() + { + throw new NotSupportedException("Embedded databases are not supported"); + } + } +} diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 797c8bb7fa..a5fea12a6b 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -10,6 +10,7 @@ + @@ -57,6 +58,9 @@ <_Parameter1>Umbraco.Tests.Benchmarks + + <_Parameter1>Umbraco.Tests.Integration + @@ -67,8 +71,4 @@ - - - - diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index b231e6def0..b85471b95a 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -1,5 +1,7 @@ using LightInject; +using LightInject.Microsoft.DependencyInjection; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -26,7 +28,10 @@ namespace Umbraco.Tests.Integration var msdiServiceProvider = services.BuildServiceProvider(); // LightInject / Umbraco - var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); + var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var serviceProviderFactory = new UmbracoServiceProviderFactory(container); + var umbracoContainer = serviceProviderFactory.GetContainer(); + serviceProviderFactory.CreateBuilder(services); // called during Host Builder, needed to capture services // Dependencies needed for creating composition/register essentials var testHelper = new TestHelper(); @@ -42,7 +47,8 @@ namespace Umbraco.Tests.Integration testHelper.AppCaches, umbracoDatabaseFactory, typeLoader, runtimeState, testHelper.GetTypeFinder(), testHelper.IOHelper, testHelper.GetUmbracoVersion(), dbProviderFactoryCreator); - // Resolve + // Cross wire - this would be called by the Host Builder at the very end of ConfigureServices + var lightInjectServiceProvider = serviceProviderFactory.CreateServiceProvider(umbracoContainer.Container); // From MSDI var foo1 = msdiServiceProvider.GetService(); diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 871520d1c6..32eb327127 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Runtime; using Umbraco.Tests.Common; using Umbraco.Tests.Common.Composing; using Umbraco.Tests.Integration.Implementations; +using Umbraco.Web.BackOffice.AspNetCore; namespace Umbraco.Tests.Integration { @@ -19,20 +20,19 @@ namespace Umbraco.Tests.Integration [Test] public void BootCoreRuntime() { - // MSDI - var services = new ServiceCollection(); - // LightInject / Umbraco - var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider); + var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var serviceProviderFactory = new UmbracoServiceProviderFactory(container); + var umbracoContainer = serviceProviderFactory.GetContainer(); - // Dependencies needed for Core Runtime + // Create the core runtime var testHelper = new TestHelper(); - var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(), testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker, testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator, testHelper.MainDom, testHelper.GetTypeFinder()); + // boot it! var factory = coreRuntime.Boot(umbracoContainer); Assert.IsTrue(coreRuntime.MainDom.IsMainDom); diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs index 5470516cf7..ddd0a9b122 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Umbraco.Net; namespace Umbraco.Web.BackOffice.AspNetCore @@ -12,6 +13,17 @@ namespace Umbraco.Web.BackOffice.AspNetCore _httpContextAccessor = httpContextAccessor; } - public string SessionId => _httpContextAccessor?.HttpContext.Session?.Id; + + public string SessionId + { + get + { + // If session isn't enabled this will throw an exception so we check + var sessionFeature = _httpContextAccessor?.HttpContext?.Features.Get(); + return sessionFeature != null + ? _httpContextAccessor?.HttpContext?.Session?.Id + : "0"; + } + } } } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs index 9441170ae4..0fbd45214d 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -1,4 +1,6 @@ -using System.Configuration; +using System; +using System.Data.Common; +using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -6,27 +8,82 @@ using Microsoft.Extensions.Hosting; using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Logging.Serilog; +using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; namespace Umbraco.Web.BackOffice.AspNetCore { + public static class UmbracoBackOfficeServiceCollectionExtensions { - public static IServiceCollection AddUmbracoBackOffice(this IServiceCollection services) + + /// + /// Adds the Umbraco Back Core requirements + /// + /// + /// + /// + /// Must be called after all services are added to the application because we are cross-wiring the container (currently) + /// + public static IServiceCollection AddUmbracoCore(this IServiceCollection services) { - - services.AddSingleton(); CreateCompositionRoot(services); + if (!UmbracoServiceProviderFactory.IsActive) + throw new InvalidOperationException("Ensure to add UseUmbraco() in your Program.cs after ConfigureWebHostDefaults to enable Umbraco's service provider factory"); + + var umbContainer = UmbracoServiceProviderFactory.UmbracoContainer; + + // TODO: Get rid of this 'Current' requirement + var globalSettings = Current.Configs.Global(); + var umbracoVersion = new UmbracoVersion(globalSettings); + + var coreRuntime = GetCoreRuntime( + Current.Configs, + umbracoVersion, + Current.IOHelper, + Current.Logger, + Current.Profiler, + Current.HostingEnvironment, + Current.BackOfficeInfo); + + var factory = coreRuntime.Boot(umbContainer); + return services; } + private static IRuntime GetCoreRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, + IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) + { + var connectionStringConfig = configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; + var dbProviderFactoryCreator = new SqlServerDbProviderFactoryCreator( + connectionStringConfig?.ProviderName, + DbProviderFactories.GetFactory); + + // Determine if we should use the sql main dom or the default + var appSettingMainDomLock = configs.Global().MainDomLock; + var mainDomLock = appSettingMainDomLock == "SqlMainDomLock" + ? (IMainDomLock)new SqlMainDomLock(logger, configs, dbProviderFactoryCreator) + : new MainDomSemaphoreLock(logger, hostingEnvironment); + + var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); + + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly())); + + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetCoreBootPermissionsChecker(), + hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder); + + return coreRuntime; + } private static void CreateCompositionRoot(IServiceCollection services) { @@ -43,7 +100,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore var hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, httpContextAccessor, hostApplicationLifetime); var ioHelper = new IOHelper(hostingEnvironment); - var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, new AspNetCoreMarchal()); + + var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, new AspNetCoreMarchal()); var configs = configFactory.Create(ioHelper, logger); var backOfficeInfo = new AspNetCoreBackOfficeInfo(configs.Global()); @@ -51,5 +109,13 @@ namespace Umbraco.Web.BackOffice.AspNetCore Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); } + + private class AspNetCoreBootPermissionsChecker : IUmbracoBootPermissionChecker + { + public void ThrowIfNotPermissions() + { + // nothing to check + } + } } } diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 36238f1a6d..bd20769d45 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -10,6 +10,10 @@ + + + + diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 21eb1b6585..6b77cb5f93 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using Serilog; +using Umbraco.Core.Composing; namespace Umbraco.Web.UI.BackOffice { @@ -19,7 +15,9 @@ namespace Umbraco.Web.UI.BackOffice } public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }) + .UseUmbraco() + .UseSerilog(); } } diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 8e4da28917..7e2d19ac02 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -19,8 +19,8 @@ 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.AddUmbracoCore(); services.AddUmbracoWebsite(); - services.AddUmbracoBackOffice(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From 44fd18d78710095d4519c985b8b97bf2c9ffabde Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 19:10:21 +1100 Subject: [PATCH 09/20] Gets AddUmbracoBackOffice working --- src/Umbraco.Tests.Integration/RuntimeTests.cs | 38 ++++++++++++++++++- ...coBackOfficeServiceCollectionExtensions.cs | 35 ++++++++++------- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 32eb327127..828a5b68d1 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -1,4 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +using LightInject; +using LightInject.Microsoft.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; using NUnit.Framework; using System; using System.Linq; @@ -43,6 +49,36 @@ namespace Umbraco.Tests.Integration Assertions.AssertContainer(umbracoContainer.Container, reportOnly: true); // TODO Change that to false eventually when we clean up the container } + [Test] + public void AddUmbracoBackOffice() + { + var testHelper = new TestHelper(); + + // MSDI + var services = new ServiceCollection(); + // These services are required + services.AddSingleton(x => testHelper.GetHttpContextAccessor()); + services.AddSingleton(x => testHelper.GetWebHostEnvironment()); + services.AddSingleton(x => Mock.Of()); + + // LightInject / Umbraco + var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var serviceProviderFactory = new UmbracoServiceProviderFactory(container); + var umbracoContainer = serviceProviderFactory.GetContainer(); + + // Add it! + services.AddUmbracoCore(umbracoContainer, GetType().Assembly); + + // assert results + var runtimeState = umbracoContainer.GetInstance(); + var mainDom = umbracoContainer.GetInstance(); + + Assert.IsTrue(mainDom.IsMainDom); + Assert.IsNull(runtimeState.BootFailedException); + Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); + Assert.IsTrue(MyComposer.IsComposed); + } + [RuntimeLevel(MinLevel = RuntimeLevel.Install)] public class MyComposer : IUserComposer { diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs index 0fbd45214d..6a74323bc4 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -32,19 +32,28 @@ namespace Umbraco.Web.BackOffice.AspNetCore /// public static IServiceCollection AddUmbracoCore(this IServiceCollection services) { - services.AddSingleton(); - - CreateCompositionRoot(services); - if (!UmbracoServiceProviderFactory.IsActive) throw new InvalidOperationException("Ensure to add UseUmbraco() in your Program.cs after ConfigureWebHostDefaults to enable Umbraco's service provider factory"); var umbContainer = UmbracoServiceProviderFactory.UmbracoContainer; + return services.AddUmbracoCore(umbContainer, Assembly.GetEntryAssembly()); + } + + public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IRegister umbContainer, Assembly entryAssembly) + { + services.AddSingleton(); + + CreateCompositionRoot(services); + // TODO: Get rid of this 'Current' requirement var globalSettings = Current.Configs.Global(); var umbracoVersion = new UmbracoVersion(globalSettings); + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. + var typeFinder = new TypeFinder(Current.Logger, new DefaultUmbracoAssemblyProvider(entryAssembly)); + var coreRuntime = GetCoreRuntime( Current.Configs, umbracoVersion, @@ -52,7 +61,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore Current.Logger, Current.Profiler, Current.HostingEnvironment, - Current.BackOfficeInfo); + Current.BackOfficeInfo, + typeFinder); var factory = coreRuntime.Boot(umbContainer); @@ -60,7 +70,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore } private static IRuntime GetCoreRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, - IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) + IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, + ITypeFinder typeFinder) { var connectionStringConfig = configs.ConnectionStrings()[Constants.System.UmbracoConnectionName]; var dbProviderFactoryCreator = new SqlServerDbProviderFactoryCreator( @@ -75,10 +86,6 @@ namespace Umbraco.Web.BackOffice.AspNetCore var mainDom = new MainDom(logger, hostingEnvironment, mainDomLock); - // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however - // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly())); - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetCoreBootPermissionsChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder); @@ -87,11 +94,13 @@ 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. var serviceProvider = services.BuildServiceProvider(); - var httpContextAccessor = serviceProvider.GetService(); - var webHostEnvironment = serviceProvider.GetService(); - var hostApplicationLifetime = serviceProvider.GetService(); + var httpContextAccessor = serviceProvider.GetRequiredService(); + var webHostEnvironment = serviceProvider.GetRequiredService(); + var hostApplicationLifetime = serviceProvider.GetRequiredService(); var configFactory = new ConfigsFactory(); From cb749242ff442701c72d9d8005bc4103edd1ea8c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Mar 2020 19:12:52 +1100 Subject: [PATCH 10/20] name change --- src/Umbraco.Tests.Integration/RuntimeTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 828a5b68d1..034b937a3b 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Integration } [Test] - public void AddUmbracoBackOffice() + public void AddUmbracoCore() { var testHelper = new TestHelper(); From 7e679f34c49c6d7ba97b5082bf6ceca03ebbef3f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Mar 2020 22:41:55 +1100 Subject: [PATCH 11/20] fixes sln --- src/umbraco.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/src/umbraco.sln b/src/umbraco.sln index 3f15979719..ca5d4d5188 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -122,6 +122,7 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Website", "Umbraco.Web.Website\Umbraco.Web.Website.csproj", "{5A246D54-3109-4D2B-BE7D-FC0787D126AE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Integration", "Umbraco.Tests.Integration\Umbraco.Tests.Integration.csproj", "{D6319409-777A-4BD0-93ED-B2DFD805B32C}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Common", "Umbraco.Tests.Common\Umbraco.Tests.Common.csproj", "{A499779C-1B3B-48A8-B551-458E582E6E96}" EndProject Global From 299e9eb209e5ba70205abac8654c2d9c82786b2b Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 16:39:27 +1100 Subject: [PATCH 12/20] fixing tests and merges --- .../AspNetCoreConfigsFactory.cs | 2 +- src/Umbraco.Tests.Common/TestHelperBase.cs | 18 ++++++++--- src/Umbraco.Tests.Integration/RuntimeTests.cs | 8 +++++ src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 1 - ...coBackOfficeServiceCollectionExtensions.cs | 32 +++---------------- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Configuration/AspNetCoreConfigsFactory.cs b/src/Umbraco.Configuration/AspNetCoreConfigsFactory.cs index 1e9f7976d5..0cacab9e1d 100644 --- a/src/Umbraco.Configuration/AspNetCoreConfigsFactory.cs +++ b/src/Umbraco.Configuration/AspNetCoreConfigsFactory.cs @@ -14,7 +14,7 @@ namespace Umbraco.Configuration public AspNetCoreConfigsFactory(IConfiguration configuration) { - _configuration = configuration; + _configuration = configuration ?? throw new System.ArgumentNullException(nameof(configuration)); } public Configs Create() diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index ef437d45a6..be43ad6d38 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -28,15 +28,15 @@ namespace Umbraco.Tests.Common public abstract class TestHelperBase { private readonly ITypeFinder _typeFinder; - private readonly IConfigsFactory _configsFactory; + private IConfigsFactory _configsFactory; private UriUtility _uriUtility; private IIOHelper _ioHelper; private Configs _configs; + private IUmbracoVersion _umbracoVersion; public TestHelperBase(Assembly entryAssembly) { - _configsFactory = new ConfigsFactory(); - SettingsForTests = new SettingsForTests(); + SettingsForTests = new SettingsForTests(); MainDom = new SimpleMainDom(); _typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(entryAssembly)); } @@ -54,6 +54,7 @@ namespace Umbraco.Tests.Common _configs = GetConfigsFactory().Create(); return _configs; } + public IRuntimeState GetRuntimeState() { return new RuntimeState( @@ -69,7 +70,12 @@ namespace Umbraco.Tests.Common public abstract IBackOfficeInfo GetBackOfficeInfo(); - public IConfigsFactory GetConfigsFactory() => _configsFactory; + public IConfigsFactory GetConfigsFactory() + { + if (_configsFactory == null) + _configsFactory = new ConfigsFactory(); + return _configsFactory; + } /// /// Gets the current assembly directory. @@ -133,7 +139,9 @@ namespace Umbraco.Tests.Common public IUmbracoVersion GetUmbracoVersion() { - return new UmbracoVersion(GetConfigs().Global()); + if (_umbracoVersion == null) + _umbracoVersion = new UmbracoVersion(GetConfigs().Global()); + return _umbracoVersion; } public IRegister GetRegister() diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 034b937a3b..6ccfdd76d6 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -2,7 +2,9 @@ using LightInject.Microsoft.DependencyInjection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Moq; using NUnit.Framework; @@ -66,7 +68,13 @@ namespace Umbraco.Tests.Integration var serviceProviderFactory = new UmbracoServiceProviderFactory(container); var umbracoContainer = serviceProviderFactory.GetContainer(); + // Some IConfiguration must exist in the container first + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + services.AddSingleton(x => configurationBuilder.Build()); + // Add it! + services.AddUmbracoConfiguration(); services.AddUmbracoCore(umbracoContainer, GetType().Assembly); // assert results diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 579ca104ea..24dcb229c9 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -33,7 +33,6 @@ namespace Umbraco.Tests.TestHelpers protected override void Compose() { base.Compose(); - base.Compose(); Composition.RegisterUnique(); Composition.RegisterUnique(); diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs index b795d31670..697e8b7dc4 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -30,6 +30,9 @@ namespace Umbraco.Web.BackOffice.AspNetCore { var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetService(); + if (configuration == null) + throw new InvalidOperationException($"Could not resolve {typeof(IConfiguration)} from the container"); + var configsFactory = new AspNetCoreConfigsFactory(configuration); var configs = configsFactory.Create(); @@ -112,33 +115,6 @@ namespace Umbraco.Web.BackOffice.AspNetCore return coreRuntime; } - public static IServiceCollection CreateCompositionRoot( - this IServiceCollection services, - IHttpContextAccessor httpContextAccessor, - IWebHostEnvironment webHostEnvironment, - IHostApplicationLifetime hostApplicationLifetime, - Configs configs) - { - var hostingSettings = configs.Hosting(); - var coreDebug = configs.CoreDebug(); - var globalSettings = configs.Global(); - - var hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, - httpContextAccessor, hostApplicationLifetime); - var ioHelper = new IOHelper(hostingEnvironment, globalSettings); - var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, - new AspNetCoreSessionIdResolver(httpContextAccessor), - () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, - new AspNetCoreMarchal()); - - var backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings); - var profiler = new LogProfiler(logger); - - Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); - - return services; - } - private static void CreateCompositionRoot(IServiceCollection services) { // TODO: This isn't the best to have to resolve the services now but to avoid this will @@ -150,6 +126,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore var hostApplicationLifetime = serviceProvider.GetRequiredService(); var configs = serviceProvider.GetService(); + if (configs == null) + throw new InvalidOperationException($"Could not resolve type {typeof(Configs)} from the container, ensure {nameof(AddUmbracoConfiguration)} is called before calling {nameof(AddUmbracoCore)}"); var hostingSettings = configs.Hosting(); var coreDebug = configs.CoreDebug(); From 427c44468f9653eb9a543239c90004e09bdc744d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 16:41:39 +1100 Subject: [PATCH 13/20] Changes methods to properties since we don't return new instances each time. --- src/Umbraco.Tests.Common/TestHelperBase.cs | 37 ++++++++++++------- .../ContainerTests.cs | 4 +- .../Implementations/TestHelper.cs | 2 +- src/Umbraco.Tests.Integration/RuntimeTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 8 ++-- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index be43ad6d38..88886246cd 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -48,11 +48,14 @@ namespace Umbraco.Tests.Common return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); } - public Configs GetConfigs() + public Configs Configs { - if (_configs == null) - _configs = GetConfigsFactory().Create(); - return _configs; + get + { + if (_configs == null) + _configs = ConfigsFactory.Create(); + return _configs; + } } public IRuntimeState GetRuntimeState() @@ -62,7 +65,7 @@ namespace Umbraco.Tests.Common Mock.Of(), new Lazy(), new Lazy(), - GetUmbracoVersion(), + UmbracoVersion, GetHostingEnvironment(), GetBackOfficeInfo() ); @@ -70,11 +73,14 @@ namespace Umbraco.Tests.Common public abstract IBackOfficeInfo GetBackOfficeInfo(); - public IConfigsFactory GetConfigsFactory() + public IConfigsFactory ConfigsFactory { - if (_configsFactory == null) - _configsFactory = new ConfigsFactory(); - return _configsFactory; + get + { + if (_configsFactory == null) + _configsFactory = new ConfigsFactory(); + return _configsFactory; + } } /// @@ -137,16 +143,19 @@ namespace Umbraco.Tests.Common return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); } - public IUmbracoVersion GetUmbracoVersion() + public IUmbracoVersion UmbracoVersion { - if (_umbracoVersion == null) - _umbracoVersion = new UmbracoVersion(GetConfigs().Global()); - return _umbracoVersion; + get + { + if (_umbracoVersion == null) + _umbracoVersion = new UmbracoVersion(Configs.Global()); + return _umbracoVersion; + } } public IRegister GetRegister() { - return RegisterFactory.Create(GetConfigs().Global()); + return RegisterFactory.Create(Configs.Global()); } public abstract IHostingEnvironment GetHostingEnvironment(); diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index b85471b95a..77725ce575 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -42,10 +42,10 @@ namespace Umbraco.Tests.Integration // Register in the container var composition = new Composition(umbracoContainer, typeLoader, - testHelper.Logger, runtimeState, testHelper.GetConfigs(), testHelper.IOHelper, testHelper.AppCaches); + testHelper.Logger, runtimeState, testHelper.Configs, testHelper.IOHelper, testHelper.AppCaches); composition.RegisterEssentials(testHelper.Logger, testHelper.Profiler, testHelper.Logger, testHelper.MainDom, testHelper.AppCaches, umbracoDatabaseFactory, typeLoader, runtimeState, testHelper.GetTypeFinder(), - testHelper.IOHelper, testHelper.GetUmbracoVersion(), dbProviderFactoryCreator); + testHelper.IOHelper, testHelper.UmbracoVersion, dbProviderFactoryCreator); // Cross wire - this would be called by the Host Builder at the very end of ConfigureServices var lightInjectServiceProvider = serviceProviderFactory.CreateServiceProvider(umbracoContainer.Container); diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 5254892b23..e98f4d108a 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Integration.Implementations public override IBackOfficeInfo GetBackOfficeInfo() { if (_backOfficeInfo == null) - _backOfficeInfo = new AspNetCoreBackOfficeInfo(SettingsForTests.GetDefaultGlobalSettings(GetUmbracoVersion())); + _backOfficeInfo = new AspNetCoreBackOfficeInfo(SettingsForTests.GetDefaultGlobalSettings(UmbracoVersion)); return _backOfficeInfo; } diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 6ccfdd76d6..0a8b250cec 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Integration // Create the core runtime var testHelper = new TestHelper(); - var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(), + var coreRuntime = new CoreRuntime(testHelper.Configs, testHelper.UmbracoVersion, testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker, testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator, testHelper.MainDom, testHelper.GetTypeFinder()); diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 05d4de6e23..cf3554f3af 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -56,7 +56,7 @@ namespace Umbraco.Tests.TestHelpers public override IBackOfficeInfo GetBackOfficeInfo() => new AspNetBackOfficeInfo( - SettingsForTests.GenerateMockGlobalSettings(GetUmbracoVersion()), + SettingsForTests.GenerateMockGlobalSettings(UmbracoVersion), TestHelper.IOHelper, Mock.Of(), SettingsForTests.GenerateMockWebRoutingSettings()); public override IHostingEnvironment GetHostingEnvironment() @@ -70,13 +70,13 @@ namespace Umbraco.Tests.TestHelpers public static TypeLoader GetMockedTypeLoader() => _testHelperInternal.GetMockedTypeLoader(); - public static Configs GetConfigs() => _testHelperInternal.GetConfigs(); + public static Configs GetConfigs() => _testHelperInternal.Configs; public static IRuntimeState GetRuntimeState() => _testHelperInternal.GetRuntimeState(); public static IBackOfficeInfo GetBackOfficeInfo() => _testHelperInternal.GetBackOfficeInfo(); - public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.GetConfigsFactory(); + public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.ConfigsFactory; /// /// Gets the current assembly directory. @@ -314,7 +314,7 @@ namespace Umbraco.Tests.TestHelpers } - public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.GetUmbracoVersion(); + public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.UmbracoVersion; public static IRegister GetRegister() => _testHelperInternal.GetRegister(); From 6013d856b748199d851fd21233215280efef955f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 17:15:32 +1100 Subject: [PATCH 14/20] Changes back TestHelperBase, we'll just allocate new configs each time. --- src/Umbraco.Tests.Common/TestHelperBase.cs | 38 ++++--------------- .../ContainerTests.cs | 4 +- .../Implementations/TestHelper.cs | 2 +- src/Umbraco.Tests.Integration/RuntimeTests.cs | 2 +- .../Runtimes/CoreRuntimeTests.cs | 1 - src/Umbraco.Tests/TestHelpers/TestHelper.cs | 8 ++-- 6 files changed, 16 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 88886246cd..236190d8a9 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -28,11 +28,8 @@ namespace Umbraco.Tests.Common public abstract class TestHelperBase { private readonly ITypeFinder _typeFinder; - private IConfigsFactory _configsFactory; private UriUtility _uriUtility; private IIOHelper _ioHelper; - private Configs _configs; - private IUmbracoVersion _umbracoVersion; public TestHelperBase(Assembly entryAssembly) { @@ -48,15 +45,7 @@ namespace Umbraco.Tests.Common return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); } - public Configs Configs - { - get - { - if (_configs == null) - _configs = ConfigsFactory.Create(); - return _configs; - } - } + public Configs GetConfigs() => GetConfigsFactory().Create(); public IRuntimeState GetRuntimeState() { @@ -65,7 +54,7 @@ namespace Umbraco.Tests.Common Mock.Of(), new Lazy(), new Lazy(), - UmbracoVersion, + GetUmbracoVersion(), GetHostingEnvironment(), GetBackOfficeInfo() ); @@ -73,15 +62,7 @@ namespace Umbraco.Tests.Common public abstract IBackOfficeInfo GetBackOfficeInfo(); - public IConfigsFactory ConfigsFactory - { - get - { - if (_configsFactory == null) - _configsFactory = new ConfigsFactory(); - return _configsFactory; - } - } + public IConfigsFactory GetConfigsFactory() => new ConfigsFactory(); /// /// Gets the current assembly directory. @@ -143,19 +124,16 @@ namespace Umbraco.Tests.Common return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); } - public IUmbracoVersion UmbracoVersion + public IUmbracoVersion GetUmbracoVersion() { - get - { - if (_umbracoVersion == null) - _umbracoVersion = new UmbracoVersion(Configs.Global()); - return _umbracoVersion; - } + if (_umbracoVersion == null) + _umbracoVersion = new UmbracoVersion(GetConfigs().Global()); + return _umbracoVersion; } public IRegister GetRegister() { - return RegisterFactory.Create(Configs.Global()); + return RegisterFactory.Create(GetConfigs().Global()); } public abstract IHostingEnvironment GetHostingEnvironment(); diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index 77725ce575..b85471b95a 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -42,10 +42,10 @@ namespace Umbraco.Tests.Integration // Register in the container var composition = new Composition(umbracoContainer, typeLoader, - testHelper.Logger, runtimeState, testHelper.Configs, testHelper.IOHelper, testHelper.AppCaches); + testHelper.Logger, runtimeState, testHelper.GetConfigs(), testHelper.IOHelper, testHelper.AppCaches); composition.RegisterEssentials(testHelper.Logger, testHelper.Profiler, testHelper.Logger, testHelper.MainDom, testHelper.AppCaches, umbracoDatabaseFactory, typeLoader, runtimeState, testHelper.GetTypeFinder(), - testHelper.IOHelper, testHelper.UmbracoVersion, dbProviderFactoryCreator); + testHelper.IOHelper, testHelper.GetUmbracoVersion(), dbProviderFactoryCreator); // Cross wire - this would be called by the Host Builder at the very end of ConfigureServices var lightInjectServiceProvider = serviceProviderFactory.CreateServiceProvider(umbracoContainer.Container); diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index e98f4d108a..5254892b23 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Integration.Implementations public override IBackOfficeInfo GetBackOfficeInfo() { if (_backOfficeInfo == null) - _backOfficeInfo = new AspNetCoreBackOfficeInfo(SettingsForTests.GetDefaultGlobalSettings(UmbracoVersion)); + _backOfficeInfo = new AspNetCoreBackOfficeInfo(SettingsForTests.GetDefaultGlobalSettings(GetUmbracoVersion())); return _backOfficeInfo; } diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 0a8b250cec..6ccfdd76d6 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Integration // Create the core runtime var testHelper = new TestHelper(); - var coreRuntime = new CoreRuntime(testHelper.Configs, testHelper.UmbracoVersion, + var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(), testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker, testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator, testHelper.MainDom, testHelper.GetTypeFinder()); diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index fd9813b080..92ab825ee2 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -177,7 +177,6 @@ namespace Umbraco.Tests.Runtimes public override void Terminate() { - ((IRegisteredObject) _mainDom).Stop(false); base.Terminate(); } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index cf3554f3af..05d4de6e23 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -56,7 +56,7 @@ namespace Umbraco.Tests.TestHelpers public override IBackOfficeInfo GetBackOfficeInfo() => new AspNetBackOfficeInfo( - SettingsForTests.GenerateMockGlobalSettings(UmbracoVersion), + SettingsForTests.GenerateMockGlobalSettings(GetUmbracoVersion()), TestHelper.IOHelper, Mock.Of(), SettingsForTests.GenerateMockWebRoutingSettings()); public override IHostingEnvironment GetHostingEnvironment() @@ -70,13 +70,13 @@ namespace Umbraco.Tests.TestHelpers public static TypeLoader GetMockedTypeLoader() => _testHelperInternal.GetMockedTypeLoader(); - public static Configs GetConfigs() => _testHelperInternal.Configs; + public static Configs GetConfigs() => _testHelperInternal.GetConfigs(); public static IRuntimeState GetRuntimeState() => _testHelperInternal.GetRuntimeState(); public static IBackOfficeInfo GetBackOfficeInfo() => _testHelperInternal.GetBackOfficeInfo(); - public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.ConfigsFactory; + public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.GetConfigsFactory(); /// /// Gets the current assembly directory. @@ -314,7 +314,7 @@ namespace Umbraco.Tests.TestHelpers } - public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.UmbracoVersion; + public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.GetUmbracoVersion(); public static IRegister GetRegister() => _testHelperInternal.GetRegister(); From 7900179abd4e1e2629cc00c1b86a413fc27e9cd8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 17:16:33 +1100 Subject: [PATCH 15/20] oops fix build --- src/Umbraco.Tests.Common/TestHelperBase.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 236190d8a9..536bebee56 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -124,12 +124,7 @@ namespace Umbraco.Tests.Common return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); } - public IUmbracoVersion GetUmbracoVersion() - { - if (_umbracoVersion == null) - _umbracoVersion = new UmbracoVersion(GetConfigs().Global()); - return _umbracoVersion; - } + public IUmbracoVersion GetUmbracoVersion() => new UmbracoVersion(GetConfigs().Global()); public IRegister GetRegister() { From 5761381bc3e889be4032300d5407313a3a21e4eb Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 17:25:45 +1100 Subject: [PATCH 16/20] adds notes --- .../Composing/ValidationResultExtensions.cs | 1 + src/Umbraco.Tests.Integration/RuntimeTests.cs | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs b/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs index 10f27418ea..cfd136b63c 100644 --- a/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs +++ b/src/Umbraco.Tests.Common/Composing/ValidationResultExtensions.cs @@ -4,6 +4,7 @@ using Umbraco.Core; namespace Umbraco.Tests.Common.Composing { + // These are used for Light Inject container validation public static class ValidationResultExtensions { public static string ToText(this ValidationResult result) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 6ccfdd76d6..25b0db642a 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -4,19 +4,13 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Moq; using NUnit.Framework; -using System; -using System.Linq; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Composing.LightInject; -using Umbraco.Core.Configuration; using Umbraco.Core.Runtime; using Umbraco.Tests.Common; -using Umbraco.Tests.Common.Composing; using Umbraco.Tests.Integration.Implementations; using Umbraco.Web.BackOffice.AspNetCore; From 679b88a2fbd812fbe91b3f4c6bd7b2e127833c81 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Mar 2020 17:35:04 +1100 Subject: [PATCH 17/20] more asserts --- src/Umbraco.Tests.Integration/RuntimeTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 25b0db642a..4b5cb30544 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -9,6 +9,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Core.Runtime; using Umbraco.Tests.Common; using Umbraco.Tests.Integration.Implementations; @@ -41,8 +42,14 @@ namespace Umbraco.Tests.Integration Assert.IsNull(coreRuntime.State.BootFailedException); Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level); Assert.IsTrue(MyComposer.IsComposed); + Assert.IsTrue(MyComponent.IsInit); + Assert.IsFalse(MyComponent.IsTerminated); Assertions.AssertContainer(umbracoContainer.Container, reportOnly: true); // TODO Change that to false eventually when we clean up the container + + coreRuntime.Terminate(); + + Assert.IsTrue(MyComponent.IsTerminated); } [Test] @@ -86,11 +93,35 @@ namespace Umbraco.Tests.Integration { public void Compose(Composition composition) { + composition.Components().Append(); IsComposed = true; } public static bool IsComposed { get; private set; } } + + public class MyComponent : IComponent + { + public static bool IsInit { get; private set; } + public static bool IsTerminated { get; private set; } + + private readonly ILogger _logger; + + public MyComponent(ILogger logger) + { + _logger = logger; + } + + public void Initialize() + { + IsInit = true; + } + + public void Terminate() + { + IsTerminated = true; + } + } } From 2bbdb699f2011b4aed7a573a266bca59feeae950 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Mar 2020 11:59:42 +1100 Subject: [PATCH 18/20] reduces code duplication --- .../Composing/UmbracoServiceProviderFactory.cs | 8 +++++++- src/Umbraco.Tests.Common/TestHelperBase.cs | 3 +++ src/Umbraco.Tests.Integration/ContainerTests.cs | 2 +- src/Umbraco.Tests.Integration/RuntimeTests.cs | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs index 1d79bf9837..6fd0bda61e 100644 --- a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs @@ -17,12 +17,18 @@ namespace Umbraco.Core.Composing _container = new LightInjectContainer(container); } + /// + /// Creates an ASP.NET Core compatible service container + /// + /// + public static ServiceContainer CreateServiceContainer() => new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + /// /// Default ctor for use in Host Builder configuration /// public UmbracoServiceProviderFactory() { - var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var container = CreateServiceContainer(); UmbracoContainer = _container = new LightInjectContainer(container); IsActive = true; } diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 536bebee56..f336d0922d 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -1,6 +1,9 @@ using System; using System.IO; using System.Reflection; +using LightInject; +using LightInject.Microsoft.DependencyInjection; +using Microsoft.Extensions.Hosting; using Moq; using Umbraco.Core; using Umbraco.Core.Cache; diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs index b85471b95a..d190d1165d 100644 --- a/src/Umbraco.Tests.Integration/ContainerTests.cs +++ b/src/Umbraco.Tests.Integration/ContainerTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Integration var msdiServiceProvider = services.BuildServiceProvider(); // LightInject / Umbraco - var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var container = UmbracoServiceProviderFactory.CreateServiceContainer(); var serviceProviderFactory = new UmbracoServiceProviderFactory(container); var umbracoContainer = serviceProviderFactory.GetContainer(); serviceProviderFactory.CreateBuilder(services); // called during Host Builder, needed to capture services diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 4b5cb30544..24786a1591 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -24,7 +24,7 @@ namespace Umbraco.Tests.Integration public void BootCoreRuntime() { // LightInject / Umbraco - var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var container = UmbracoServiceProviderFactory.CreateServiceContainer(); var serviceProviderFactory = new UmbracoServiceProviderFactory(container); var umbracoContainer = serviceProviderFactory.GetContainer(); @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Integration services.AddSingleton(x => Mock.Of()); // LightInject / Umbraco - var container = new ServiceContainer(ContainerOptions.Default.Clone().WithMicrosoftSettings().WithAspNetCoreSettings()); + var container = UmbracoServiceProviderFactory.CreateServiceContainer(); var serviceProviderFactory = new UmbracoServiceProviderFactory(container); var umbracoContainer = serviceProviderFactory.GetContainer(); From 139cd3935f968b377ac1fac4a7afbfe4e485d476 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Mar 2020 12:00:48 +1100 Subject: [PATCH 19/20] oops --- src/Umbraco.Tests.Common/TestHelperBase.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index f336d0922d..536bebee56 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -1,9 +1,6 @@ using System; using System.IO; using System.Reflection; -using LightInject; -using LightInject.Microsoft.DependencyInjection; -using Microsoft.Extensions.Hosting; using Moq; using Umbraco.Core; using Umbraco.Core.Cache; From 2f8a85abe5f2fb74e1397c95e663307a805106e3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Mar 2020 12:11:46 +1100 Subject: [PATCH 20/20] resolves remaining PR comments --- .../AspNetCore/AspNetCoreSessionIdResolver.cs | 5 +++-- .../UmbracoBackOfficeServiceCollectionExtensions.cs | 5 +++-- src/Umbraco.Web.UI.NetCore/Program.cs | 6 ++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs index ddd0a9b122..cafb02d367 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreSessionIdResolver.cs @@ -18,10 +18,11 @@ namespace Umbraco.Web.BackOffice.AspNetCore { get { + var httpContext = _httpContextAccessor?.HttpContext; // If session isn't enabled this will throw an exception so we check - var sessionFeature = _httpContextAccessor?.HttpContext?.Features.Get(); + var sessionFeature = httpContext?.Features.Get(); return sessionFeature != null - ? _httpContextAccessor?.HttpContext?.Session?.Id + ? httpContext?.Session?.Id : "0"; } } diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs index 697e8b7dc4..135ba90b97 100644 --- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -137,10 +137,11 @@ namespace Umbraco.Web.BackOffice.AspNetCore var ioHelper = new IOHelper(hostingEnvironment, globalSettings); var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), - () => serviceProvider.GetService(), coreDebug, ioHelper, + // need to build a new service provider since the one already resolved above doesn't have the IRequestCache yet + () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, new AspNetCoreMarchal()); - var backOfficeInfo = new AspNetCoreBackOfficeInfo(configs.Global()); + var backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings); var profiler = new LogProfiler(logger); Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 6b77cb5f93..1151f16be8 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -using Serilog; using Umbraco.Core.Composing; namespace Umbraco.Web.UI.BackOffice @@ -15,9 +14,8 @@ namespace Umbraco.Web.UI.BackOffice } public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) + Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }) - .UseUmbraco() - .UseSerilog(); + .UseUmbraco(); } }