Merge pull request #9556 from rustybox/netcore/feature/core-component-cleanup-low-hanging-fruit
Netcore: composer / component cleanup low hanging fruit
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -191,6 +191,7 @@ src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb
|
||||
/src/Umbraco.Web.UI.NetCore/App_Data/Smidge/Cache/*
|
||||
/src/Umbraco.Web.UI.NetCore/umbraco/logs
|
||||
|
||||
src/Umbraco.Tests.Integration/umbraco/Data/
|
||||
src/Umbraco.Tests.Integration/umbraco/logs/
|
||||
|
||||
src/Umbraco.Tests.Integration/Views/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.Events;
|
||||
|
||||
@@ -26,5 +27,22 @@ namespace Umbraco.Core.DependencyInjection
|
||||
builder.Services.AddTransient(typeof(INotificationHandler<TNotification>), typeof(TNotificationHandler));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a notification handler against the Umbraco service collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TNotification">The type of notification.</typeparam>
|
||||
/// <typeparam name="TNotificationHandler">The type of notificiation handler.</typeparam>
|
||||
/// <param name="builder">The Umbraco builder.</param>
|
||||
/// <param name="factory">Factory method</param>
|
||||
/// <returns>The <see cref="IUmbracoBuilder"/>.</returns>
|
||||
public static IUmbracoBuilder AddNotificationHandler<TNotification, TNotificationHandler>(this IUmbracoBuilder builder, Func<IServiceProvider, TNotificationHandler> factory)
|
||||
where TNotificationHandler : class, INotificationHandler<TNotification>
|
||||
where TNotification : INotification
|
||||
{
|
||||
// Register the handler as transient. This ensures that anything can be injected into it.
|
||||
builder.Services.AddTransient(typeof(INotificationHandler<TNotification>), factory);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/Umbraco.Core/Events/UmbracoApplicationStarting.cs
Normal file
16
src/Umbraco.Core/Events/UmbracoApplicationStarting.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class UmbracoApplicationStarting : INotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoApplicationStarting"/> class.
|
||||
/// </summary>
|
||||
/// <param name="runtimeLevel">The runtime level</param>
|
||||
public UmbracoApplicationStarting(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the runtime level of execution.
|
||||
/// </summary>
|
||||
public RuntimeLevel RuntimeLevel { get; }
|
||||
}
|
||||
}
|
||||
4
src/Umbraco.Core/Events/UmbracoApplicationStopping.cs
Normal file
4
src/Umbraco.Core/Events/UmbracoApplicationStopping.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class UmbracoApplicationStopping : INotification { }
|
||||
}
|
||||
@@ -1,22 +1,15 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the Umbraco runtime.
|
||||
/// </summary>
|
||||
public interface IRuntime
|
||||
public interface IRuntime : IHostedService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the runtime state.
|
||||
/// </summary>
|
||||
IRuntimeState State { get; }
|
||||
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the runtime.
|
||||
/// </summary>
|
||||
void Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.8" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Core.Compose
|
||||
{
|
||||
public sealed class ManifestWatcherComponent : IComponent
|
||||
{
|
||||
private readonly IHostingEnvironment _hosting;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime;
|
||||
|
||||
// if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for
|
||||
// package.manifest chances and restarts the application on any change
|
||||
private ManifestWatcher _mw;
|
||||
|
||||
public ManifestWatcherComponent(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime)
|
||||
{
|
||||
_hosting = hosting;
|
||||
_loggerFactory = loggerFactory;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_umbracoApplicationLifetime = umbracoApplicationLifetime;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (_hosting.IsDebugMode == false) return;
|
||||
|
||||
//if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false)
|
||||
// return;
|
||||
|
||||
var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins);
|
||||
if (Directory.Exists(appPlugins) == false) return;
|
||||
|
||||
_mw = new ManifestWatcher(_loggerFactory.CreateLogger<ManifestWatcher>(), _umbracoApplicationLifetime);
|
||||
_mw.Start(Directory.GetDirectories(appPlugins));
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
if (_mw == null) return;
|
||||
|
||||
_mw.Dispose();
|
||||
_mw = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Compose
|
||||
{
|
||||
public class ManifestWatcherComposer : ComponentComposer<ManifestWatcherComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Composing.CompositionExtensions;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Grid;
|
||||
@@ -62,15 +62,17 @@ using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Trees;
|
||||
using TextStringValueConverter = Umbraco.Core.PropertyEditors.ValueConverters.TextStringValueConverter;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
namespace Umbraco.Infrastructure.Runtime
|
||||
{
|
||||
// core's initial composer composes before all core composers
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
public class CoreInitialComposer : ComponentComposer<CoreInitialComponent>
|
||||
public static class CoreInitialServices
|
||||
{
|
||||
public override void Compose(IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builder)
|
||||
{
|
||||
base.Compose(builder);
|
||||
builder.AddNotificationHandler<UmbracoApplicationStarting, EssentialDirectoryCreator>();
|
||||
|
||||
builder.Services.AddSingleton<ManifestWatcher>();
|
||||
builder.AddNotificationHandler<UmbracoApplicationStarting, ManifestWatcher>(factory => factory.GetRequiredService<ManifestWatcher>());
|
||||
builder.AddNotificationHandler<UmbracoApplicationStopping, ManifestWatcher>(factory => factory.GetRequiredService<ManifestWatcher>());
|
||||
|
||||
// composers
|
||||
builder
|
||||
@@ -302,7 +304,7 @@ namespace Umbraco.Core.Runtime
|
||||
// register *all* checks, except those marked [HideFromTypeFinder] of course
|
||||
builder.Services.AddUnique<IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
|
||||
builder.HealthChecks()
|
||||
.Add(() => builder.TypeLoader.GetTypes<HealthCheck.HealthCheck>());
|
||||
.Add(() => builder.TypeLoader.GetTypes<Core.HealthCheck.HealthCheck>());
|
||||
|
||||
builder.WithCollectionBuilder<HealthCheckNotificationMethodCollectionBuilder>()
|
||||
.Add(() => builder.TypeLoader.GetTypes<IHealthCheckNotificationMethod>());
|
||||
@@ -382,7 +384,10 @@ namespace Umbraco.Core.Runtime
|
||||
builder.Services.AddUnique<MediaPermissions>();
|
||||
builder.Services.AddUnique<IImageDimensionExtractor, ImageDimensionExtractor>();
|
||||
|
||||
|
||||
builder.Services.AddUnique<PackageDataInstallation>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
@@ -10,71 +13,102 @@ namespace Umbraco.Infrastructure.Runtime
|
||||
{
|
||||
public class CoreRuntime : IRuntime
|
||||
{
|
||||
public IRuntimeState State { get; }
|
||||
|
||||
private readonly ILogger<CoreRuntime> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ComponentCollection _components;
|
||||
private readonly IApplicationShutdownRegistry _applicationShutdownRegistry;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
public CoreRuntime(
|
||||
ILogger<CoreRuntime> logger,
|
||||
ILoggerFactory loggerFactory,
|
||||
IRuntimeState state,
|
||||
ComponentCollection components,
|
||||
IApplicationShutdownRegistry applicationShutdownRegistry,
|
||||
IProfilingLogger profilingLogger,
|
||||
IMainDom mainDom,
|
||||
IUmbracoDatabaseFactory databaseFactory)
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IEventAggregator eventAggregator,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
State = state;
|
||||
_logger = logger;
|
||||
_loggerFactory = loggerFactory;
|
||||
_components = components;
|
||||
_applicationShutdownRegistry = applicationShutdownRegistry;
|
||||
_profilingLogger = profilingLogger;
|
||||
_mainDom = mainDom;
|
||||
_databaseFactory = databaseFactory;
|
||||
}
|
||||
|
||||
_eventAggregator = eventAggregator;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
|
||||
public void Start()
|
||||
|
||||
_logger = _loggerFactory.CreateLogger<CoreRuntime>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of the Umbraco runtime.
|
||||
/// </summary>
|
||||
public IRuntimeState State { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
StaticApplicationLogging.Initialize(_loggerFactory);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
|
||||
{
|
||||
var exception = (Exception)args.ExceptionObject;
|
||||
var isTerminating = args.IsTerminating; // always true?
|
||||
|
||||
var msg = "Unhandled exception in AppDomain";
|
||||
if (isTerminating) msg += " (terminating)";
|
||||
|
||||
if (isTerminating)
|
||||
{
|
||||
msg += " (terminating)";
|
||||
}
|
||||
|
||||
msg += ".";
|
||||
|
||||
_logger.LogError(exception, msg);
|
||||
};
|
||||
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data));
|
||||
|
||||
DetermineRuntimeLevel();
|
||||
|
||||
if (State.Level <= RuntimeLevel.BootFailed)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot start the runtime if the runtime level is less than or equal to {RuntimeLevel.BootFailed}");
|
||||
}
|
||||
|
||||
var hostingEnvironmentLifetime = _applicationShutdownRegistry;
|
||||
IApplicationShutdownRegistry hostingEnvironmentLifetime = _applicationShutdownRegistry;
|
||||
if (hostingEnvironmentLifetime == null)
|
||||
throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(Start)}");
|
||||
{
|
||||
throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}");
|
||||
}
|
||||
|
||||
// acquire the main domain - if this fails then anything that should be registered with MainDom will not operate
|
||||
AcquireMainDom();
|
||||
|
||||
await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken);
|
||||
|
||||
// create & initialize the components
|
||||
_components.Initialize();
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_components.Terminate();
|
||||
await _eventAggregator.PublishAsync(new UmbracoApplicationStopping(), cancellationToken);
|
||||
StaticApplicationLogging.Initialize(null);
|
||||
}
|
||||
|
||||
private void AcquireMainDom()
|
||||
{
|
||||
using (var timer = _profilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired."))
|
||||
using (DisposableTimer timer = _profilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired."))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -90,7 +124,7 @@ namespace Umbraco.Infrastructure.Runtime
|
||||
|
||||
private void DetermineRuntimeLevel()
|
||||
{
|
||||
using var timer = _profilingLogger.DebugDuration<CoreRuntime>("Determining runtime level.", "Determined.");
|
||||
using DisposableTimer timer = _profilingLogger.DebugDuration<CoreRuntime>("Determining runtime level.", "Determined.");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Composing;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
namespace Umbraco.Infrastructure.Runtime
|
||||
{
|
||||
public class CoreInitialComponent : IComponent
|
||||
public class EssentialDirectoryCreator : INotificationHandler<UmbracoApplicationStarting>
|
||||
{
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public CoreInitialComponent(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions<GlobalSettings> globalSettings)
|
||||
public EssentialDirectoryCreator(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions<GlobalSettings> globalSettings)
|
||||
{
|
||||
_ioHelper = ioHelper;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
|
||||
{
|
||||
// ensure we have some essential directories
|
||||
// every other component can then initialize safely
|
||||
@@ -28,9 +31,8 @@ namespace Umbraco.Core.Runtime
|
||||
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews));
|
||||
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews));
|
||||
_ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials));
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{ }
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/Umbraco.Infrastructure/Runtime/ManifestWatcher.cs
Normal file
65
src/Umbraco.Infrastructure/Runtime/ManifestWatcher.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Infrastructure.Runtime
|
||||
{
|
||||
public sealed class ManifestWatcher :
|
||||
INotificationHandler<UmbracoApplicationStarting>,
|
||||
INotificationHandler<UmbracoApplicationStopping>
|
||||
{
|
||||
private readonly IHostingEnvironment _hosting;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime;
|
||||
|
||||
// if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for
|
||||
// package.manifest chances and restarts the application on any change
|
||||
private Core.Manifest.ManifestWatcher _mw;
|
||||
|
||||
public ManifestWatcher(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime)
|
||||
{
|
||||
_hosting = hosting;
|
||||
_loggerFactory = loggerFactory;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_umbracoApplicationLifetime = umbracoApplicationLifetime;
|
||||
}
|
||||
|
||||
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_hosting.IsDebugMode == false)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins);
|
||||
if (Directory.Exists(appPlugins) == false)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
_mw = new Core.Manifest.ManifestWatcher(_loggerFactory.CreateLogger<Core.Manifest.ManifestWatcher>(), _umbracoApplicationLifetime);
|
||||
_mw.Start(Directory.GetDirectories(appPlugins));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task HandleAsync(UmbracoApplicationStopping notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_mw == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
_mw.Dispose();
|
||||
_mw = null;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,16 +135,16 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_entitySerializer = entitySerializer;
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
|
||||
// we always want to handle repository events, configured or not
|
||||
// assuming no repository event will trigger before the whole db is ready
|
||||
// (ideally we'd have Upgrading.App vs Upgrading.Data application states...)
|
||||
InitializeRepositoryEvents();
|
||||
|
||||
_lifeTime.ApplicationInit += OnApplicationInit;
|
||||
}
|
||||
|
||||
internal void OnApplicationInit(object sender, EventArgs e)
|
||||
{
|
||||
// we always want to handle repository events, configured or not
|
||||
// assuming no repository event will trigger before the whole db is ready
|
||||
// (ideally we'd have Upgrading.App vs Upgrading.Data application states...)
|
||||
InitializeRepositoryEvents();
|
||||
|
||||
// however, the cache is NOT available until we are configured, because loading
|
||||
// content (and content types) from database cannot be consistent (see notes in "Handle
|
||||
// Notifications" region), so
|
||||
|
||||
@@ -33,54 +33,6 @@ namespace Umbraco.Tests.Integration
|
||||
MyComposer.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling AddUmbracoCore to configure the container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task AddUmbracoCore()
|
||||
{
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
.UseUmbraco()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
services.AddSingleton(testHelper.DbProviderFactoryCreator);
|
||||
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
var typeLoader = services.AddTypeLoader(
|
||||
GetType().Assembly,
|
||||
webHostEnvironment,
|
||||
testHelper.GetHostingEnvironment(),
|
||||
testHelper.ConsoleLoggerFactory,
|
||||
AppCaches.NoCache,
|
||||
hostContext.Configuration,
|
||||
testHelper.Profiler);
|
||||
|
||||
var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, testHelper.ConsoleLoggerFactory);
|
||||
builder.Services.AddUnique<AppCaches>(AppCaches.NoCache);
|
||||
builder.AddConfiguration();
|
||||
builder.AddUmbracoCore();
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
var app = new ApplicationBuilder(host.Services);
|
||||
|
||||
// assert results
|
||||
var runtimeState = app.ApplicationServices.GetRequiredService<IRuntimeState>();
|
||||
var mainDom = app.ApplicationServices.GetRequiredService<IMainDom>();
|
||||
|
||||
Assert.IsFalse(mainDom.IsMainDom); // We haven't "Started" the runtime yet
|
||||
Assert.IsNull(runtimeState.BootFailedException);
|
||||
Assert.IsFalse(MyComponent.IsInit); // We haven't "Started" the runtime yet
|
||||
|
||||
await host.StopAsync();
|
||||
|
||||
Assert.IsFalse(MyComponent.IsTerminated); // we didn't "Start" the runtime so nothing was registered for shutdown
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling AddUmbracoCore to configure the container and UseUmbracoCore to start the runtime
|
||||
/// </summary>
|
||||
@@ -91,7 +43,6 @@ namespace Umbraco.Tests.Integration
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
.UseUmbraco()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
@@ -109,11 +60,12 @@ namespace Umbraco.Tests.Integration
|
||||
hostContext.Configuration,
|
||||
testHelper.Profiler);
|
||||
|
||||
var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, testHelper.ConsoleLoggerFactory);
|
||||
var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader,
|
||||
testHelper.ConsoleLoggerFactory);
|
||||
builder.Services.AddUnique<AppCaches>(AppCaches.NoCache);
|
||||
builder.AddConfiguration()
|
||||
.AddUmbracoCore()
|
||||
.Build();
|
||||
.AddUmbracoCore()
|
||||
.Build();
|
||||
|
||||
services.AddRouting(); // LinkGenerator
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
@@ -11,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Extensions;
|
||||
@@ -22,8 +19,6 @@ using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
|
||||
namespace Umbraco.Tests.Integration.TestServerTest
|
||||
@@ -39,7 +34,7 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
InMemoryConfiguration["Umbraco:CMS:Hosting:Debug"] = "true";
|
||||
|
||||
// create new WebApplicationFactory specifying 'this' as the IStartup instance
|
||||
var factory = new UmbracoWebApplicationFactory<UmbracoTestServerTestBase>(CreateHostBuilder);
|
||||
var factory = new UmbracoWebApplicationFactory<UmbracoTestServerTestBase>(CreateHostBuilder, BeforeHostStart);
|
||||
|
||||
// additional host configuration for web server integration tests
|
||||
Factory = factory.WithWebHostBuilder(builder =>
|
||||
@@ -75,11 +70,10 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
// call startup
|
||||
builder.Configure(app =>
|
||||
{
|
||||
UseTestLocalDb(app.ApplicationServices);
|
||||
Services = app.ApplicationServices;
|
||||
Configure(app);
|
||||
});
|
||||
}).UseEnvironment(Environments.Development);
|
||||
|
||||
}).UseEnvironment(Environments.Development);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@@ -119,15 +113,6 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
protected LinkGenerator LinkGenerator { get; private set; }
|
||||
protected WebApplicationFactory<UmbracoTestServerTestBase> Factory { get; private set; }
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
base.TerminateCoreRuntime();
|
||||
|
||||
Factory.Dispose();
|
||||
}
|
||||
|
||||
#region IStartup
|
||||
|
||||
public override void ConfigureServices(IServiceCollection services)
|
||||
@@ -162,7 +147,5 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
@@ -8,16 +8,30 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
public class UmbracoWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
|
||||
{
|
||||
private readonly Func<IHostBuilder> _createHostBuilder;
|
||||
private readonly Action<IHost> _beforeStart;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to create a new WebApplicationFactory
|
||||
/// </summary>
|
||||
/// <param name="createHostBuilder">Method to create the IHostBuilder</param>
|
||||
public UmbracoWebApplicationFactory(Func<IHostBuilder> createHostBuilder)
|
||||
/// <param name="beforeStart">Method to perform an action before IHost starts</param>
|
||||
public UmbracoWebApplicationFactory(Func<IHostBuilder> createHostBuilder, Action<IHost> beforeStart = null)
|
||||
{
|
||||
_createHostBuilder = createHostBuilder;
|
||||
_beforeStart = beforeStart;
|
||||
}
|
||||
|
||||
protected override IHostBuilder CreateHostBuilder() => _createHostBuilder();
|
||||
|
||||
protected override IHost CreateHost(IHostBuilder builder)
|
||||
{
|
||||
IHost host = builder.Build();
|
||||
|
||||
_beforeStart?.Invoke(host);
|
||||
|
||||
host.Start();
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
protected ILoggerFactory _loggerFactory;
|
||||
protected IUmbracoDatabaseFactory _databaseFactory;
|
||||
protected IEnumerable<TestDbMeta> _testDatabases;
|
||||
|
||||
protected UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands;
|
||||
protected IList<TestDbMeta> _testDatabases;
|
||||
|
||||
protected const int _threadCount = 2;
|
||||
|
||||
protected UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands = new UmbracoDatabase.CommandInfo[0];
|
||||
|
||||
protected BlockingCollection<TestDbMeta> _prepareQueue;
|
||||
protected BlockingCollection<TestDbMeta> _readySchemaQueue;
|
||||
@@ -92,46 +94,52 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
});
|
||||
}
|
||||
|
||||
protected void RebuildSchema(IDbCommand command, TestDbMeta meta)
|
||||
private void RebuildSchema(IDbCommand command, TestDbMeta meta)
|
||||
{
|
||||
if (_cachedDatabaseInitCommands != null)
|
||||
lock (_cachedDatabaseInitCommands)
|
||||
{
|
||||
foreach (var dbCommand in _cachedDatabaseInitCommands)
|
||||
if (!_cachedDatabaseInitCommands.Any())
|
||||
{
|
||||
|
||||
if (dbCommand.Text.StartsWith("SELECT "))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
command.CommandText = dbCommand.Text;
|
||||
command.Parameters.Clear();
|
||||
|
||||
foreach (var parameterInfo in dbCommand.Parameters)
|
||||
{
|
||||
AddParameter(command, parameterInfo);
|
||||
}
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
RebuildSchemaFirstTime(command, meta);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
foreach (var dbCommand in _cachedDatabaseInitCommands)
|
||||
{
|
||||
_databaseFactory.Configure(meta.ConnectionString, Core.Constants.DatabaseProviders.SqlServer);
|
||||
|
||||
using (var database = (UmbracoDatabase)_databaseFactory.CreateDatabase())
|
||||
if (dbCommand.Text.StartsWith("SELECT "))
|
||||
{
|
||||
database.LogCommands = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
var schemaCreator = new DatabaseSchemaCreator(database, _loggerFactory.CreateLogger<DatabaseSchemaCreator>(), _loggerFactory, new UmbracoVersion());
|
||||
schemaCreator.InitializeDatabaseSchema();
|
||||
command.CommandText = dbCommand.Text;
|
||||
command.Parameters.Clear();
|
||||
|
||||
transaction.Complete();
|
||||
foreach (var parameterInfo in dbCommand.Parameters)
|
||||
{
|
||||
AddParameter(command, parameterInfo);
|
||||
}
|
||||
|
||||
_cachedDatabaseInitCommands = database.Commands.ToArray();
|
||||
}
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildSchemaFirstTime(IDbCommand command, TestDbMeta meta)
|
||||
{
|
||||
_databaseFactory.Configure(meta.ConnectionString, Core.Constants.DatabaseProviders.SqlServer);
|
||||
|
||||
using (var database = (UmbracoDatabase)_databaseFactory.CreateDatabase())
|
||||
{
|
||||
database.LogCommands = true;
|
||||
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
var schemaCreator = new DatabaseSchemaCreator(database, _loggerFactory.CreateLogger<DatabaseSchemaCreator>(), _loggerFactory, new UmbracoVersion());
|
||||
schemaCreator.InitializeDatabaseSchema();
|
||||
|
||||
transaction.Complete();
|
||||
|
||||
_cachedDatabaseInitCommands = database.Commands.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
private static LocalDb.Instance _localDbInstance;
|
||||
private static string _filesPath;
|
||||
|
||||
private const int _threadCount = 2;
|
||||
|
||||
public static LocalDbTestDatabase Instance { get; private set; }
|
||||
|
||||
//It's internal because `Umbraco.Core.Persistence.LocalDb` is internal
|
||||
@@ -64,13 +62,17 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
var tempName = Guid.NewGuid().ToString("N");
|
||||
_localDbInstance.CreateDatabase(tempName, _filesPath);
|
||||
_localDbInstance.DetachDatabase(tempName);
|
||||
|
||||
_prepareQueue = new BlockingCollection<TestDbMeta>();
|
||||
_readySchemaQueue = new BlockingCollection<TestDbMeta>();
|
||||
_readyEmptyQueue = new BlockingCollection<TestDbMeta>();
|
||||
|
||||
foreach (var meta in _testDatabases)
|
||||
for (var i = 0; i < _testDatabases.Count; i++)
|
||||
{
|
||||
_localDb.CopyDatabaseFiles(tempName, _filesPath, targetDatabaseName: meta.Name, overwrite: true, delete: false);
|
||||
var meta = _testDatabases[i];
|
||||
var isLast = i == _testDatabases.Count - 1;
|
||||
|
||||
_localDb.CopyDatabaseFiles(tempName, _filesPath, targetDatabaseName: meta.Name, overwrite: true, delete: isLast);
|
||||
meta.ConnectionString = _localDbInstance.GetAttachedConnectionString(meta.Name, _filesPath);
|
||||
_prepareQueue.Add(meta);
|
||||
}
|
||||
@@ -85,7 +87,9 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
public void Finish()
|
||||
{
|
||||
if (_prepareQueue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_prepareQueue.CompleteAdding();
|
||||
while (_prepareQueue.TryTake(out _))
|
||||
@@ -100,14 +104,18 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
{ }
|
||||
|
||||
if (_filesPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = Path.Combine(_filesPath, DatabaseName).ToUpper();
|
||||
|
||||
foreach (var database in _localDbInstance.GetDatabases())
|
||||
{
|
||||
if (database.StartsWith(filename))
|
||||
{
|
||||
_localDbInstance.DropDatabase(database);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(_filesPath))
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
private readonly string _masterConnectionString;
|
||||
public const string DatabaseName = "UmbracoTests";
|
||||
|
||||
private const int _threadCount = 2;
|
||||
|
||||
public static SqlDeveloperTestDatabase Instance { get; private set; }
|
||||
|
||||
public SqlDeveloperTestDatabase(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory databaseFactory, string masterConnectionString)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
@@ -8,13 +9,20 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
public static ITestDatabase Create(string filesPath, ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory)
|
||||
{
|
||||
return string.IsNullOrEmpty(Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString"))
|
||||
? CreateLocalDb(filesPath, loggerFactory, dbFactory.Create())
|
||||
: CreateSqlDeveloper(loggerFactory, dbFactory.Create());
|
||||
var connectionString = Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString");
|
||||
|
||||
return string.IsNullOrEmpty(connectionString)
|
||||
? CreateLocalDb(filesPath, loggerFactory, dbFactory)
|
||||
: CreateSqlDeveloper(loggerFactory, dbFactory, connectionString);
|
||||
}
|
||||
|
||||
private static ITestDatabase CreateLocalDb(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
private static ITestDatabase CreateLocalDb(string filesPath, ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory)
|
||||
{
|
||||
if (!Directory.Exists(filesPath))
|
||||
{
|
||||
Directory.CreateDirectory(filesPath);
|
||||
}
|
||||
|
||||
var localDb = new LocalDb();
|
||||
|
||||
if (!localDb.IsAvailable)
|
||||
@@ -22,22 +30,21 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
throw new InvalidOperationException("LocalDB is not available.");
|
||||
}
|
||||
|
||||
return new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory);
|
||||
return new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory.Create());
|
||||
}
|
||||
|
||||
private static ITestDatabase CreateSqlDeveloper(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
private static ITestDatabase CreateSqlDeveloper(ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory, string connectionString)
|
||||
{
|
||||
// NOTE: Example setup for Linux box.
|
||||
// $ export SA_PASSWORD=Foobar123!
|
||||
// $ export UmbracoIntegrationTestConnectionString="Server=localhost,1433;User Id=sa;Password=$SA_PASSWORD;"
|
||||
// $ docker run -e 'ACCEPT_EULA=Y' -e "SA_PASSWORD=$SA_PASSWORD" -e 'MSSQL_PID=Developer' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2017-latest-ubuntu
|
||||
var connectionString = Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString");
|
||||
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("ENV: UmbracoIntegrationTestConnectionString is not set");
|
||||
}
|
||||
|
||||
return new SqlDeveloperTestDatabase(loggerFactory, dbFactory, connectionString);
|
||||
return new SqlDeveloperTestDatabase(loggerFactory, dbFactory.Create(), connectionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,14 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
foreach (var a in _testTeardown)
|
||||
a();
|
||||
}
|
||||
|
||||
_testTeardown = null;
|
||||
FirstTestInFixture = false;
|
||||
FirstTestInSession = false;
|
||||
|
||||
// Ensure CoreRuntime stopped (now it's a HostedService)
|
||||
IHost host = Services.GetRequiredService<IHost>();
|
||||
host.StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
@@ -94,14 +99,18 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
InMemoryConfiguration[Constants.Configuration.ConfigGlobal + ":" + nameof(GlobalSettings.InstallEmptyDatabase)] = "true";
|
||||
var hostBuilder = CreateHostBuilder();
|
||||
|
||||
var host = hostBuilder.Start();
|
||||
IHost host = hostBuilder.Build();
|
||||
BeforeHostStart(host);
|
||||
host.Start();
|
||||
|
||||
Services = host.Services;
|
||||
var app = new ApplicationBuilder(host.Services);
|
||||
|
||||
Configure(app);
|
||||
}
|
||||
|
||||
OnFixtureTearDown(() => host.Dispose());
|
||||
protected void BeforeHostStart(IHost host)
|
||||
{
|
||||
Services = host.Services;
|
||||
UseTestDatabase(Services);
|
||||
}
|
||||
|
||||
#region Generic Host Builder and Runtime
|
||||
@@ -110,13 +119,21 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
try
|
||||
{
|
||||
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
|
||||
switch (testOptions.Logger)
|
||||
switch (TestOptions.Logger)
|
||||
{
|
||||
case UmbracoTestOptions.Logger.Mock:
|
||||
return NullLoggerFactory.Instance;
|
||||
case UmbracoTestOptions.Logger.Serilog:
|
||||
return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddSerilog(); });
|
||||
return Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
|
||||
{
|
||||
var path = Path.Combine(TestHelper.WorkingDirectory, "logs", "umbraco_integration_tests_.txt");
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.File(path, rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
builder.AddSerilog(Log.Logger);
|
||||
});
|
||||
case UmbracoTestOptions.Logger.Console:
|
||||
return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||
}
|
||||
@@ -141,7 +158,6 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
// create separate Host instances. So instead of UseStartup, we just call ConfigureServices/Configure ourselves,
|
||||
// and in the case of the UmbracoTestServerTestBase it will use the ConfigureWebHost to Configure the IApplicationBuilder directly.
|
||||
//.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(GetType()); })
|
||||
.UseUmbraco()
|
||||
.ConfigureAppConfiguration((context, configBuilder) =>
|
||||
{
|
||||
context.HostingEnvironment = TestHelper.GetWebHostEnvironment();
|
||||
@@ -152,8 +168,15 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
})
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services.AddTransient(_ => CreateLoggerFactory());
|
||||
ConfigureServices(services);
|
||||
services.AddUnique(CreateLoggerFactory());
|
||||
|
||||
if (!TestOptions.Boot)
|
||||
{
|
||||
// If boot is false, we don't want the CoreRuntime hosted service to start
|
||||
// So we replace it with a Mock
|
||||
services.AddUnique(Mock.Of<IRuntime>());
|
||||
}
|
||||
});
|
||||
return hostBuilder;
|
||||
}
|
||||
@@ -213,29 +236,13 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app)
|
||||
{
|
||||
UseTestLocalDb(app.ApplicationServices);
|
||||
|
||||
//get the currently set options
|
||||
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
|
||||
if (testOptions.Boot)
|
||||
if (TestOptions.Boot)
|
||||
{
|
||||
Services.GetRequiredService<IBackOfficeSecurityFactory>().EnsureBackOfficeSecurity();
|
||||
Services.GetRequiredService<IUmbracoContextFactory>().EnsureUmbracoContext();
|
||||
app.UseUmbracoCore(); // Takes 200 ms
|
||||
|
||||
OnTestTearDown(TerminateCoreRuntime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Some IComponents hook onto static events (e.g. Published in ContentService)
|
||||
/// If these fire after the components host has been shutdown, errors can occur.
|
||||
/// If CoreRuntime.Start() is called We also need to de-register the events.
|
||||
/// </remarks>
|
||||
protected void TerminateCoreRuntime()
|
||||
{
|
||||
Services.GetRequiredService<IRuntime>().Terminate();
|
||||
StaticApplicationLogging.Initialize(null);
|
||||
app.UseUmbracoCore(); // This no longer starts CoreRuntime, it's very fast
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -246,25 +253,20 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
private static ITestDatabase _dbInstance;
|
||||
private static TestDbMeta _fixtureDbMeta;
|
||||
|
||||
protected void UseTestLocalDb(IServiceProvider serviceProvider)
|
||||
protected void UseTestDatabase(IServiceProvider serviceProvider)
|
||||
{
|
||||
var state = serviceProvider.GetRequiredService<IRuntimeState>();
|
||||
var testDatabaseFactoryProvider = serviceProvider.GetRequiredService<TestUmbracoDatabaseFactoryProvider>();
|
||||
var databaseFactory = serviceProvider.GetRequiredService<IUmbracoDatabaseFactory>();
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
|
||||
// This will create a db, install the schema and ensure the app is configured to run
|
||||
InstallTestLocalDb(testDatabaseFactoryProvider, databaseFactory, serviceProvider.GetRequiredService<ILoggerFactory>(), state, TestHelper.WorkingDirectory);
|
||||
SetupTestDatabase(testDatabaseFactoryProvider, databaseFactory, loggerFactory, state, TestHelper.WorkingDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or create an instance of <see cref="LocalDbTestDatabase"/>
|
||||
/// Get or create an instance of <see cref="ITestDatabase"/>
|
||||
/// </summary>
|
||||
/// <param name="filesPath"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="loggerFactory"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="dbFactory"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// There must only be ONE instance shared between all tests in a session
|
||||
/// </remarks>
|
||||
@@ -273,9 +275,12 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
lock (_dbLocker)
|
||||
{
|
||||
if (_dbInstance != null)
|
||||
{
|
||||
return _dbInstance;
|
||||
}
|
||||
|
||||
_dbInstance = TestDatabaseFactory.Create(filesPath, loggerFactory, dbFactory);
|
||||
|
||||
return _dbInstance;
|
||||
}
|
||||
}
|
||||
@@ -283,65 +288,47 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
/// <summary>
|
||||
/// Creates a LocalDb instance to use for the test
|
||||
/// </summary>
|
||||
private void InstallTestLocalDb(
|
||||
private void SetupTestDatabase(
|
||||
TestUmbracoDatabaseFactoryProvider testUmbracoDatabaseFactoryProvider,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
IRuntimeState runtimeState,
|
||||
string workingDirectory)
|
||||
{
|
||||
var dbFilePath = Path.Combine(workingDirectory, "LocalDb");
|
||||
|
||||
// get the currently set db options
|
||||
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
|
||||
|
||||
if (testOptions.Database == UmbracoTestOptions.Database.None)
|
||||
if (TestOptions.Database == UmbracoTestOptions.Database.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// need to manually register this factory
|
||||
DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
|
||||
|
||||
if (!Directory.Exists(dbFilePath))
|
||||
Directory.CreateDirectory(dbFilePath);
|
||||
var dbFilePath = Path.Combine(workingDirectory, "LocalDb");
|
||||
|
||||
var db = GetOrCreateDatabase(dbFilePath, loggerFactory, testUmbracoDatabaseFactoryProvider);
|
||||
|
||||
switch (testOptions.Database)
|
||||
switch (TestOptions.Database)
|
||||
{
|
||||
case UmbracoTestOptions.Database.NewSchemaPerTest:
|
||||
|
||||
// New DB + Schema
|
||||
var newSchemaDbMeta = db.AttachSchema();
|
||||
TestDbMeta newSchemaDbMeta = db.AttachSchema();
|
||||
|
||||
// Add teardown callback
|
||||
OnTestTearDown(() => db.Detach(newSchemaDbMeta));
|
||||
|
||||
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
|
||||
if (!databaseFactory.Configured)
|
||||
{
|
||||
databaseFactory.Configure(newSchemaDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
|
||||
// re-run the runtime level check
|
||||
runtimeState.DetermineRuntimeLevel();
|
||||
ConfigureTestDatabaseFactory(newSchemaDbMeta, databaseFactory, runtimeState);
|
||||
|
||||
Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level);
|
||||
|
||||
break;
|
||||
case UmbracoTestOptions.Database.NewEmptyPerTest:
|
||||
var newEmptyDbMeta = db.AttachEmpty();
|
||||
TestDbMeta newEmptyDbMeta = db.AttachEmpty();
|
||||
|
||||
// Add teardown callback
|
||||
OnTestTearDown(() => db.Detach(newEmptyDbMeta));
|
||||
|
||||
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
|
||||
if (!databaseFactory.Configured)
|
||||
{
|
||||
databaseFactory.Configure(newEmptyDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
|
||||
// re-run the runtime level check
|
||||
runtimeState.DetermineRuntimeLevel();
|
||||
ConfigureTestDatabaseFactory(newEmptyDbMeta, databaseFactory, runtimeState);
|
||||
|
||||
Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level);
|
||||
|
||||
@@ -353,21 +340,14 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
if (FirstTestInFixture)
|
||||
{
|
||||
// New DB + Schema
|
||||
var newSchemaFixtureDbMeta = db.AttachSchema();
|
||||
TestDbMeta newSchemaFixtureDbMeta = db.AttachSchema();
|
||||
_fixtureDbMeta = newSchemaFixtureDbMeta;
|
||||
|
||||
// Add teardown callback
|
||||
OnFixtureTearDown(() => db.Detach(newSchemaFixtureDbMeta));
|
||||
}
|
||||
|
||||
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
|
||||
if (!databaseFactory.Configured)
|
||||
{
|
||||
databaseFactory.Configure(_fixtureDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
|
||||
// re-run the runtime level check
|
||||
runtimeState.DetermineRuntimeLevel();
|
||||
ConfigureTestDatabaseFactory(_fixtureDbMeta, databaseFactory, runtimeState);
|
||||
|
||||
break;
|
||||
case UmbracoTestOptions.Database.NewEmptyPerFixture:
|
||||
@@ -377,29 +357,40 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
if (FirstTestInFixture)
|
||||
{
|
||||
// New DB + Schema
|
||||
var newEmptyFixtureDbMeta = db.AttachEmpty();
|
||||
TestDbMeta newEmptyFixtureDbMeta = db.AttachEmpty();
|
||||
_fixtureDbMeta = newEmptyFixtureDbMeta;
|
||||
|
||||
// Add teardown callback
|
||||
OnFixtureTearDown(() => db.Detach(newEmptyFixtureDbMeta));
|
||||
}
|
||||
|
||||
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
|
||||
if (!databaseFactory.Configured)
|
||||
{
|
||||
databaseFactory.Configure(_fixtureDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
ConfigureTestDatabaseFactory(_fixtureDbMeta, databaseFactory, runtimeState);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null);
|
||||
throw new ArgumentOutOfRangeException(nameof(TestOptions), TestOptions, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureTestDatabaseFactory(TestDbMeta meta, IUmbracoDatabaseFactory factory, IRuntimeState state)
|
||||
{
|
||||
ILogger<UmbracoIntegrationTest> log = Services.GetRequiredService<ILogger<UmbracoIntegrationTest>>();
|
||||
log.LogInformation($"ConfigureTestDatabaseFactory - Using test database: [{meta.Name}] - IsEmpty: [{meta.IsEmpty}]");
|
||||
|
||||
// It's just been pulled from container and wasn't used to create test database
|
||||
Assert.IsFalse(factory.Configured);
|
||||
|
||||
factory.Configure(meta.ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
state.DetermineRuntimeLevel();
|
||||
log.LogInformation($"ConfigureTestDatabaseFactory - Determined RuntimeLevel: [{state.Level}]");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common services
|
||||
|
||||
protected UmbracoTestAttribute TestOptions => TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
|
||||
|
||||
protected virtual T GetRequiredService<T>() => Services.GetRequiredService<T>();
|
||||
|
||||
public Dictionary<string, string> InMemoryConfiguration { get; } = new Dictionary<string, string>();
|
||||
@@ -429,7 +420,6 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
/// Returns the <see cref="ILoggerFactory"/>
|
||||
/// </summary>
|
||||
protected ILoggerFactory LoggerFactory => Services.GetRequiredService<ILoggerFactory>();
|
||||
|
||||
protected AppCaches AppCaches => Services.GetRequiredService<AppCaches>();
|
||||
protected IIOHelper IOHelper => Services.GetRequiredService<IIOHelper>();
|
||||
protected IShortStringHelper ShortStringHelper => Services.GetRequiredService<IShortStringHelper>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -10,10 +10,12 @@ using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.PublishedCache.NuCache;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
@@ -510,7 +512,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change it to invariant and back
|
||||
GetRequiredService<IPublishedSnapshotService>(); //hack to ensure events are initialized
|
||||
|
||||
//hack to ensure events are initialized
|
||||
(GetRequiredService<IPublishedSnapshotService>() as PublishedSnapshotService)?.OnApplicationInit(null, null);
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
@@ -599,7 +603,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
// one simple content type, invariant
|
||||
// can change it to variant and back
|
||||
// can then switch one property to variant
|
||||
GetRequiredService<IPublishedSnapshotService>(); //hack to ensure events are initialized
|
||||
|
||||
//hack to ensure events are initialized
|
||||
(GetRequiredService<IPublishedSnapshotService>() as PublishedSnapshotService)?.OnApplicationInit(null, null);
|
||||
|
||||
var globalSettings = new GlobalSettings();
|
||||
|
||||
@@ -690,7 +696,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change an invariant property to variant and back
|
||||
GetRequiredService<IPublishedSnapshotService>(); //hack to ensure events are initialized
|
||||
|
||||
//hack to ensure events are initialized
|
||||
(GetRequiredService<IPublishedSnapshotService>() as PublishedSnapshotService)?.OnApplicationInit(null, null);
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
@@ -967,8 +975,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
// can change the composing content type to invariant and back
|
||||
// can change the composed content type to invariant and back
|
||||
|
||||
|
||||
GetRequiredService<IPublishedSnapshotService>(); //hack to ensure events are initialized
|
||||
//hack to ensure events are initialized
|
||||
(GetRequiredService<IPublishedSnapshotService>() as PublishedSnapshotService)?.OnApplicationInit(null, null);
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var composing = CreateContentType(ContentVariation.Culture, "composing");
|
||||
@@ -1064,7 +1072,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
// can change the composing content type to invariant and back
|
||||
// can change the variant composed content type to invariant and back
|
||||
|
||||
GetRequiredService<IPublishedSnapshotService>(); //hack to ensure events are initialized
|
||||
//hack to ensure events are initialized
|
||||
(GetRequiredService<IPublishedSnapshotService>() as PublishedSnapshotService)?.OnApplicationInit(null, null);
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var composing = CreateContentType(ContentVariation.Culture, "composing");
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -31,7 +32,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)]
|
||||
public class ThreadSafetyServiceTest : UmbracoIntegrationTest
|
||||
{
|
||||
private IContentService ContentService => GetRequiredService<IContentService>();
|
||||
@@ -98,13 +99,15 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
if (Environment.GetEnvironmentVariable("UMBRACO_TMP") != null)
|
||||
Assert.Ignore("Do not run on VSTS.");
|
||||
|
||||
var log = GetRequiredService<ILogger<ThreadSafetyServiceTest>>();
|
||||
|
||||
// the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton
|
||||
var contentService = (ContentService)ContentService;
|
||||
|
||||
var threads = new List<Thread>();
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
Debug.WriteLine("Starting...");
|
||||
log.LogInformation("Starting...");
|
||||
|
||||
var done = TraceLocks();
|
||||
|
||||
@@ -114,12 +117,12 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Running...", Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
var name1 = "test-" + Guid.NewGuid();
|
||||
var content1 = contentService.Create(name1, -1, "umbTextpage");
|
||||
|
||||
Debug.WriteLine("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId);
|
||||
Save(contentService, content1);
|
||||
|
||||
Thread.Sleep(100); //quick pause for maximum overlap!
|
||||
@@ -127,7 +130,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
var name2 = "test-" + Guid.NewGuid();
|
||||
var content2 = contentService.Create(name2, -1, "umbTextpage");
|
||||
|
||||
Debug.WriteLine("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId);
|
||||
Save(contentService, content2);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -139,16 +142,16 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
}
|
||||
|
||||
// start all threads
|
||||
Debug.WriteLine("Starting threads");
|
||||
log.LogInformation("Starting threads");
|
||||
threads.ForEach(x => x.Start());
|
||||
|
||||
// wait for all to complete
|
||||
Debug.WriteLine("Joining threads");
|
||||
log.LogInformation("Joining threads");
|
||||
threads.ForEach(x => x.Join());
|
||||
|
||||
done.Set();
|
||||
|
||||
Debug.WriteLine("Checking exceptions");
|
||||
log.LogInformation("Checking exceptions");
|
||||
if (exceptions.Count == 0)
|
||||
{
|
||||
//now look up all items, there should be 40!
|
||||
@@ -166,13 +169,16 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("UMBRACO_TMP") != null)
|
||||
Assert.Ignore("Do not run on VSTS.");
|
||||
|
||||
var log = GetRequiredService<ILogger<ThreadSafetyServiceTest>>();
|
||||
|
||||
// mimick the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton
|
||||
var mediaService = (MediaService)MediaService;
|
||||
|
||||
var threads = new List<Thread>();
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
Debug.WriteLine("Starting...");
|
||||
log.LogInformation("Starting...");
|
||||
|
||||
var done = TraceLocks();
|
||||
|
||||
@@ -182,18 +188,18 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Running...", Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
var name1 = "test-" + Guid.NewGuid();
|
||||
var media1 = mediaService.CreateMedia(name1, -1, Constants.Conventions.MediaTypes.Folder);
|
||||
Debug.WriteLine("[{0}] Saving media #1.", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Saving media #1.", Thread.CurrentThread.ManagedThreadId);
|
||||
Save(mediaService, media1);
|
||||
|
||||
Thread.Sleep(100); //quick pause for maximum overlap!
|
||||
|
||||
var name2 = "test-" + Guid.NewGuid();
|
||||
var media2 = mediaService.CreateMedia(name2, -1, Constants.Conventions.MediaTypes.Folder);
|
||||
Debug.WriteLine("[{0}] Saving media #2.", Thread.CurrentThread.ManagedThreadId);
|
||||
log.LogInformation("[{0}] Saving media #2.", Thread.CurrentThread.ManagedThreadId);
|
||||
Save(mediaService, media2);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -143,11 +143,14 @@ namespace Umbraco.Core.DependencyInjection
|
||||
builder.Services.AddUnique<IUmbracoDatabase>(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().CreateDatabase());
|
||||
builder.Services.AddUnique<ISqlContext>(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().SqlContext);
|
||||
builder.Services.AddUnique<IUmbracoVersion, UmbracoVersion>();
|
||||
builder.Services.AddUnique<IRuntime, CoreRuntime>();
|
||||
builder.Services.AddUnique<IRuntimeState, RuntimeState>();
|
||||
builder.Services.AddUnique<IHostingEnvironment, AspNetCoreHostingEnvironment>();
|
||||
builder.Services.AddUnique<IMainDom, MainDom>();
|
||||
|
||||
builder.Services.AddUnique<IRuntime, CoreRuntime>();
|
||||
builder.Services.AddHostedService<IRuntime>(factory => factory.GetRequiredService<IRuntime>());
|
||||
|
||||
builder.AddCoreInitialServices();
|
||||
builder.AddComposers();
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog.Context;
|
||||
using Smidge;
|
||||
using Smidge.Nuglify;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
|
||||
@@ -23,9 +21,9 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static bool UmbracoCanBoot(this IApplicationBuilder app)
|
||||
{
|
||||
var runtime = app.ApplicationServices.GetRequiredService<IRuntime>();
|
||||
var state = app.ApplicationServices.GetRequiredService<IRuntimeState>();
|
||||
// can't continue if boot failed
|
||||
return runtime.State.Level > RuntimeLevel.BootFailed;
|
||||
return state.Level > RuntimeLevel.BootFailed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,25 +37,10 @@ namespace Umbraco.Extensions
|
||||
|
||||
if (!app.UmbracoCanBoot()) return app;
|
||||
|
||||
var hostingEnvironment = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data));
|
||||
|
||||
var runtime = app.ApplicationServices.GetRequiredService<IRuntime>();
|
||||
|
||||
// Register a listener for application shutdown in order to terminate the runtime
|
||||
var hostLifetime = app.ApplicationServices.GetRequiredService<IApplicationShutdownRegistry>();
|
||||
var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime);
|
||||
hostLifetime.RegisterObject(runtimeShutdown);
|
||||
|
||||
// Register our global threadabort enricher for logging
|
||||
var threadAbortEnricher = app.ApplicationServices.GetRequiredService<ThreadAbortExceptionEnricher>();
|
||||
LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context
|
||||
|
||||
StaticApplicationLogging.Initialize(app.ApplicationServices.GetRequiredService<ILoggerFactory>());
|
||||
|
||||
// Start the runtime!
|
||||
runtime.Start();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -121,33 +104,6 @@ namespace Umbraco.Extensions
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the runtime is shutdown when the application is shutting down
|
||||
/// </summary>
|
||||
private class CoreRuntimeShutdown : IRegisteredObject
|
||||
{
|
||||
public CoreRuntimeShutdown(IRuntime runtime, IApplicationShutdownRegistry hostLifetime)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_hostLifetime = hostLifetime;
|
||||
}
|
||||
|
||||
private bool _completed = false;
|
||||
private readonly IRuntime _runtime;
|
||||
private readonly IApplicationShutdownRegistry _hostLifetime;
|
||||
|
||||
public void Stop(bool immediate)
|
||||
{
|
||||
if (!_completed)
|
||||
{
|
||||
_completed = true;
|
||||
_runtime.Terminate();
|
||||
_hostLifetime.UnregisterObject(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Diagnostics;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Umbraco.Web.Common.Formatters;
|
||||
using Umbraco.Web.Common.Install;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
using Umbraco.Web.Common.Macros;
|
||||
@@ -38,7 +33,6 @@ namespace Umbraco.Web.Common.Runtime
|
||||
/// Adds/replaces AspNetCore specific services
|
||||
/// </summary>
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
[ComposeAfter(typeof(CoreInitialComposer))]
|
||||
public class AspNetCoreComposer : ComponentComposer<AspNetCoreComponent>, IComposer
|
||||
{
|
||||
public override void Compose(IUmbracoBuilder builder)
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Umbraco.Web.UI.NetCore
|
||||
{
|
||||
x.ClearProviders();
|
||||
})
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
|
||||
.UseUmbraco();
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,19 @@
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.Templates;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing.CompositionExtensions;
|
||||
using Umbraco.Web.Macros;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Security.Providers;
|
||||
|
||||
namespace Umbraco.Web.Runtime
|
||||
{
|
||||
// web's initial composer composes after core's, and before all core composers
|
||||
[ComposeAfter(typeof(CoreInitialComposer))]
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
public sealed class WebInitialComposer : ComponentComposer<WebInitialComponent>
|
||||
{
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace Umbraco.Web
|
||||
|
||||
protected UmbracoApplicationBase()
|
||||
{
|
||||
|
||||
HostingSettings hostingSettings = null;
|
||||
GlobalSettings globalSettings = null;
|
||||
SecuritySettings securitySettings = null;
|
||||
@@ -60,8 +59,6 @@ namespace Umbraco.Web
|
||||
|
||||
var backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, ioHelper, _loggerFactory.CreateLogger<AspNetBackOfficeInfo>(), Options.Create(webRoutingSettings));
|
||||
var profiler = GetWebProfiler(hostingEnvironment);
|
||||
StaticApplicationLogging.Initialize(_loggerFactory);
|
||||
Logger = NullLogger<UmbracoApplicationBase>.Instance;
|
||||
}
|
||||
|
||||
private IProfiler GetWebProfiler(IHostingEnvironment hostingEnvironment)
|
||||
@@ -87,7 +84,6 @@ namespace Umbraco.Web
|
||||
_loggerFactory = loggerFactory;
|
||||
|
||||
Logger = logger;
|
||||
StaticApplicationLogging.Initialize(_loggerFactory);
|
||||
}
|
||||
|
||||
protected ILogger<UmbracoApplicationBase> Logger { get; }
|
||||
@@ -189,7 +185,6 @@ namespace Umbraco.Web
|
||||
LogContext.Push(new HttpRequestIdEnricher(_factory.GetRequiredService<IRequestCache>()));
|
||||
|
||||
_runtime = _factory.GetRequiredService<IRuntime>();
|
||||
_runtime.Start();
|
||||
}
|
||||
|
||||
// called by ASP.NET (auto event wireup) once per app domain
|
||||
@@ -237,7 +232,6 @@ namespace Umbraco.Web
|
||||
{
|
||||
if (_runtime != null)
|
||||
{
|
||||
_runtime.Terminate();
|
||||
_runtime.DisposeIfDisposable();
|
||||
|
||||
_runtime = null;
|
||||
|
||||
Reference in New Issue
Block a user