2020-03-25 20:42:02 +01:00
using System ;
2020-04-20 06:19:59 +02:00
using System.Collections ;
using System.Collections.Generic ;
2020-03-25 20:42:02 +01:00
using System.Data.Common ;
2020-04-27 10:09:10 +02:00
using System.Data.SqlClient ;
2020-04-03 01:08:52 +11:00
using System.IO ;
2020-03-25 20:42:02 +01:00
using System.Reflection ;
2020-02-24 16:18:47 +01:00
using Microsoft.AspNetCore.Hosting ;
using Microsoft.AspNetCore.Http ;
2020-03-16 14:02:08 +01:00
using Microsoft.Extensions.Configuration ;
2020-02-18 08:32:06 +01:00
using Microsoft.Extensions.DependencyInjection ;
2020-04-22 14:23:56 +10:00
using Microsoft.Extensions.Logging ;
using Serilog ;
using Serilog.Extensions.Hosting ;
using Serilog.Extensions.Logging ;
2020-03-24 10:51:53 +01:00
using Smidge ;
2020-03-30 21:27:35 +02:00
using Smidge.Nuglify ;
2020-02-25 08:56:58 +01:00
using Umbraco.Composing ;
2020-03-16 14:02:08 +01:00
using Umbraco.Configuration ;
2020-03-16 19:14:04 +01:00
using Umbraco.Core ;
2020-02-24 16:18:47 +01:00
using Umbraco.Core.Cache ;
2020-03-13 18:44:58 +11:00
using Umbraco.Core.Composing ;
2020-02-24 16:18:47 +01:00
using Umbraco.Core.Configuration ;
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
using Umbraco.Core.Logging.Serilog ;
2020-03-13 18:44:58 +11:00
using Umbraco.Core.Persistence ;
2020-05-06 20:37:03 +02:00
using Umbraco.Core.Persistence.SqlSyntax ;
2020-02-25 08:56:58 +01:00
using Umbraco.Core.Runtime ;
2020-03-31 12:22:11 +02:00
using Umbraco.Web.Common.AspNetCore ;
2020-03-25 05:39:25 +01:00
using Umbraco.Web.Common.Runtime.Profiler ;
2020-02-18 08:32:06 +01:00
2020-05-07 10:08:23 +02:00
namespace Umbraco.Extensions
2020-02-18 08:32:06 +01:00
{
2020-03-24 14:48:32 +11:00
public static class UmbracoCoreServiceCollectionExtensions
2020-02-18 08:32:06 +01:00
{
2020-05-06 20:37:03 +02:00
public static IServiceCollection AddUmbracoSqlCeSupport ( this IServiceCollection services )
{
try
{
var binFolder = Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) ;
if ( binFolder ! = null )
{
var dllPath = Path . Combine ( binFolder , "Umbraco.Persistance.SqlCe.dll" ) ;
Assembly . LoadFrom ( dllPath ) ;
var sqlCeSyntaxProviderType = Type . GetType ( "Umbraco.Persistance.SqlCe.SqlCeSyntaxProvider, Umbraco.Persistance.SqlCe" ) ;
var sqlCeBulkSqlInsertProviderType = Type . GetType ( "Umbraco.Persistance.SqlCe.SqlCeBulkSqlInsertProvider, Umbraco.Persistance.SqlCe" ) ;
var sqlCeEmbeddedDatabaseCreatorType = Type . GetType ( "Umbraco.Persistance.SqlCe.SqlCeEmbeddedDatabaseCreator, Umbraco.Persistance.SqlCe" ) ;
if ( ! ( sqlCeSyntaxProviderType is null | | sqlCeBulkSqlInsertProviderType is null | | sqlCeEmbeddedDatabaseCreatorType is null ) )
{
services . AddSingleton ( typeof ( ISqlSyntaxProvider ) , sqlCeSyntaxProviderType ) ;
services . AddSingleton ( typeof ( IBulkSqlInsertProvider ) , sqlCeBulkSqlInsertProviderType ) ;
services . AddSingleton ( typeof ( IEmbeddedDatabaseCreator ) , sqlCeEmbeddedDatabaseCreatorType ) ;
}
Assembly . LoadFrom ( Path . Combine ( binFolder , "System.Data.SqlServerCe.dll" ) ) ;
var sqlCe = Type . GetType ( "System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe" ) ;
if ( ! ( sqlCe is null ) )
{
DbProviderFactories . RegisterFactory ( Core . Constants . DbProviderNames . SqlCe , sqlCe ) ;
}
}
}
catch
{
// Ignore if SqlCE is not available
}
return services ;
}
public static IServiceCollection AddUmbracoSqlServerSupport ( this IServiceCollection services )
{
DbProviderFactories . RegisterFactory ( Core . Constants . DbProviderNames . SqlServer , SqlClientFactory . Instance ) ;
services . AddSingleton < ISqlSyntaxProvider , SqlServerSyntaxProvider > ( ) ;
services . AddSingleton < IBulkSqlInsertProvider , SqlServerBulkSqlInsertProvider > ( ) ;
services . AddSingleton < IEmbeddedDatabaseCreator , NoopEmbeddedDatabaseCreator > ( ) ;
return services ;
}
2020-03-24 14:48:32 +11:00
/// <summary>
/// Adds the Umbraco Configuration requirements
/// </summary>
/// <param name="services"></param>
2020-03-25 18:21:44 +11:00
/// <param name="configuration"></param>
2020-03-24 14:48:32 +11:00
/// <returns></returns>
2020-03-25 18:21:44 +11:00
public static IServiceCollection AddUmbracoConfiguration ( this IServiceCollection services , IConfiguration configuration )
2020-03-17 17:56:00 +01:00
{
2020-03-25 18:21:44 +11:00
if ( configuration = = null ) throw new ArgumentNullException ( nameof ( configuration ) ) ;
2020-03-23 16:39:27 +11:00
2020-03-17 17:56:00 +01:00
var configsFactory = new AspNetCoreConfigsFactory ( configuration ) ;
var configs = configsFactory . Create ( ) ;
2020-03-19 18:43:39 +01:00
2020-03-17 17:56:00 +01:00
services . AddSingleton ( configs ) ;
return services ;
}
2020-03-23 15:50:01 +11:00
2020-03-13 18:44:58 +11:00
/// <summary>
2020-03-24 14:48:32 +11:00
/// Adds the Umbraco Back Core requirements
2020-03-13 18:44:58 +11:00
/// </summary>
/// <param name="services"></param>
2020-03-25 18:21:44 +11:00
/// <param name="webHostEnvironment"></param>
2020-03-13 18:44:58 +11:00
/// <returns></returns>
2020-03-25 18:21:44 +11:00
public static IServiceCollection AddUmbracoCore ( this IServiceCollection services , IWebHostEnvironment webHostEnvironment )
2020-04-01 14:19:41 +02:00
{
return services . AddUmbracoCore ( webHostEnvironment , out _ ) ;
}
/// <summary>
/// Adds the Umbraco Back Core requirements
/// </summary>
/// <param name="services"></param>
/// <param name="webHostEnvironment"></param>
/// <param name="factory"></param>
/// <returns></returns>
public static IServiceCollection AddUmbracoCore ( this IServiceCollection services , IWebHostEnvironment webHostEnvironment , out IFactory factory )
2020-03-13 18:44:58 +11:00
{
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 ;
2020-04-22 14:23:56 +10:00
var loggingConfig = new LoggingConfiguration (
Path . Combine ( webHostEnvironment . ContentRootPath , "App_Data\\Logs" ) ,
Path . Combine ( webHostEnvironment . ContentRootPath , "config\\serilog.config" ) ,
Path . Combine ( webHostEnvironment . ContentRootPath , "config\\serilog.user.config" ) ) ;
2020-04-20 06:19:59 +02:00
IHttpContextAccessor httpContextAccessor = new HttpContextAccessor ( ) ;
services . AddSingleton < IHttpContextAccessor > ( httpContextAccessor ) ;
2020-04-28 12:31:15 +02:00
var requestCache = new GenericDictionaryRequestAppCache ( ( ) = > httpContextAccessor . HttpContext ? . Items ) ;
2020-04-20 06:19:59 +02:00
2020-04-22 10:29:21 +02:00
services . AddUmbracoCore ( webHostEnvironment ,
umbContainer ,
Assembly . GetEntryAssembly ( ) ,
requestCache ,
loggingConfig ,
out factory ) ;
2020-03-25 20:42:02 +01:00
return services ;
2020-03-13 19:10:21 +11:00
}
2020-03-24 14:48:32 +11:00
/// <summary>
/// Adds the Umbraco Back Core requirements
/// </summary>
/// <param name="services"></param>
2020-03-25 18:21:44 +11:00
/// <param name="webHostEnvironment"></param>
2020-03-24 14:48:32 +11:00
/// <param name="umbContainer"></param>
/// <param name="entryAssembly"></param>
2020-04-20 06:19:59 +02:00
/// <param name="requestCache"></param>
2020-04-22 10:29:21 +02:00
/// <param name="httpContextAccessor"></param>
2020-04-22 14:23:56 +10:00
/// <param name="loggingConfiguration"></param>
2020-04-01 14:19:41 +02:00
/// <param name="factory"></param>
2020-03-24 14:48:32 +11:00
/// <returns></returns>
2020-04-22 14:23:56 +10:00
public static IServiceCollection AddUmbracoCore (
this IServiceCollection services ,
IWebHostEnvironment webHostEnvironment ,
IRegister umbContainer ,
Assembly entryAssembly ,
2020-04-20 06:19:59 +02:00
IRequestCache requestCache ,
2020-04-22 14:23:56 +10:00
ILoggingConfiguration loggingConfiguration ,
out IFactory factory )
2020-02-18 08:32:06 +01:00
{
2020-03-24 11:53:56 +11:00
if ( services is null ) throw new ArgumentNullException ( nameof ( services ) ) ;
2020-03-27 11:39:17 +01:00
var container = umbContainer ;
if ( container is null ) throw new ArgumentNullException ( nameof ( container ) ) ;
2020-03-24 11:53:56 +11:00
if ( entryAssembly is null ) throw new ArgumentNullException ( nameof ( entryAssembly ) ) ;
2020-05-06 20:37:03 +02:00
services . AddSingleton < IDbProviderFactoryCreator > ( x = > new DbProviderFactoryCreator (
x . GetService < Configs > ( ) . ConnectionStrings ( ) [ Core . Constants . System . UmbracoConnectionName ] ? . ProviderName ,
DbProviderFactories . GetFactory ,
x . GetServices < ISqlSyntaxProvider > ( ) ,
x . GetServices < IBulkSqlInsertProvider > ( ) ,
x . GetServices < IEmbeddedDatabaseCreator > ( )
) ) ;
2020-04-20 06:19:59 +02:00
var serviceProvider = services . BuildServiceProvider ( ) ;
var configs = serviceProvider . GetService < Configs > ( ) ;
2020-04-29 08:56:42 +02:00
var dbProviderFactoryCreator = serviceProvider . GetRequiredService < IDbProviderFactoryCreator > ( ) ;
2020-02-24 16:18:47 +01:00
2020-05-06 20:37:03 +02:00
2020-04-22 10:29:21 +02:00
CreateCompositionRoot ( services ,
configs ,
webHostEnvironment ,
loggingConfiguration ,
out var logger , out var ioHelper , out var hostingEnvironment , out var backOfficeInfo , out var profiler ) ;
2020-03-13 19:10:21 +11:00
2020-03-24 11:53:56 +11:00
var globalSettings = configs . Global ( ) ;
2020-03-13 18:44:58 +11:00
var umbracoVersion = new UmbracoVersion ( globalSettings ) ;
2020-04-27 10:09:10 +02:00
var typeFinder = CreateTypeFinder ( logger , profiler , webHostEnvironment , entryAssembly , configs . TypeFinder ( ) ) ;
2020-03-13 18:44:58 +11:00
var coreRuntime = GetCoreRuntime (
2020-03-24 11:53:56 +11:00
configs ,
2020-03-13 18:44:58 +11:00
umbracoVersion ,
2020-03-24 11:53:56 +11:00
ioHelper ,
logger ,
profiler ,
hostingEnvironment ,
backOfficeInfo ,
2020-04-27 10:09:10 +02:00
typeFinder ,
2020-04-28 07:01:30 +02:00
requestCache ,
dbProviderFactoryCreator ) ;
2020-03-13 18:44:58 +11:00
2020-04-01 14:19:41 +02:00
factory = coreRuntime . Configure ( container ) ;
2020-03-13 18:44:58 +11:00
2020-02-18 08:32:06 +01:00
return services ;
}
2020-05-06 20:37:03 +02:00
2020-04-27 10:09:10 +02:00
private static ITypeFinder CreateTypeFinder ( Core . Logging . ILogger logger , IProfiler profiler , IWebHostEnvironment webHostEnvironment , Assembly entryAssembly , ITypeFinderSettings typeFinderSettings )
2020-04-03 01:08:52 +11:00
{
var runtimeHashPaths = new RuntimeHashPaths ( ) ;
runtimeHashPaths . AddFolder ( new DirectoryInfo ( Path . Combine ( webHostEnvironment . ContentRootPath , "bin" ) ) ) ;
var runtimeHash = new RuntimeHash ( new ProfilingLogger ( logger , profiler ) , runtimeHashPaths ) ;
2020-04-27 10:09:10 +02:00
return new TypeFinder ( logger , new DefaultUmbracoAssemblyProvider ( entryAssembly ) , runtimeHash , new TypeFinderConfig ( typeFinderSettings ) ) ;
2020-04-03 01:08:52 +11:00
}
2020-04-20 06:19:59 +02:00
private static IRuntime GetCoreRuntime (
2020-04-22 10:29:21 +02:00
Configs configs , IUmbracoVersion umbracoVersion , IIOHelper ioHelper , Core . Logging . ILogger logger ,
2020-03-13 19:10:21 +11:00
IProfiler profiler , Core . Hosting . IHostingEnvironment hostingEnvironment , IBackOfficeInfo backOfficeInfo ,
2020-04-28 07:01:30 +02:00
ITypeFinder typeFinder , IRequestCache requestCache , IDbProviderFactoryCreator dbProviderFactoryCreator )
2020-03-13 18:44:58 +11:00
{
// Determine if we should use the sql main dom or the default
2020-03-23 15:50:01 +11:00
var globalSettings = configs . Global ( ) ;
var connStrings = configs . ConnectionStrings ( ) ;
var appSettingMainDomLock = globalSettings . MainDomLock ;
2020-03-13 18:44:58 +11:00
var mainDomLock = appSettingMainDomLock = = "SqlMainDomLock"
2020-03-23 15:50:01 +11:00
? ( IMainDomLock ) new SqlMainDomLock ( logger , globalSettings , connStrings , dbProviderFactoryCreator )
2020-03-13 18:44:58 +11:00
: new MainDomSemaphoreLock ( logger , hostingEnvironment ) ;
2020-03-25 15:06:22 +11:00
var mainDom = new MainDom ( logger , mainDomLock ) ;
2020-03-13 18:44:58 +11:00
2020-04-20 06:19:59 +02:00
var coreRuntime = new CoreRuntime (
configs ,
umbracoVersion ,
ioHelper ,
logger ,
profiler ,
new AspNetCoreBootPermissionsChecker ( ) ,
hostingEnvironment ,
backOfficeInfo ,
dbProviderFactoryCreator ,
mainDom ,
typeFinder ,
requestCache ) ;
2020-03-13 18:44:58 +11:00
return coreRuntime ;
}
2020-02-25 08:56:58 +01:00
2020-04-22 14:23:56 +10:00
private static IServiceCollection CreateCompositionRoot (
IServiceCollection services ,
2020-04-22 10:29:21 +02:00
Configs configs ,
2020-04-22 14:23:56 +10:00
IWebHostEnvironment webHostEnvironment ,
2020-04-22 10:29:21 +02:00
ILoggingConfiguration loggingConfiguration ,
out Core . Logging . ILogger logger ,
out IIOHelper ioHelper ,
out Core . Hosting . IHostingEnvironment hostingEnvironment ,
out IBackOfficeInfo backOfficeInfo ,
out IProfiler profiler )
2020-02-24 16:18:47 +01:00
{
2020-03-23 16:39:27 +11:00
if ( configs = = null )
throw new InvalidOperationException ( $"Could not resolve type {typeof(Configs)} from the container, ensure {nameof(AddUmbracoConfiguration)} is called before calling {nameof(AddUmbracoCore)}" ) ;
2020-02-24 16:18:47 +01:00
2020-03-16 19:14:04 +01:00
var hostingSettings = configs . Hosting ( ) ;
var globalSettings = configs . Global ( ) ;
2020-02-24 16:18:47 +01:00
2020-04-03 01:08:52 +11:00
hostingEnvironment = new AspNetCoreHostingEnvironment ( hostingSettings , webHostEnvironment ) ;
2020-03-24 11:53:56 +11:00
ioHelper = new IOHelper ( hostingEnvironment , globalSettings ) ;
2020-04-22 14:23:56 +10:00
logger = AddLogger ( services , hostingEnvironment , loggingConfiguration ) ;
2020-03-12 16:56:39 +01:00
2020-03-24 14:47:10 +11:00
backOfficeInfo = new AspNetCoreBackOfficeInfo ( globalSettings ) ;
2020-04-22 15:23:51 +10:00
profiler = GetWebProfiler ( hostingEnvironment ) ;
2020-02-24 16:18:47 +01:00
2020-03-16 14:02:08 +01:00
return services ;
2020-02-24 16:18:47 +01:00
}
2020-03-24 10:51:53 +01:00
2020-04-22 14:23:56 +10:00
/// <summary>
/// Create and configure the logger
/// </summary>
/// <param name="hostingEnvironment"></param>
private static Core . Logging . ILogger AddLogger ( IServiceCollection services , Core . Hosting . IHostingEnvironment hostingEnvironment , ILoggingConfiguration loggingConfiguration )
{
// Create a serilog logger
var logger = SerilogLogger . CreateWithDefaultConfiguration ( hostingEnvironment , loggingConfiguration ) ;
// Wire up all the bits that serilog needs. We need to use our own code since the Serilog ext methods don't cater to our needs since
// we don't want to use the global serilog `Log` object and we don't have our own ILogger implementation before the HostBuilder runs which
2020-04-22 10:29:21 +02:00
// is the only other option that these ext methods allow.
2020-04-22 14:23:56 +10:00
// I have created a PR to make this nicer https://github.com/serilog/serilog-extensions-hosting/pull/19 but we'll need to wait for that.
// Also see : https://github.com/serilog/serilog-extensions-hosting/blob/dev/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
services . AddSingleton < ILoggerFactory > ( services = > new SerilogLoggerFactory ( logger . SerilogLog , false ) ) ;
2020-04-22 10:29:21 +02:00
// This won't (and shouldn't) take ownership of the logger.
2020-04-22 14:23:56 +10:00
services . AddSingleton ( logger . SerilogLog ) ;
// Registered to provide two services...
var diagnosticContext = new DiagnosticContext ( logger . SerilogLog ) ;
// Consumed by e.g. middleware
services . AddSingleton ( diagnosticContext ) ;
// Consumed by user code
services . AddSingleton < IDiagnosticContext > ( diagnosticContext ) ;
return logger ;
}
2020-03-25 20:42:02 +01:00
public static IServiceCollection AddUmbracoRuntimeMinifier ( this IServiceCollection services ,
2020-03-24 17:34:34 +01:00
IConfiguration configuration )
2020-03-24 10:51:53 +01:00
{
2020-04-21 10:14:03 +02:00
services . AddSmidge ( configuration . GetSection ( Core . Constants . Configuration . ConfigRuntimeMinification ) ) ;
2020-03-30 21:27:35 +02:00
services . AddSmidgeNuglify ( ) ;
2020-03-24 10:51:53 +01:00
return services ;
}
2020-03-13 18:44:58 +11:00
2020-04-22 15:23:51 +10:00
private static IProfiler GetWebProfiler ( Umbraco . Core . Hosting . IHostingEnvironment hostingEnvironment )
2020-03-25 05:39:25 +01:00
{
// create and start asap to profile boot
if ( ! hostingEnvironment . IsDebugMode )
{
// should let it be null, that's how MiniProfiler is meant to work,
// but our own IProfiler expects an instance so let's get one
return new VoidProfiler ( ) ;
}
2020-04-22 15:23:51 +10:00
var webProfiler = new WebProfiler ( ) ;
2020-03-29 22:35:52 +02:00
webProfiler . StartBoot ( ) ;
2020-03-25 05:39:25 +01:00
return webProfiler ;
}
2020-03-13 18:44:58 +11:00
private class AspNetCoreBootPermissionsChecker : IUmbracoBootPermissionChecker
{
public void ThrowIfNotPermissions ( )
{
// nothing to check
}
}
2020-03-24 14:48:32 +11:00
2020-03-26 06:51:23 +01:00
2020-02-18 08:32:06 +01:00
}
2020-04-20 06:19:59 +02:00
2020-02-18 08:32:06 +01:00
}