using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog;
using Serilog.Extensions.Hosting;
using Umbraco.Core;
using Umbraco.Core.Builder;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Configuration.Models.Validation;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Serilog;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Runtime;
using Umbraco.Infrastructure.HostedServices;
using Umbraco.Infrastructure.HostedServices.ServerRegistration;
using Umbraco.Web.Common.AspNetCore;
using Umbraco.Web.Common.Profiler;
using ConnectionStrings = Umbraco.Core.Configuration.Models.ConnectionStrings;
using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Extensions
{
public static class UmbracoCoreServiceCollectionExtensions
{
///
/// Adds the Umbraco Back Core requirements
///
///
///
///
///
///
///
/// Weird hack for tests, won't exist much longer
///
/// Shouldn't exist
public static IUmbracoBuilder AddUmbracoCore(
this IUmbracoBuilder builder,
IWebHostEnvironment webHostEnvironment,
Assembly entryAssembly,
AppCaches appCaches,
ILoggingConfiguration loggingConfiguration,
IConfiguration configuration,
//TODO: Yep that's extremely ugly
Action configureSomeMoreBits)
{
if (builder is null) throw new ArgumentNullException(nameof(builder));
if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly));
builder.Services.AddLazySupport();
// Add service session
// This can be overwritten by the user by adding their own call to AddSession
// since the last call of AddSession take precedence
builder.Services.AddSession(options =>
{
options.Cookie.Name = "UMB_SESSION";
options.Cookie.HttpOnly = true;
});
var syntaxProviders = new List();
var insertProviders = new List();
var databaseCreators = new List();
// Add supported databases
builder.Services.AddUmbracoSqlCeSupport(syntaxProviders, insertProviders, databaseCreators);
builder.Services.AddUmbracoSqlServerSupport(syntaxProviders, insertProviders, databaseCreators);
var dbProviderFactoryCreator = new DbProviderFactoryCreator(
DbProviderFactories.GetFactory,
syntaxProviders,
insertProviders,
databaseCreators);
builder.Services.AddSingleton(dbProviderFactoryCreator);
// TODO: We should not be doing this at all.
var serviceProvider = builder.Services.BuildServiceProvider();
// Switching to IOptions vs IOptionsMonitor was rejected previously as it prevents setting IsDebug true without a restart
var hostingSettings = serviceProvider.GetService>(); // <--- We are now building ServiceProvider just for this line
var hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment);
var ioHelper = new IOHelper(hostingEnvironment);
var profiler = GetWebProfiler(hostingEnvironment);
builder.Services.AddUnique();
builder.Services.AddLogger(loggingConfiguration, configuration);
var loggerFactory = builder.BuilderLoggerFactory;
var typeFinderSettings = builder.Config.GetSection(Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings();
var typeFinder = CreateTypeFinder(loggerFactory, profiler, webHostEnvironment, entryAssembly, Options.Create(typeFinderSettings));
var globalSettings = builder.Config.GetSection(Core.Constants.Configuration.ConfigGlobal).Get() ?? new GlobalSettings();
var connectionStrings = builder.Config.GetSection("ConnectionStrings").Get(opt => opt.BindNonPublicProperties = true) ?? new ConnectionStrings();
configureSomeMoreBits(
builder,
globalSettings,
connectionStrings,
new UmbracoVersion(),
ioHelper,
profiler,
hostingEnvironment,
typeFinder,
appCaches,
dbProviderFactoryCreator);
builder.AddComposers();
return builder;
}
public static IUmbracoBuilder AddComposers(this IUmbracoBuilder builder)
{
var composerTypes = builder.TypeLoader.GetTypes();
var enableDisable = builder.TypeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute));
new Composers(builder, composerTypes, enableDisable, builder.BuilderLoggerFactory.CreateLogger()).Compose();
return builder;
}
///
/// Adds SqlCe support for Umbraco
///
private static IServiceCollection AddUmbracoSqlCeSupport(
this IServiceCollection services,
ICollection syntaxProviders,
ICollection insertProviders,
ICollection databaseCreators)
{
try
{
var binFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (binFolder != null)
{
var dllPath = Path.Combine(binFolder, "Umbraco.Persistance.SqlCe.dll");
var umbSqlCeAssembly = Assembly.LoadFrom(dllPath);
var sqlCeSyntaxProviderType = umbSqlCeAssembly.GetType("Umbraco.Persistance.SqlCe.SqlCeSyntaxProvider");
var sqlCeBulkSqlInsertProviderType = umbSqlCeAssembly.GetType("Umbraco.Persistance.SqlCe.SqlCeBulkSqlInsertProvider");
var sqlCeEmbeddedDatabaseCreatorType = umbSqlCeAssembly.GetType("Umbraco.Persistance.SqlCe.SqlCeEmbeddedDatabaseCreator");
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);
syntaxProviders.Add((ISqlSyntaxProvider)Activator.CreateInstance(sqlCeSyntaxProviderType));
insertProviders.Add((IBulkSqlInsertProvider)Activator.CreateInstance(sqlCeBulkSqlInsertProviderType));
databaseCreators.Add((IEmbeddedDatabaseCreator)Activator.CreateInstance(sqlCeEmbeddedDatabaseCreatorType));
}
var sqlCeAssembly = Assembly.LoadFrom(Path.Combine(binFolder, "System.Data.SqlServerCe.dll"));
var sqlCe = sqlCeAssembly.GetType("System.Data.SqlServerCe.SqlCeProviderFactory");
if (!(sqlCe is null))
{
DbProviderFactories.RegisterFactory(Core.Constants.DbProviderNames.SqlCe, sqlCe);
}
}
}
catch
{
// Ignore if SqlCE is not available
}
return services;
}
///
/// Adds Sql Server support for Umbraco
///
public static IServiceCollection AddUmbracoSqlServerSupport(
this IServiceCollection services,
ICollection syntaxProviders,
ICollection insertProviders,
ICollection databaseCreators)
{
DbProviderFactories.RegisterFactory(Core.Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
var syntaxProvider = new SqlServerSyntaxProvider();
var insertProvider = new SqlServerBulkSqlInsertProvider();
var databaseCreator = new NoopEmbeddedDatabaseCreator();
services.AddSingleton(syntaxProvider);
services.AddSingleton(insertProvider);
services.AddSingleton(databaseCreator);
syntaxProviders.Add(syntaxProvider);
insertProviders.Add(insertProvider);
databaseCreators.Add(databaseCreator);
return services;
}
///
/// Adds hosted services for Umbraco.
///
///
///
public static IServiceCollection AddUmbracoHostedServices(this IServiceCollection services)
{
services.AddHostedService();
services.AddHostedService();
services.AddHostedService();
services.AddHostedService();
services.AddHostedService();
services.AddHostedService();
services.AddHostedService();
return services;
}
///
/// Adds HTTP clients for Umbraco.
///
///
///
public static IServiceCollection AddUmbracoHttpClients(this IServiceCollection services)
{
services.AddHttpClient();
return services;
}
private static ITypeFinder CreateTypeFinder(ILoggerFactory loggerFactory, IProfiler profiler, IWebHostEnvironment webHostEnvironment, Assembly entryAssembly, IOptions typeFinderSettings)
{
var runtimeHashPaths = new RuntimeHashPaths();
runtimeHashPaths.AddFolder(new DirectoryInfo(Path.Combine(webHostEnvironment.ContentRootPath, "bin")));
var runtimeHash = new RuntimeHash(new ProfilingLogger(loggerFactory.CreateLogger("RuntimeHash"), profiler), runtimeHashPaths);
return new TypeFinder(loggerFactory.CreateLogger(), new DefaultUmbracoAssemblyProvider(entryAssembly), runtimeHash, new TypeFinderConfig(typeFinderSettings));
}
internal static void ConfigureSomeMorebits(
IUmbracoBuilder builder,
GlobalSettings globalSettings,
ConnectionStrings connectionStrings,
IUmbracoVersion umbracoVersion,
IIOHelper ioHelper,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
ITypeFinder typeFinder,
AppCaches appCaches,
IDbProviderFactoryCreator dbProviderFactoryCreator)
{
// Determine if we should use the sql main dom or the default
var appSettingMainDomLock = globalSettings.MainDomLock;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var mainDomLock = appSettingMainDomLock == "SqlMainDomLock" || isWindows == false
? (IMainDomLock)new SqlMainDomLock(builder.BuilderLoggerFactory.CreateLogger(), builder.BuilderLoggerFactory, globalSettings, connectionStrings, dbProviderFactoryCreator, hostingEnvironment)
: new MainDomSemaphoreLock(builder.BuilderLoggerFactory.CreateLogger(), hostingEnvironment);
var mainDom = new MainDom(builder.BuilderLoggerFactory.CreateLogger(), mainDomLock);
var logger = builder.BuilderLoggerFactory.CreateLogger