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.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Extends the <see cref="IHostBuilder"/> to enable Umbraco to be used as the service container.
|
||||
/// </summary>
|
||||
public static class HostBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Assigns a custom service provider factory to use Umbraco's container
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static IHostBuilder UseUmbraco(this IHostBuilder builder)
|
||||
{
|
||||
return builder.UseServiceProviderFactory(new UmbracoServiceProviderFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates the container.
|
||||
/// </summary>
|
||||
public static class RegisterFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IRegister"/> based on an existing MSDI IServiceCollection
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to create Umbraco's container and cross-wire it up before the applicaton starts
|
||||
/// </summary>
|
||||
public class UmbracoServiceProviderFactory : IServiceProviderFactory<IServiceContainer>
|
||||
{
|
||||
public UmbracoServiceProviderFactory(ServiceContainer container)
|
||||
{
|
||||
_container = new LightInjectContainer(container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default ctor for use in Host Builder configuration
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// When the empty ctor is used this returns if this factory is active
|
||||
/// </summary>
|
||||
public static bool IsActive { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the empty ctor is used this returns the created IRegister
|
||||
/// </summary>
|
||||
public static IRegister UmbracoContainer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create the container with the required settings for aspnetcore3
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public IServiceContainer CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
_services = services;
|
||||
return _container.Container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This cross-wires the container just before the application calls "Configure"
|
||||
/// </summary>
|
||||
/// <param name="containerBuilder"></param>
|
||||
/// <returns></returns>
|
||||
public IServiceProvider CreateServiceProvider(IServiceContainer containerBuilder)
|
||||
{
|
||||
var provider = containerBuilder.CreateServiceProvider(_services);
|
||||
return provider;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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<string, DbProviderFactory> _getFactory;
|
||||
|
||||
public SqlServerDbProviderFactoryCreator(string defaultProviderName, Func<string, DbProviderFactory> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<PackageReference Include="LightInject" Version="6.3.2" />
|
||||
<PackageReference Include="LightInject.Annotation" Version="1.1.0" />
|
||||
<PackageReference Include="LightInject.Microsoft.DependencyInjection" Version="3.3.0" />
|
||||
<PackageReference Include="LightInject.Microsoft.Hosting" Version="1.2.0" />
|
||||
<PackageReference Include="Markdown" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.2" />
|
||||
@@ -57,6 +58,9 @@
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.Benchmarks</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -67,8 +71,4 @@
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Composing\Microsoft\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -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<Foo>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ISessionFeature>();
|
||||
return sessionFeature != null
|
||||
? _httpContextAccessor?.HttpContext?.Session?.Id
|
||||
: "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Umbraco Back Core requirements
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Must be called after all services are added to the application because we are cross-wiring the container (currently)
|
||||
/// </remarks>
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services)
|
||||
{
|
||||
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
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<IRequestCache>(), coreDebug, ioHelper, new AspNetCoreMarchal());
|
||||
|
||||
var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), () => services.BuildServiceProvider().GetService<IRequestCache>(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Configuration\Umbraco.Configuration.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
|
||||
@@ -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<Startup>(); });
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
|
||||
.UseUmbraco()
|
||||
.UseSerilog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user