diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index 59c75192b9..26e46eab0a 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -19,4 +19,4 @@ using System.Resources;
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("0.5.0")]
-[assembly: AssemblyInformationalVersion("0.5.0-alpha001")]
+[assembly: AssemblyInformationalVersion("0.5.0-alpha002")]
diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs
index 13c5e063be..3427c55731 100644
--- a/src/Umbraco.Core/Composing/TypeFinder.cs
+++ b/src/Umbraco.Core/Composing/TypeFinder.cs
@@ -156,6 +156,7 @@ namespace Umbraco.Core.Composing
"MiniProfiler.",
"Owin,",
"SQLite",
+ "ReSharperTestRunner32" // used by resharper testrunner
};
///
diff --git a/src/Umbraco.Core/Services/IRuntimeState.cs b/src/Umbraco.Core/Services/IRuntimeState.cs
index 4b57908ea7..fd817f1986 100644
--- a/src/Umbraco.Core/Services/IRuntimeState.cs
+++ b/src/Umbraco.Core/Services/IRuntimeState.cs
@@ -50,5 +50,10 @@ namespace Umbraco.Core
///
BootFailedException BootFailedException { get; }
+ ///
+ /// Determines the runtime level.
+ ///
+ void DetermineRuntimeLevel();
+
}
}
diff --git a/src/Umbraco.Core/SimpleMainDom.cs b/src/Umbraco.Core/SimpleMainDom.cs
index e6bdda67d7..a2ef0b8d78 100644
--- a/src/Umbraco.Core/SimpleMainDom.cs
+++ b/src/Umbraco.Core/SimpleMainDom.cs
@@ -8,11 +8,12 @@ namespace Umbraco.Core
///
/// Provides a simple implementation of .
///
- public class SimpleMainDom : IMainDom
+ public class SimpleMainDom : IMainDom, IDisposable
{
private readonly object _locko = new object();
private readonly List> _callbacks = new List>();
private bool _isStopping;
+ private bool _disposedValue;
///
public bool IsMainDom { get; private set; } = true;
@@ -59,5 +60,24 @@ namespace Umbraco.Core
IsMainDom = false;
}
}
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ Stop();
+ }
+ _disposedValue = true;
+ }
+ }
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs
index b7d9cde9d1..b1254a4beb 100644
--- a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs
+++ b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs
@@ -20,6 +20,7 @@ namespace Umbraco.Examine
composition.RegisterUnique();
composition.RegisterUnique();
composition.RegisterUnique();
+ composition.RegisterUnique();
}
}
}
diff --git a/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs
new file mode 100644
index 0000000000..f4946d491e
--- /dev/null
+++ b/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Examine
+{
+ public interface ILuceneDirectoryFactory
+ {
+ Lucene.Net.Store.Directory CreateDirectory(string indexName);
+ }
+}
diff --git a/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs
new file mode 100644
index 0000000000..8f3bf8bf69
--- /dev/null
+++ b/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs
@@ -0,0 +1,69 @@
+using Umbraco.Core.Configuration;
+using Umbraco.Core;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Hosting;
+using Lucene.Net.Store;
+using System.IO;
+using System;
+using Examine.LuceneEngine.Directories;
+
+namespace Umbraco.Examine
+{
+
+ public class LuceneFileSystemDirectoryFactory : ILuceneDirectoryFactory
+ {
+ private readonly ITypeFinder _typeFinder;
+ private readonly IHostingEnvironment _hostingEnvironment;
+ private readonly IIndexCreatorSettings _settings;
+
+ public LuceneFileSystemDirectoryFactory(ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, IIndexCreatorSettings settings)
+ {
+ _typeFinder = typeFinder;
+ _hostingEnvironment = hostingEnvironment;
+ _settings = settings;
+ }
+
+ public Lucene.Net.Store.Directory CreateDirectory(string indexName) => CreateFileSystemLuceneDirectory(indexName);
+
+ ///
+ /// Creates a file system based Lucene with the correct locking guidelines for Umbraco
+ ///
+ ///
+ /// The folder name to store the index (single word, not a fully qualified folder) (i.e. Internal)
+ ///
+ ///
+ public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName)
+ {
+
+ var dirInfo = new DirectoryInfo(Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData), "ExamineIndexes", folderName));
+ if (!dirInfo.Exists)
+ System.IO.Directory.CreateDirectory(dirInfo.FullName);
+
+ //check if there's a configured directory factory, if so create it and use that to create the lucene dir
+ var configuredDirectoryFactory = _settings.LuceneDirectoryFactory;
+
+ if (!configuredDirectoryFactory.IsNullOrWhiteSpace())
+ {
+ //this should be a fully qualified type
+ var factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory);
+ if (factoryType == null) throw new NullReferenceException("No directory type found for value: " + configuredDirectoryFactory);
+ var directoryFactory = (IDirectoryFactory)Activator.CreateInstance(factoryType);
+ return directoryFactory.CreateDirectory(dirInfo);
+ }
+
+ //no dir factory, just create a normal fs directory
+
+ var luceneDir = new SimpleFSDirectory(dirInfo);
+
+ //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain
+ //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock
+ //which simply checks the existence of the lock file
+ // The full syntax of this is: new NoPrefixSimpleFsLockFactory(dirInfo)
+ // however, we are setting the DefaultLockFactory in startup so we'll use that instead since it can be managed globally.
+ luceneDir.SetLockFactory(DirectoryFactory.DefaultLockFactory(dirInfo));
+ return luceneDir;
+
+
+ }
+ }
+}
diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs
index 5c6b111d87..8ecb1b4421 100644
--- a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs
+++ b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs
@@ -29,47 +29,6 @@ namespace Umbraco.Examine
_settings = settings.Value;
}
- public abstract IEnumerable Create();
-
- ///
- /// Creates a file system based Lucene with the correct locking guidelines for Umbraco
- ///
- ///
- /// The folder name to store the index (single word, not a fully qualified folder) (i.e. Internal)
- ///
- ///
- public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName)
- {
-
- var dirInfo = new DirectoryInfo(Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData), "ExamineIndexes", folderName));
- if (!dirInfo.Exists)
- System.IO.Directory.CreateDirectory(dirInfo.FullName);
-
- //check if there's a configured directory factory, if so create it and use that to create the lucene dir
- var configuredDirectoryFactory = _settings.LuceneDirectoryFactory;
-
- if (!configuredDirectoryFactory.IsNullOrWhiteSpace())
- {
- //this should be a fully qualified type
- var factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory);
- if (factoryType == null) throw new NullReferenceException("No directory type found for value: " + configuredDirectoryFactory);
- var directoryFactory = (IDirectoryFactory)Activator.CreateInstance(factoryType);
- return directoryFactory.CreateDirectory(dirInfo);
- }
-
- //no dir factory, just create a normal fs directory
-
- var luceneDir = new SimpleFSDirectory(dirInfo);
-
- //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain
- //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock
- //which simply checks the existence of the lock file
- // The full syntax of this is: new NoPrefixSimpleFsLockFactory(dirInfo)
- // however, we are setting the DefaultLockFactory in startup so we'll use that instead since it can be managed globally.
- luceneDir.SetLockFactory(DirectoryFactory.DefaultLockFactory(dirInfo));
- return luceneDir;
-
-
- }
+ public abstract IEnumerable Create();
}
}
diff --git a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs
new file mode 100644
index 0000000000..327328390b
--- /dev/null
+++ b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs
@@ -0,0 +1,24 @@
+using Lucene.Net.Store;
+using System;
+
+namespace Umbraco.Examine
+{
+ public class LuceneRAMDirectoryFactory : ILuceneDirectoryFactory
+ {
+ public LuceneRAMDirectoryFactory()
+ {
+
+ }
+
+ public Lucene.Net.Store.Directory CreateDirectory(string indexName) => new RandomIdRAMDirectory();
+
+ private class RandomIdRAMDirectory : RAMDirectory
+ {
+ private readonly string _lockId = Guid.NewGuid().ToString();
+ public override string GetLockId()
+ {
+ return _lockId;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs
index dc2a5570a6..4cb26d5ae5 100644
--- a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs
+++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs
@@ -19,9 +19,8 @@ namespace Umbraco.Examine
///
/// An indexer for Umbraco content and media
///
- public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex
+ public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex, IDisposable
{
-
protected ILocalizationService LanguageService { get; }
#region Constructors
diff --git a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs
index c4eb2249fa..39113b4f50 100644
--- a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs
+++ b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs
@@ -14,6 +14,7 @@ using Microsoft.Extensions.Options;
namespace Umbraco.Examine
{
+
///
/// Creates the indexes used by Umbraco
///
@@ -30,7 +31,8 @@ namespace Umbraco.Examine
IUmbracoIndexConfig umbracoIndexConfig,
IHostingEnvironment hostingEnvironment,
IRuntimeState runtimeState,
- IOptions settings) : base(typeFinder, hostingEnvironment, settings)
+ IOptions settings,
+ ILuceneDirectoryFactory directoryFactory) : base(typeFinder, hostingEnvironment, settings)
{
ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger));
LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService));
@@ -39,11 +41,13 @@ namespace Umbraco.Examine
UmbracoIndexConfig = umbracoIndexConfig;
HostingEnvironment = hostingEnvironment ?? throw new System.ArgumentNullException(nameof(hostingEnvironment));
RuntimeState = runtimeState ?? throw new System.ArgumentNullException(nameof(runtimeState));
+ DirectoryFactory = directoryFactory;
}
protected IProfilingLogger ProfilingLogger { get; }
protected IHostingEnvironment HostingEnvironment { get; }
protected IRuntimeState RuntimeState { get; }
+ protected ILuceneDirectoryFactory DirectoryFactory { get; }
protected ILocalizationService LanguageService { get; }
protected IPublicAccessService PublicAccessService { get; }
protected IMemberService MemberService { get; }
@@ -67,7 +71,7 @@ namespace Umbraco.Examine
{
var index = new UmbracoContentIndex(
Constants.UmbracoIndexes.InternalIndexName,
- CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath),
+ DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.InternalIndexPath),
new UmbracoFieldDefinitionCollection(),
new CultureInvariantWhitespaceAnalyzer(),
ProfilingLogger,
@@ -83,7 +87,7 @@ namespace Umbraco.Examine
{
var index = new UmbracoContentIndex(
Constants.UmbracoIndexes.ExternalIndexName,
- CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath),
+ DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.ExternalIndexPath),
new UmbracoFieldDefinitionCollection(),
new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30),
ProfilingLogger,
@@ -99,7 +103,7 @@ namespace Umbraco.Examine
var index = new UmbracoMemberIndex(
Constants.UmbracoIndexes.MembersIndexName,
new UmbracoFieldDefinitionCollection(),
- CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath),
+ DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.MembersIndexPath),
new CultureInvariantWhitespaceAnalyzer(),
ProfilingLogger,
HostingEnvironment,
diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
index 15d01bdd12..790cab0913 100644
--- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
@@ -5,13 +5,10 @@ using System.Linq;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Composing;
-using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
-using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
-using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
@@ -35,42 +32,78 @@ namespace Umbraco.Web.Compose
public void Initialize()
{
//Send notifications for the send to publish action
- ContentService.SentToPublish += (sender, args) => _notifier.Notify(_actions.GetAction(), args.Entity);
-
+ ContentService.SentToPublish += ContentService_SentToPublish;
//Send notifications for the published action
- ContentService.Published += (sender, args) => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray());
-
+ ContentService.Published += ContentService_Published;
//Send notifications for the saved action
- ContentService.Sorted += (sender, args) => ContentServiceSorted(_notifier, sender, args, _actions);
-
+ ContentService.Sorted += ContentService_Sorted;
//Send notifications for the update and created actions
- ContentService.Saved += (sender, args) => ContentServiceSaved(_notifier, sender, args, _actions);
-
+ ContentService.Saved += ContentService_Saved;
//Send notifications for the unpublish action
- ContentService.Unpublished += (sender, args) => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray());
-
+ ContentService.Unpublished += ContentService_Unpublished;
//Send notifications for the move/move to recycle bin and restore actions
- ContentService.Moved += (sender, args) => ContentServiceMoved(_notifier, sender, args, _actions);
-
+ ContentService.Moved += ContentService_Moved;
//Send notifications for the delete action when content is moved to the recycle bin
- ContentService.Trashed += (sender, args) => _notifier.Notify(_actions.GetAction(), args.MoveInfoCollection.Select(m => m.Entity).ToArray());
-
+ ContentService.Trashed += ContentService_Trashed;
//Send notifications for the copy action
- ContentService.Copied += (sender, args) => _notifier.Notify(_actions.GetAction(), args.Original);
-
+ ContentService.Copied += ContentService_Copied;
//Send notifications for the rollback action
- ContentService.RolledBack += (sender, args) => _notifier.Notify(_actions.GetAction(), args.Entity);
-
+ ContentService.RolledBack += ContentService_RolledBack;
//Send notifications for the public access changed action
- PublicAccessService.Saved += (sender, args) => PublicAccessServiceSaved(_notifier, sender, args, _contentService, _actions);
+ PublicAccessService.Saved += PublicAccessService_Saved;
- UserService.UserGroupPermissionsAssigned += (sender, args) => UserServiceUserGroupPermissionsAssigned(_notifier, sender, args, _contentService, _actions);
+ UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned;
}
public void Terminate()
- { }
+ {
+ ContentService.SentToPublish -= ContentService_SentToPublish;
+ ContentService.Published -= ContentService_Published;
+ ContentService.Sorted -= ContentService_Sorted;
+ ContentService.Saved -= ContentService_Saved;
+ ContentService.Unpublished -= ContentService_Unpublished;
+ ContentService.Moved -= ContentService_Moved;
+ ContentService.Trashed -= ContentService_Trashed;
+ ContentService.Copied -= ContentService_Copied;
+ ContentService.RolledBack -= ContentService_RolledBack;
+ PublicAccessService.Saved -= PublicAccessService_Saved;
+ UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned;
+ }
- private void ContentServiceSorted(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs args, ActionCollection actions)
+ private void UserService_UserGroupPermissionsAssigned(IUserService sender, Core.Events.SaveEventArgs args)
+ => UserServiceUserGroupPermissionsAssigned(args, _contentService);
+
+ private void PublicAccessService_Saved(IPublicAccessService sender, Core.Events.SaveEventArgs args)
+ => PublicAccessServiceSaved(args, _contentService);
+
+ private void ContentService_RolledBack(IContentService sender, Core.Events.RollbackEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.Entity);
+
+ private void ContentService_Copied(IContentService sender, Core.Events.CopyEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.Original);
+
+ private void ContentService_Trashed(IContentService sender, Core.Events.MoveEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.MoveInfoCollection.Select(m => m.Entity).ToArray());
+
+ private void ContentService_Moved(IContentService sender, Core.Events.MoveEventArgs args)
+ => ContentServiceMoved(args);
+
+ private void ContentService_Unpublished(IContentService sender, Core.Events.PublishEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray());
+
+ private void ContentService_Saved(IContentService sender, Core.Events.ContentSavedEventArgs args)
+ => ContentServiceSaved(args);
+
+ private void ContentService_Sorted(IContentService sender, Core.Events.SaveEventArgs args)
+ => ContentServiceSorted(sender, args);
+
+ private void ContentService_Published(IContentService sender, Core.Events.ContentPublishedEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray());
+
+ private void ContentService_SentToPublish(IContentService sender, Core.Events.SendToPublishEventArgs args)
+ => _notifier.Notify(_actions.GetAction(), args.Entity);
+
+ private void ContentServiceSorted(IContentService sender, Core.Events.SaveEventArgs args)
{
var parentId = args.SavedEntities.Select(x => x.ParentId).Distinct().ToList();
if (parentId.Count != 1) return; // this shouldn't happen, for sorting all entities will have the same parent id
@@ -82,10 +115,10 @@ namespace Umbraco.Web.Compose
var parent = sender.GetById(parentId[0]);
if (parent == null) return; // this shouldn't happen
- notifier.Notify(actions.GetAction(), new[] { parent });
+ _notifier.Notify(_actions.GetAction(), new[] { parent });
}
- private void ContentServiceSaved(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs args, ActionCollection actions)
+ private void ContentServiceSaved(Core.Events.SaveEventArgs args)
{
var newEntities = new List();
var updatedEntities = new List();
@@ -105,21 +138,21 @@ namespace Umbraco.Web.Compose
updatedEntities.Add(entity);
}
}
- notifier.Notify(actions.GetAction(), newEntities.ToArray());
- notifier.Notify(actions.GetAction(), updatedEntities.ToArray());
+ _notifier.Notify(_actions.GetAction(), newEntities.ToArray());
+ _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray());
}
- private void UserServiceUserGroupPermissionsAssigned(Notifier notifier, IUserService sender, Core.Events.SaveEventArgs args, IContentService contentService, ActionCollection actions)
+ private void UserServiceUserGroupPermissionsAssigned(Core.Events.SaveEventArgs args, IContentService contentService)
{
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.EntityId)).ToArray();
if(entities.Any() == false)
{
return;
}
- notifier.Notify(actions.GetAction(), entities);
+ _notifier.Notify(_actions.GetAction(), entities);
}
- private void ContentServiceMoved(Notifier notifier, IContentService sender, Core.Events.MoveEventArgs args, ActionCollection actions)
+ private void ContentServiceMoved(Core.Events.MoveEventArgs args)
{
// notify about the move for all moved items
_notifier.Notify(_actions.GetAction(), args.MoveInfoCollection.Select(m => m.Entity).ToArray());
@@ -135,14 +168,14 @@ namespace Umbraco.Web.Compose
}
}
- private void PublicAccessServiceSaved(Notifier notifier, IPublicAccessService sender, Core.Events.SaveEventArgs args, IContentService contentService, ActionCollection actions)
+ private void PublicAccessServiceSaved(Core.Events.SaveEventArgs args, IContentService contentService)
{
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray();
if(entities.Any() == false)
{
return;
}
- notifier.Notify(actions.GetAction(), entities);
+ _notifier.Notify(_actions.GetAction(), entities);
}
///
diff --git a/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs b/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs
index 37bcfb1ceb..a917cfe0ef 100644
--- a/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/PublicAccessComponent.cs
@@ -16,13 +16,15 @@ namespace Umbraco.Web.Compose
public void Initialize()
{
- MemberGroupService.Saved += (s, e) => MemberGroupService_Saved(s, e, _publicAccessService);
+ MemberGroupService.Saved += MemberGroupService_Saved;
}
public void Terminate()
- { }
+ {
+ MemberGroupService.Saved -= MemberGroupService_Saved;
+ }
- static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e, IPublicAccessService publicAccessService)
+ private void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e)
{
foreach (var grp in e.SavedEntities)
{
@@ -32,7 +34,7 @@ namespace Umbraco.Web.Compose
&& grp.AdditionalData["previousName"].ToString().IsNullOrWhiteSpace() == false
&& grp.AdditionalData["previousName"].ToString() != grp.Name)
{
- publicAccessService.RenameMemberGroupRoleRules(grp.AdditionalData["previousName"].ToString(), grp.Name);
+ _publicAccessService.RenameMemberGroupRoleRules(grp.AdditionalData["previousName"].ToString(), grp.Name);
}
}
}
diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs b/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs
index 56a97e4cba..3418dfcfc0 100644
--- a/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs
@@ -23,7 +23,9 @@ namespace Umbraco.Core.Compose
}
public void Terminate()
- { }
+ {
+ ContentService.Copied -= ContentServiceCopied;
+ }
private void ContentServiceCopied(IContentService sender, Events.CopyEventArgs e)
{
diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs
index c81aa2fd7d..aa92972e9c 100644
--- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs
@@ -24,47 +24,52 @@ namespace Umbraco.Core.Compose
public void Initialize()
{
- ContentService.Moved += (sender, args) => ContentService_Moved(sender, args, _relationService);
- ContentService.Trashed += (sender, args) => ContentService_Trashed(sender, args, _relationService, _entityService, _textService, _auditService);
- MediaService.Moved += (sender, args) => MediaService_Moved(sender, args, _relationService);
- MediaService.Trashed += (sender, args) => MediaService_Trashed(sender, args, _relationService, _entityService, _textService, _auditService);
+ ContentService.Moved += ContentService_Moved;
+ ContentService.Trashed += ContentService_Trashed;
+ MediaService.Moved += MediaService_Moved;
+ MediaService.Trashed += MediaService_Trashed;
}
public void Terminate()
- { }
+ {
+ ContentService.Moved -= ContentService_Moved;
+ ContentService.Trashed -= ContentService_Trashed;
+ MediaService.Moved -= MediaService_Moved;
+ MediaService.Trashed -= MediaService_Trashed;
+ }
- private static void ContentService_Moved(IContentService sender, MoveEventArgs e, IRelationService relationService)
+ private void ContentService_Moved(IContentService sender, MoveEventArgs e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
{
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
- var relations = relationService.GetByChildId(item.Entity.Id);
+ var relations = _relationService.GetByChildId(item.Entity.Id);
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
{
- relationService.Delete(relation);
+ _relationService.Delete(relation);
}
}
}
- private static void MediaService_Moved(IMediaService sender, MoveEventArgs e, IRelationService relationService)
+ private void MediaService_Moved(IMediaService sender, MoveEventArgs e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
{
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
- var relations = relationService.GetByChildId(item.Entity.Id);
+ var relations = _relationService.GetByChildId(item.Entity.Id);
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
{
- relationService.Delete(relation);
+ _relationService.Delete(relation);
}
}
}
- private static void ContentService_Trashed(IContentService sender, MoveEventArgs e, IRelationService relationService, IEntityService entityService, ILocalizedTextService textService, IAuditService auditService)
+ private void ContentService_Trashed(IContentService sender, MoveEventArgs e)
{
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
- var relationType = relationService.GetRelationTypeByAlias(relationTypeAlias);
+ var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
// check that the relation-type exists, if not, then recreate it
if (relationType == null)
@@ -73,7 +78,7 @@ namespace Umbraco.Core.Compose
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
- relationService.Save(relationType);
+ _relationService.Save(relationType);
}
foreach (var item in e.MoveInfoCollection)
@@ -86,34 +91,34 @@ namespace Umbraco.Core.Compose
//before we can create this relation, we need to ensure that the original parent still exists which
//may not be the case if the encompassing transaction also deleted it when this item was moved to the bin
- if (entityService.Exists(originalParentId))
+ if (_entityService.Exists(originalParentId))
{
// Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later
var relation = new Relation(originalParentId, item.Entity.Id, relationType);
- relationService.Save(relation);
+ _relationService.Save(relation);
- auditService.Add(AuditType.Delete,
+ _auditService.Add(AuditType.Delete,
item.Entity.WriterId,
item.Entity.Id,
ObjectTypes.GetName(UmbracoObjectTypes.Document),
- string.Format(textService.Localize(
+ string.Format(_textService.Localize(
"recycleBin/contentTrashed"),
item.Entity.Id, originalParentId));
}
}
}
- private static void MediaService_Trashed(IMediaService sender, MoveEventArgs e, IRelationService relationService, IEntityService entityService, ILocalizedTextService textService, IAuditService auditService)
+ private void MediaService_Trashed(IMediaService sender, MoveEventArgs e)
{
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
- var relationType = relationService.GetRelationTypeByAlias(relationTypeAlias);
+ var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
// check that the relation-type exists, if not, then recreate it
if (relationType == null)
{
var documentObjectType = Constants.ObjectTypes.Document;
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
- relationService.Save(relationType);
+ _relationService.Save(relationType);
}
foreach (var item in e.MoveInfoCollection)
{
@@ -123,16 +128,16 @@ namespace Umbraco.Core.Compose
: Constants.System.Root;
//before we can create this relation, we need to ensure that the original parent still exists which
//may not be the case if the encompassing transaction also deleted it when this item was moved to the bin
- if (entityService.Exists(originalParentId))
+ if (_entityService.Exists(originalParentId))
{
// Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later
var relation = new Relation(originalParentId, item.Entity.Id, relationType);
- relationService.Save(relation);
- auditService.Add(AuditType.Delete,
+ _relationService.Save(relation);
+ _auditService.Add(AuditType.Delete,
item.Entity.CreatorId,
item.Entity.Id,
ObjectTypes.GetName(UmbracoObjectTypes.Media),
- string.Format(textService.Localize(
+ string.Format(_textService.Localize(
"recycleBin/mediaTrashed"),
item.Entity.Id, originalParentId));
}
diff --git a/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs b/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs
index 88ae80bf8b..b2ded07034 100644
--- a/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs
+++ b/src/Umbraco.Infrastructure/CompositionExtensions_Essentials.cs
@@ -31,6 +31,7 @@ namespace Umbraco.Core
TypeLoader typeLoader,
IRuntimeState state,
ITypeFinder typeFinder,
+
IIOHelper ioHelper,
IUmbracoVersion umbracoVersion,
IDbProviderFactoryCreator dbProviderFactoryCreator,
diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
index e19fac4028..22ad84df74 100644
--- a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
+++ b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
@@ -22,29 +22,20 @@ namespace Umbraco.Web.Install.InstallSteps
private readonly IRuntimeState _runtime;
private readonly ILogger _logger;
private readonly IUmbracoVersion _umbracoVersion;
- private readonly GlobalSettings _globalSettings;
private readonly ConnectionStrings _connectionStrings;
- private readonly IIOHelper _ioHelper;
- private readonly IConfigManipulator _configManipulator;
public DatabaseUpgradeStep(
DatabaseBuilder databaseBuilder,
IRuntimeState runtime,
ILogger logger,
IUmbracoVersion umbracoVersion,
- IOptions globalSettings,
- IOptions connectionStrings,
- IIOHelper ioHelper,
- IConfigManipulator configManipulator)
+ IOptions connectionStrings)
{
_databaseBuilder = databaseBuilder;
_runtime = runtime;
_logger = logger;
_umbracoVersion = umbracoVersion;
- _globalSettings = globalSettings.Value;
_connectionStrings = connectionStrings.Value ?? throw new ArgumentNullException(nameof(connectionStrings));
- _ioHelper = ioHelper;
- _configManipulator = configManipulator;
}
public override Task ExecuteAsync(object model)
@@ -57,7 +48,7 @@ namespace Umbraco.Web.Install.InstallSteps
{
_logger.Info("Running 'Upgrade' service");
- var plan = new UmbracoPlan(_umbracoVersion, _globalSettings);
+ var plan = new UmbracoPlan(_umbracoVersion);
plan.AddPostMigration(); // needed when running installer (back-office)
var result = _databaseBuilder.UpgradeSchemaAndData(plan);
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
index c272ebc15b..678860809a 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
@@ -23,7 +23,6 @@ namespace Umbraco.Core.Migrations.Install
{
private readonly IUmbracoDatabaseFactory _databaseFactory;
private readonly IScopeProvider _scopeProvider;
- private readonly GlobalSettings _globalSettings;
private readonly IRuntimeState _runtime;
private readonly IMigrationBuilder _migrationBuilder;
private readonly IKeyValueService _keyValueService;
@@ -40,7 +39,6 @@ namespace Umbraco.Core.Migrations.Install
///
public DatabaseBuilder(
IScopeProvider scopeProvider,
- IOptions globalSettings,
IUmbracoDatabaseFactory databaseFactory,
IRuntimeState runtime,
ILogger logger,
@@ -52,7 +50,6 @@ namespace Umbraco.Core.Migrations.Install
IConfigManipulator configManipulator)
{
_scopeProvider = scopeProvider;
- _globalSettings = globalSettings.Value;
_databaseFactory = databaseFactory;
_runtime = runtime;
_logger = logger;
@@ -321,7 +318,7 @@ namespace Umbraco.Core.Migrations.Install
return _databaseSchemaValidationResult;
var database = scope.Database;
- var dbSchema = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, _globalSettings);
+ var dbSchema = new DatabaseSchemaCreator(database, _logger, _umbracoVersion);
_databaseSchemaValidationResult = dbSchema.ValidateSchema();
return _databaseSchemaValidationResult;
}
@@ -369,7 +366,7 @@ namespace Umbraco.Core.Migrations.Install
if (_runtime.Level == RuntimeLevel.Run)
throw new Exception("Umbraco is already configured!");
- var creator = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, _globalSettings);
+ var creator = new DatabaseSchemaCreator(database, _logger, _umbracoVersion);
creator.InitializeDatabaseSchema();
message = message + "Installation completed!
";
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
index 6f0f32c37d..866f5230b0 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs
@@ -17,14 +17,12 @@ namespace Umbraco.Core.Migrations.Install
private readonly IDatabase _database;
private readonly ILogger _logger;
private readonly IUmbracoVersion _umbracoVersion;
- private readonly GlobalSettings _globalSettings;
- public DatabaseDataCreator(IDatabase database, ILogger logger, IUmbracoVersion umbracoVersion, GlobalSettings globalSettings)
+ public DatabaseDataCreator(IDatabase database, ILogger logger, IUmbracoVersion umbracoVersion)
{
_database = database;
_logger = logger;
_umbracoVersion = umbracoVersion;
- _globalSettings = globalSettings;
}
///
@@ -341,7 +339,7 @@ namespace Umbraco.Core.Migrations.Install
{
// on install, initialize the umbraco migration plan with the final state
- var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion, _globalSettings));
+ var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion));
var stateValueKey = upgrader.StateValueKey;
var finalState = upgrader.Plan.FinalState;
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs
index 97bd8bc2d6..bf30a520ca 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs
@@ -21,14 +21,12 @@ namespace Umbraco.Core.Migrations.Install
private readonly IUmbracoDatabase _database;
private readonly ILogger _logger;
private readonly IUmbracoVersion _umbracoVersion;
- private readonly GlobalSettings _globalSettings;
- public DatabaseSchemaCreator(IUmbracoDatabase database, ILogger logger, IUmbracoVersion umbracoVersion, GlobalSettings globalSettings)
+ public DatabaseSchemaCreator(IUmbracoDatabase database, ILogger logger, IUmbracoVersion umbracoVersion)
{
_database = database;
_logger = logger;
_umbracoVersion = umbracoVersion;
- _globalSettings = globalSettings;
}
private ISqlSyntaxProvider SqlSyntax => _database.SqlContext.SqlSyntax;
@@ -131,7 +129,7 @@ namespace Umbraco.Core.Migrations.Install
if (e.Cancel == false)
{
- var dataCreation = new DatabaseDataCreator(_database, _logger, _umbracoVersion, _globalSettings);
+ var dataCreation = new DatabaseDataCreator(_database, _logger, _umbracoVersion);
foreach (var table in OrderedTables)
CreateTable(false, table, dataCreation);
}
@@ -401,7 +399,7 @@ namespace Umbraco.Core.Migrations.Install
where T : new()
{
var tableType = typeof(T);
- CreateTable(overwrite, tableType, new DatabaseDataCreator(_database, _logger, _umbracoVersion, _globalSettings));
+ CreateTable(overwrite, tableType, new DatabaseDataCreator(_database, _logger, _umbracoVersion));
}
///
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
index 3b555c89b1..7f8c47f92f 100644
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
@@ -17,17 +17,15 @@ namespace Umbraco.Core.Migrations.Upgrade
public class UmbracoPlan : MigrationPlan
{
private readonly IUmbracoVersion _umbracoVersion;
- private readonly GlobalSettings _globalSettings;
private const string InitPrefix = "{init-";
private const string InitSuffix = "}";
///
/// Initializes a new instance of the class.
///
- public UmbracoPlan(IUmbracoVersion umbracoVersion, GlobalSettings globalSettings)
+ public UmbracoPlan(IUmbracoVersion umbracoVersion)
: base(Constants.System.UmbracoUpgradePlanName)
{
_umbracoVersion = umbracoVersion;
- _globalSettings = globalSettings;
DefinePlan();
}
@@ -192,7 +190,7 @@ namespace Umbraco.Core.Migrations.Upgrade
// to 8.7.0...
To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}");
-
+
//FINAL
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs
index 61aabdc1b5..868e088d93 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs
@@ -54,11 +54,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override bool PerformExists(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id) != null;
- protected override IEnumerable PerformGetAll(params int[] ids)
+ protected override IEnumerable GetAllWithFullCachePolicy()
{
- // the cache policy will always want everything
- // even GetMany(ids) gets everything and filters afterwards
- if (ids.Any()) throw new PanicException("There can be no ids specified");
return CommonRepository.GetAllTypes().OfType();
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index b2a83e83fd..50ba0a698b 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -90,6 +90,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return moveInfo;
}
+ protected override IEnumerable PerformGetAll(params int[] ids)
+ {
+ var result = GetAllWithFullCachePolicy();
+
+ // By default the cache policy will always want everything
+ // even GetMany(ids) gets everything and filters afterwards,
+ // however if we are using No Cache, we must still be able to support
+ // collections of Ids, so this is to work around that:
+ if (ids.Any())
+ {
+ return result.Where(x => ids.Contains(x.Id));
+ }
+
+ return result;
+ }
+
+ protected abstract IEnumerable GetAllWithFullCachePolicy();
+
protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, ValueStorageType storageType, string propertyTypeAlias)
{
return new PropertyType(_shortStringHelper, propertyEditorAlias, storageType, propertyTypeAlias);
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs
index 6b6ab37001..80a83ce909 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override ILanguage PerformGet(int id)
{
- throw new NotSupportedException(); // not required since policy is full dataset
+ return PerformGetAll(id).FirstOrDefault();
}
protected override IEnumerable PerformGetAll(params int[] ids)
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs
index c6caa40750..818b5c9f21 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs
@@ -48,11 +48,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IMediaType PerformGet(string alias)
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
- protected override IEnumerable PerformGetAll(params int[] ids)
+ protected override IEnumerable GetAllWithFullCachePolicy()
{
- // the cache policy will always want everything
- // even GetMany(ids) gets everything and filters afterwards
- if (ids.Any()) throw new PanicException("There can be no ids specified");
return CommonRepository.GetAllTypes().OfType();
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs
index b9c40eebc9..059035a9bc 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs
@@ -59,11 +59,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IMemberType PerformGet(string alias)
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
- protected override IEnumerable PerformGetAll(params int[] ids)
+ protected override IEnumerable GetAllWithFullCachePolicy()
{
- // the cache policy will always want everything
- // even GetMany(ids) gets everything and filters afterwards
- if (ids.Any()) throw new PanicException("There can be no ids specified");
return CommonRepository.GetAllTypes().OfType();
}
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs
index de071d1a1e..cd7b7a1f39 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs
@@ -1,7 +1,11 @@
-using System.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using Umbraco.Core.Composing;
+using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Web.PropertyEditors
@@ -9,6 +13,7 @@ namespace Umbraco.Web.PropertyEditors
public sealed class PropertyEditorsComponent : IComponent
{
private readonly PropertyEditorCollection _propertyEditors;
+ private readonly List _terminate = new List();
public PropertyEditorsComponent(PropertyEditorCollection propertyEditors)
{
@@ -27,32 +32,48 @@ namespace Umbraco.Web.PropertyEditors
}
public void Terminate()
- { }
-
- private static void Initialize(FileUploadPropertyEditor fileUpload)
{
- MediaService.Saving += fileUpload.MediaServiceSaving;
- ContentService.Copied += fileUpload.ContentServiceCopied;
-
- MediaService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
- ContentService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
- MemberService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
+ foreach (var t in _terminate) t();
}
- private static void Initialize(ImageCropperPropertyEditor imageCropper)
+ private void Initialize(FileUploadPropertyEditor fileUpload)
+ {
+ MediaService.Saving += fileUpload.MediaServiceSaving;
+ _terminate.Add(() => MediaService.Saving -= fileUpload.MediaServiceSaving);
+ ContentService.Copied += fileUpload.ContentServiceCopied;
+ _terminate.Add(() => ContentService.Copied -= fileUpload.ContentServiceCopied);
+
+ void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
+ MediaService.Deleted += mediaServiceDeleted;
+ _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted);
+
+ void contentServiceDeleted(IContentService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
+ ContentService.Deleted += contentServiceDeleted;
+ _terminate.Add(() => ContentService.Deleted -= contentServiceDeleted);
+
+ void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast()));
+ MemberService.Deleted += memberServiceDeleted;
+ _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
+ }
+
+ private void Initialize(ImageCropperPropertyEditor imageCropper)
{
MediaService.Saving += imageCropper.MediaServiceSaving;
+ _terminate.Add(() => MediaService.Saving -= imageCropper.MediaServiceSaving);
ContentService.Copied += imageCropper.ContentServiceCopied;
+ _terminate.Add(() => ContentService.Copied -= imageCropper.ContentServiceCopied);
- MediaService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
- ContentService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
- MemberService.Deleted += (sender, args)
- => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
+ void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
+ MediaService.Deleted += mediaServiceDeleted;
+ _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted);
+
+ void contentServiceDeleted(IContentService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
+ ContentService.Deleted += contentServiceDeleted;
+ _terminate.Add(() => ContentService.Deleted -= contentServiceDeleted);
+
+ void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast()));
+ MemberService.Deleted += memberServiceDeleted;
+ _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
}
}
}
diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs
index cd623b585a..0273b3d14a 100644
--- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs
+++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs
@@ -58,7 +58,12 @@ namespace Umbraco.Web.Routing
}
public void Terminate()
- { }
+ {
+ ContentService.Publishing -= ContentService_Publishing;
+ ContentService.Published -= ContentService_Published;
+ ContentService.Moving -= ContentService_Moving;
+ ContentService.Moved -= ContentService_Moved;
+ }
private void ContentService_Publishing(IContentService sender, PublishEventArgs args)
{
diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
index 9e1910d938..7d02152a57 100644
--- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
+++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
@@ -7,6 +7,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
@@ -25,12 +26,12 @@ namespace Umbraco.Core.Runtime
{
private ComponentCollection _components;
private IFactory _factory;
- private readonly RuntimeState _state;
+ // runtime state, this instance will get replaced again once the essential services are available to run the check
+ private RuntimeState _state = RuntimeState.Booting();
private readonly IUmbracoBootPermissionChecker _umbracoBootPermissionChecker;
- private readonly IRequestCache _requestCache;
+ private readonly Configs _configs;
private readonly GlobalSettings _globalSettings;
private readonly ConnectionStrings _connectionStrings;
- private readonly Configs _configs;
public CoreRuntime(
Configs configs, // TODO: remove this parameter with legacy configuraiton no longer needed in Umbraco.Web and Umbraco.Tests
@@ -46,13 +47,14 @@ namespace Umbraco.Core.Runtime
IDbProviderFactoryCreator dbProviderFactoryCreator,
IMainDom mainDom,
ITypeFinder typeFinder,
- IRequestCache requestCache)
+ AppCaches appCaches)
{
_configs = configs;
_globalSettings = globalSettings;
_connectionStrings = connectionStrings;
IOHelper = ioHelper;
+ AppCaches = appCaches;
UmbracoVersion = umbracoVersion;
Profiler = profiler;
HostingEnvironment = hostingEnvironment;
@@ -60,19 +62,14 @@ namespace Umbraco.Core.Runtime
DbProviderFactoryCreator = dbProviderFactoryCreator;
_umbracoBootPermissionChecker = umbracoBootPermissionChecker;
- _requestCache = requestCache;
Logger = logger;
MainDom = mainDom;
TypeFinder = typeFinder;
- // runtime state
- // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance'
- // as the second one captures the current value (null) and therefore fails
- _state = new RuntimeState(_globalSettings, UmbracoVersion)
- {
- Level = RuntimeLevel.Boot
- };
+ _globalSettings = globalSettings;
+ _connectionStrings = connectionStrings;
+
}
///
@@ -92,7 +89,7 @@ namespace Umbraco.Core.Runtime
///
/// Gets the profiling logger.
///
- protected IProfilingLogger ProfilingLogger { get; private set; }
+ public IProfilingLogger ProfilingLogger { get; private set; }
///
/// Gets the
@@ -105,7 +102,8 @@ namespace Umbraco.Core.Runtime
protected IIOHelper IOHelper { get; }
protected IHostingEnvironment HostingEnvironment { get; }
- protected IUmbracoVersion UmbracoVersion { get; }
+ public AppCaches AppCaches { get; }
+ public IUmbracoVersion UmbracoVersion { get; }
///
public IRuntimeState State => _state;
@@ -168,31 +166,33 @@ namespace Umbraco.Core.Runtime
try
{
-
-
// run handlers
- RuntimeOptions.DoRuntimeBoot(ProfilingLogger);
-
- // application caches
- var appCaches = GetAppCaches();
+ OnRuntimeBoot();
// database factory
- var databaseFactory = GetDatabaseFactory();
+ var databaseFactory = CreateDatabaseFactory();
// type finder/loader
- var typeLoader = new TypeLoader(TypeFinder, appCaches.RuntimeCache, new DirectoryInfo(HostingEnvironment.LocalTempPath), ProfilingLogger);
+ var typeLoader = new TypeLoader(TypeFinder, AppCaches.RuntimeCache, new DirectoryInfo(HostingEnvironment.LocalTempPath), ProfilingLogger);
+
+ // re-create the state object with the essential services
+ _state = new RuntimeState(_globalSettings, UmbracoVersion, databaseFactory, Logger);
// create the composition
- composition = new Composition(register, typeLoader, ProfilingLogger, _state, _configs, IOHelper, appCaches);
- composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, appCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper, UmbracoVersion, DbProviderFactoryCreator, HostingEnvironment, BackOfficeInfo);
+ composition = new Composition(register, typeLoader, ProfilingLogger, _state, _configs, IOHelper, AppCaches);
+
+ composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, AppCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper, UmbracoVersion, DbProviderFactoryCreator, HostingEnvironment, BackOfficeInfo);
// register ourselves (TODO: Should we put this in RegisterEssentials?)
composition.Register(_ => this, Lifetime.Singleton);
+ // run handlers
+ OnRuntimeEssentials(composition, AppCaches, typeLoader, databaseFactory);
+
try
{
// determine our runtime level
- DetermineRuntimeLevel(databaseFactory, ProfilingLogger);
+ DetermineRuntimeLevel(databaseFactory);
}
finally
{
@@ -247,9 +247,6 @@ namespace Umbraco.Core.Runtime
// throws if not full-trust
_umbracoBootPermissionChecker.ThrowIfNotPermissions();
- // run handlers
- RuntimeOptions.DoRuntimeEssentials(_factory);
-
var hostingEnvironmentLifetime = _factory.TryGetInstance();
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)}");
@@ -261,7 +258,6 @@ namespace Umbraco.Core.Runtime
_components = _factory.GetInstance();
_components.Initialize();
-
// now (and only now) is the time to switch over to perWebRequest scopes.
// up until that point we may not have a request, and scoped services would
// fail to resolve - but we run Initialize within a factory scope - and then,
@@ -317,31 +313,29 @@ namespace Umbraco.Core.Runtime
}
}
- // internal for tests
- internal void DetermineRuntimeLevel(IUmbracoDatabaseFactory databaseFactory, IProfilingLogger profilingLogger)
+ private void DetermineRuntimeLevel(IUmbracoDatabaseFactory databaseFactory)
{
- using (var timer = profilingLogger.DebugDuration("Determining runtime level.", "Determined."))
+ using var timer = ProfilingLogger.DebugDuration("Determining runtime level.", "Determined.");
+
+ try
{
- try
- {
- _state.DetermineRuntimeLevel(databaseFactory, profilingLogger);
+ _state.DetermineRuntimeLevel();
- profilingLogger.Debug("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", _state.Level, _state.Reason);
+ ProfilingLogger.Debug("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", _state.Level, _state.Reason);
- if (_state.Level == RuntimeLevel.Upgrade)
- {
- profilingLogger.Debug("Configure database factory for upgrades.");
- databaseFactory.ConfigureForUpgrade();
- }
- }
- catch
+ if (_state.Level == RuntimeLevel.Upgrade)
{
- _state.Level = RuntimeLevel.BootFailed;
- _state.Reason = RuntimeLevelReason.BootFailedOnException;
- timer?.Fail();
- throw;
+ ProfilingLogger.Debug("Configure database factory for upgrades.");
+ databaseFactory.ConfigureForUpgrade();
}
}
+ catch
+ {
+ _state.Level = RuntimeLevel.BootFailed;
+ _state.Reason = RuntimeLevelReason.BootFailedOnException;
+ timer?.Fail();
+ throw;
+ }
}
private IEnumerable ResolveComposerTypes(TypeLoader typeLoader)
@@ -377,21 +371,6 @@ namespace Umbraco.Core.Runtime
protected virtual IEnumerable GetComposerTypes(TypeLoader typeLoader)
=> typeLoader.GetTypes();
- ///
- /// Gets the application caches.
- ///
- protected virtual AppCaches GetAppCaches()
- {
- // need the deep clone runtime cache provider to ensure entities are cached properly, ie
- // are cloned in and cloned out - no request-based cache here since no web-based context,
- // is overridden by the web runtime
-
- return new AppCaches(
- new DeepCloneAppCache(new ObjectCacheAppCache()),
- _requestCache,
- new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
- }
-
///
/// Returns the application path of the site/solution
///
@@ -404,14 +383,33 @@ namespace Umbraco.Core.Runtime
=> null;
///
- /// Gets the database factory.
+ /// Creates the database factory.
///
/// This is strictly internal, for tests only.
- protected internal virtual IUmbracoDatabaseFactory GetDatabaseFactory()
+ protected internal virtual IUmbracoDatabaseFactory CreateDatabaseFactory()
=> new UmbracoDatabaseFactory(Logger, Options.Create(_globalSettings), Options.Create(_connectionStrings), new Lazy(() => _factory.GetInstance()), DbProviderFactoryCreator);
#endregion
+ #region Events
+
+ protected void OnRuntimeBoot()
+ {
+ RuntimeOptions.DoRuntimeBoot(ProfilingLogger);
+ RuntimeBooting?.Invoke(this, ProfilingLogger);
+ }
+
+ protected void OnRuntimeEssentials(Composition composition, AppCaches appCaches, TypeLoader typeLoader, IUmbracoDatabaseFactory databaseFactory)
+ {
+ RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory);
+ RuntimeEssentials?.Invoke(this, new RuntimeEssentialsEventArgs(composition, appCaches, typeLoader, databaseFactory));
+ }
+
+ public event TypedEventHandler RuntimeBooting;
+ public event TypedEventHandler RuntimeEssentials;
+
+ #endregion
+
}
}
diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeEssentialsEventArgs.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeEssentialsEventArgs.cs
new file mode 100644
index 0000000000..78d068cb9c
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Runtime/RuntimeEssentialsEventArgs.cs
@@ -0,0 +1,23 @@
+using System;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Persistence;
+
+namespace Umbraco.Core.Runtime
+{
+ public class RuntimeEssentialsEventArgs : EventArgs
+ {
+ public RuntimeEssentialsEventArgs(Composition composition, AppCaches appCaches, TypeLoader typeLoader, IUmbracoDatabaseFactory databaseFactory)
+ {
+ Composition = composition;
+ AppCaches = appCaches;
+ TypeLoader = typeLoader;
+ DatabaseFactory = databaseFactory;
+ }
+
+ public Composition Composition { get; }
+ public AppCaches AppCaches { get; }
+ public TypeLoader TypeLoader { get; }
+ public IUmbracoDatabaseFactory DatabaseFactory { get; }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/RuntimeOptions.cs b/src/Umbraco.Infrastructure/RuntimeOptions.cs
index 562a7e3c5c..23abd474a4 100644
--- a/src/Umbraco.Infrastructure/RuntimeOptions.cs
+++ b/src/Umbraco.Infrastructure/RuntimeOptions.cs
@@ -16,7 +16,7 @@ namespace Umbraco.Core
public static class RuntimeOptions
{
private static List> _onBoot;
- private static List> _onEssentials;
+ private static List> _onEssentials;
///
/// Executes the RuntimeBoot handlers.
@@ -33,13 +33,13 @@ namespace Umbraco.Core
///
/// Executes the RuntimeEssentials handlers.
///
- internal static void DoRuntimeEssentials(IFactory factory)
+ internal static void DoRuntimeEssentials(Composition composition, AppCaches appCaches, TypeLoader typeLoader, IUmbracoDatabaseFactory databaseFactory)
{
if (_onEssentials== null)
return;
foreach (var action in _onEssentials)
- action(factory);
+ action(composition, appCaches, typeLoader, databaseFactory);
}
///
@@ -64,10 +64,10 @@ namespace Umbraco.Core
/// essential things (AppCaches, a TypeLoader, and a database factory) but
/// before anything else.
///
- public static void OnRuntimeEssentials(Action action)
+ public static void OnRuntimeEssentials(Action action)
{
if (_onEssentials == null)
- _onEssentials = new List>();
+ _onEssentials = new List>();
_onEssentials.Add(action);
}
}
diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs
index 832cdc7605..4adf9fdf7c 100644
--- a/src/Umbraco.Infrastructure/RuntimeState.cs
+++ b/src/Umbraco.Infrastructure/RuntimeState.cs
@@ -1,11 +1,9 @@
using System;
using System.Threading;
using Semver;
-using Umbraco.Core.Collections;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Exceptions;
-using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.Upgrade;
using Umbraco.Core.Persistence;
@@ -19,14 +17,27 @@ namespace Umbraco.Core
{
private readonly GlobalSettings _globalSettings;
private readonly IUmbracoVersion _umbracoVersion;
+ private readonly IUmbracoDatabaseFactory _databaseFactory;
+ private readonly ILogger _logger;
+
+ ///
+ /// The initial
+ ///
+ public static RuntimeState Booting() => new RuntimeState() { Level = RuntimeLevel.Boot };
+
+ private RuntimeState()
+ {
+ }
///
/// Initializes a new instance of the class.
///
- public RuntimeState(GlobalSettings globalSettings, IUmbracoVersion umbracoVersion)
+ public RuntimeState(GlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IUmbracoDatabaseFactory databaseFactory, ILogger logger)
{
_globalSettings = globalSettings;
_umbracoVersion = umbracoVersion;
+ _databaseFactory = databaseFactory;
+ _logger = logger;
}
@@ -54,16 +65,14 @@ namespace Umbraco.Core
///
public BootFailedException BootFailedException { get; internal set; }
- ///
- /// Determines the runtime level.
- ///
- public void DetermineRuntimeLevel(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
+ ///
+ public void DetermineRuntimeLevel()
{
- if (databaseFactory.Configured == false)
+ if (_databaseFactory.Configured == false)
{
// local version *does* match code version, but the database is not configured
// install - may happen with Deploy/Cloud/etc
- logger.Debug("Database is not configured, need to install Umbraco.");
+ _logger.Debug("Database is not configured, need to install Umbraco.");
Level = RuntimeLevel.Install;
Reason = RuntimeLevelReason.InstallNoDatabase;
return;
@@ -76,16 +85,16 @@ namespace Umbraco.Core
var tries = _globalSettings.InstallMissingDatabase ? 2 : 5;
for (var i = 0;;)
{
- connect = databaseFactory.CanConnect;
+ connect = _databaseFactory.CanConnect;
if (connect || ++i == tries) break;
- logger.Debug("Could not immediately connect to database, trying again.");
+ _logger.Debug("Could not immediately connect to database, trying again.");
Thread.Sleep(1000);
}
if (connect == false)
{
// cannot connect to configured database, this is bad, fail
- logger.Debug("Could not connect to database.");
+ _logger.Debug("Could not connect to database.");
if (_globalSettings.InstallMissingDatabase)
{
@@ -109,12 +118,12 @@ namespace Umbraco.Core
bool noUpgrade;
try
{
- noUpgrade = EnsureUmbracoUpgradeState(databaseFactory, logger);
+ noUpgrade = EnsureUmbracoUpgradeState(_databaseFactory, _logger);
}
catch (Exception e)
{
// can connect to the database but cannot check the upgrade state... oops
- logger.Warn(e, "Could not check the upgrade state.");
+ _logger.Warn(e, "Could not check the upgrade state.");
if (_globalSettings.InstallEmptyDatabase)
{
@@ -146,14 +155,14 @@ namespace Umbraco.Core
// although the files version matches the code version, the database version does not
// which means the local files have been upgraded but not the database - need to upgrade
- logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco.");
+ _logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco.");
Level = RuntimeLevel.Upgrade;
Reason = RuntimeLevelReason.UpgradeMigrations;
}
private bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
{
- var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion, _globalSettings));
+ var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion));
var stateValueKey = upgrader.StateValueKey;
// no scope, no service - just directly accessing the database
diff --git a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs
index 8aabaf0b57..cbf1df197d 100644
--- a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs
+++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs
@@ -18,7 +18,7 @@ using Umbraco.Web.Routing;
namespace Umbraco.Web.Scheduling
{
- internal sealed class SchedulerComponent : IComponent
+ public sealed class SchedulerComponent : IComponent
{
private const int DefaultDelayMilliseconds = 180000; // 3 mins
private const int OneMinuteMilliseconds = 60000;
diff --git a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs
index 1946e2041b..d18480eb82 100644
--- a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs
+++ b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Search
///
/// Utility to rebuild all indexes on a background thread
///
- public sealed class BackgroundIndexRebuilder
+ public class BackgroundIndexRebuilder
{
private static readonly object RebuildLocker = new object();
private readonly IndexRebuilder _indexRebuilder;
@@ -34,7 +34,7 @@ namespace Umbraco.Web.Search
///
///
///
- public void RebuildIndexes(bool onlyEmptyIndexes, int waitMilliseconds = 0)
+ public virtual void RebuildIndexes(bool onlyEmptyIndexes, int waitMilliseconds = 0)
{
// TODO: need a way to disable rebuilding on startup
diff --git a/src/Umbraco.Infrastructure/Search/ExamineComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineComponent.cs
index 35a8804ecb..ae5bfd628b 100644
--- a/src/Umbraco.Infrastructure/Search/ExamineComponent.cs
+++ b/src/Umbraco.Infrastructure/Search/ExamineComponent.cs
@@ -24,7 +24,6 @@ namespace Umbraco.Web.Search
private readonly IValueSetBuilder _mediaValueSetBuilder;
private readonly IValueSetBuilder _memberValueSetBuilder;
private readonly BackgroundIndexRebuilder _backgroundIndexRebuilder;
- private static object _isConfiguredLocker = new object();
private readonly IScopeProvider _scopeProvider;
private readonly ServiceContext _services;
private readonly IMainDom _mainDom;
@@ -104,7 +103,13 @@ namespace Umbraco.Web.Search
}
public void Terminate()
- { }
+ {
+ ContentCacheRefresher.CacheUpdated -= ContentCacheRefresherUpdated;
+ ContentTypeCacheRefresher.CacheUpdated -= ContentTypeCacheRefresherUpdated;
+ MediaCacheRefresher.CacheUpdated -= MediaCacheRefresherUpdated;
+ MemberCacheRefresher.CacheUpdated -= MemberCacheRefresherUpdated;
+ LanguageCacheRefresher.CacheUpdated -= LanguageCacheRefresherUpdated;
+ }
#region Cache refresher updated event handlers
diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs
index 32cfd3057e..30754d3a7f 100644
--- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs
+++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs
@@ -53,32 +53,38 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose
}
public void Terminate()
- { }
+ {
+ ServerVariablesParser.Parsing -= ServerVariablesParser_Parsing;
+ ContentModelBinder.ModelBindingException -= ContentModelBinder_ModelBindingException;
+ FileService.SavingTemplate -= FileService_SavingTemplate;
+ }
private void InstallServerVars()
{
// register our url - for the backoffice api
- ServerVariablesParser.Parsing += (sender, serverVars) =>
- {
- if (!serverVars.ContainsKey("umbracoUrls"))
- throw new ArgumentException("Missing umbracoUrls.");
- var umbracoUrlsObject = serverVars["umbracoUrls"];
- if (umbracoUrlsObject == null)
- throw new ArgumentException("Null umbracoUrls");
- if (!(umbracoUrlsObject is Dictionary umbracoUrls))
- throw new ArgumentException("Invalid umbracoUrls");
+ ServerVariablesParser.Parsing += ServerVariablesParser_Parsing;
+ }
- if (!serverVars.ContainsKey("umbracoPlugins"))
- throw new ArgumentException("Missing umbracoPlugins.");
- if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins))
- throw new ArgumentException("Invalid umbracoPlugins");
+ private void ServerVariablesParser_Parsing(object sender, Dictionary serverVars)
+ {
+ if (!serverVars.ContainsKey("umbracoUrls"))
+ throw new ArgumentException("Missing umbracoUrls.");
+ var umbracoUrlsObject = serverVars["umbracoUrls"];
+ if (umbracoUrlsObject == null)
+ throw new ArgumentException("Null umbracoUrls");
+ if (!(umbracoUrlsObject is Dictionary umbracoUrls))
+ throw new ArgumentException("Invalid umbracoUrls");
- if (HttpContext.Current == null) throw new InvalidOperationException("HttpContext is null");
- var urlHelper = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()));
+ if (!serverVars.ContainsKey("umbracoPlugins"))
+ throw new ArgumentException("Missing umbracoPlugins.");
+ if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins))
+ throw new ArgumentException("Invalid umbracoPlugins");
- umbracoUrls["modelsBuilderBaseUrl"] = urlHelper.GetUmbracoApiServiceBaseUrl(controller => controller.BuildModels());
- umbracoPlugins["modelsBuilder"] = GetModelsBuilderSettings();
- };
+ if (HttpContext.Current == null) throw new InvalidOperationException("HttpContext is null");
+ var urlHelper = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()));
+
+ umbracoUrls["modelsBuilderBaseUrl"] = urlHelper.GetUmbracoApiServiceBaseUrl(controller => controller.BuildModels());
+ umbracoPlugins["modelsBuilder"] = GetModelsBuilderSettings();
}
private Dictionary GetModelsBuilderSettings()
diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs
index df5f67a206..803b6c0cae 100644
--- a/src/Umbraco.Tests.Common/TestHelperBase.cs
+++ b/src/Umbraco.Tests.Common/TestHelperBase.cs
@@ -48,14 +48,6 @@ namespace Umbraco.Tests.Common
public Configs GetConfigs() => GetConfigsFactory().Create();
- public IRuntimeState GetRuntimeState()
- {
- var globalSettings = new GlobalSettingsBuilder().Build();
- return new RuntimeState(
- globalSettings,
- GetUmbracoVersion());
- }
-
public abstract IBackOfficeInfo GetBackOfficeInfo();
public IConfigsFactory GetConfigsFactory() => new ConfigsFactory();
diff --git a/src/Umbraco.Tests.Integration/ContainerTests.cs b/src/Umbraco.Tests.Integration/ContainerTests.cs
index 2098b7241e..3ba3c31d03 100644
--- a/src/Umbraco.Tests.Integration/ContainerTests.cs
+++ b/src/Umbraco.Tests.Integration/ContainerTests.cs
@@ -83,7 +83,7 @@ namespace Umbraco.Tests.Integration
// it means the container won't be disposed, and maybe other services? not sure.
// In cases where we use it can we use IConfigureOptions? https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/
- var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
+ var umbracoContainer = UmbracoIntegrationTest.CreateUmbracoContainer(out var serviceProviderFactory);
IHostApplicationLifetime lifetime1 = null;
diff --git a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs
deleted file mode 100644
index 96ac3d603c..0000000000
--- a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System;
-using System.Data.Common;
-using System.Data.SqlClient;
-using System.IO;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Configuration.Models;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Persistence;
-using Umbraco.Tests.Integration.Testing;
-using Umbraco.Tests.Testing;
-
-namespace Umbraco.Tests.Integration.Extensions
-{
- public static class ApplicationBuilderExtensions
- {
- ///
- /// Creates a LocalDb instance to use for the test
- ///
- ///
- ///
- ///
- ///
- ///
- public static IApplicationBuilder UseTestLocalDb(this IApplicationBuilder app,
- string workingDirectory,
- UmbracoIntegrationTest integrationTest, out string connectionString)
- {
- connectionString = null;
- var dbFilePath = Path.Combine(workingDirectory, "LocalDb");
-
- // get the currently set db options
- var testOptions = TestOptionAttributeBase.GetTestOptions();
-
- if (testOptions.Database == UmbracoTestOptions.Database.None)
- return app;
-
- // need to manually register this factory
- DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
-
- if (!Directory.Exists(dbFilePath))
- Directory.CreateDirectory(dbFilePath);
-
- var db = UmbracoIntegrationTest.GetOrCreate(dbFilePath,
- app.ApplicationServices.GetRequiredService(),
- app.ApplicationServices.GetRequiredService>().Value,
- app.ApplicationServices.GetRequiredService());
-
-
- switch (testOptions.Database)
- {
- case UmbracoTestOptions.Database.NewSchemaPerTest:
-
- // New DB + Schema
- var newSchemaDbId = db.AttachSchema();
-
- // Add teardown callback
- integrationTest.OnTestTearDown(() => db.Detach(newSchemaDbId));
-
- // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
- var dbFactory = app.ApplicationServices.GetRequiredService();
- if (!dbFactory.Configured)
- {
- dbFactory.Configure(db.ConnectionString, Constants.DatabaseProviders.SqlServer);
- }
-
- // In the case that we've initialized the schema, it means that we are installed so we'll want to ensure that
- // the runtime state is configured correctly so we'll force update the configuration flag and re-run the
- // runtime state checker.
- // TODO: This wouldn't be required if we don't store the Umbraco version in config
-
- // right now we are an an 'Install' state
- var runtimeState = (RuntimeState)app.ApplicationServices.GetRequiredService();
- Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level);
-
- // dynamically change the config status
- var umbVersion = app.ApplicationServices.GetRequiredService();
- var config = app.ApplicationServices.GetRequiredService();
- config[Constants.Configuration.ConfigGlobalPrefix + "ConfigurationStatus"] = umbVersion.SemanticVersion.ToString();
-
- // re-run the runtime level check
- var profilingLogger = app.ApplicationServices.GetRequiredService();
- runtimeState.DetermineRuntimeLevel(dbFactory, profilingLogger);
-
- Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level);
-
- break;
- case UmbracoTestOptions.Database.NewEmptyPerTest:
-
- var newEmptyDbId = db.AttachEmpty();
-
- // Add teardown callback
- integrationTest.OnTestTearDown(() => db.Detach(newEmptyDbId));
-
-
- break;
- case UmbracoTestOptions.Database.NewSchemaPerFixture:
-
- // New DB + Schema
- var newSchemaFixtureDbId = db.AttachSchema();
-
- // Add teardown callback
- integrationTest.OnFixtureTearDown(() => db.Detach(newSchemaFixtureDbId));
-
- break;
- case UmbracoTestOptions.Database.NewEmptyPerFixture:
-
- throw new NotImplementedException();
-
- //// Add teardown callback
- //integrationTest.OnFixtureTearDown(() => db.Detach());
-
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null);
- }
- connectionString = db.ConnectionString;
- return app;
- }
- }
-
-
-}
diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs
index fc7974f8f3..0e46941904 100644
--- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs
+++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs
@@ -21,6 +21,7 @@ using Umbraco.Tests.Common;
using Umbraco.Tests.Common.Builders;
using Umbraco.Web.Common.AspNetCore;
using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
+using Microsoft.Extensions.FileProviders;
namespace Umbraco.Tests.Integration.Implementations
{
@@ -41,11 +42,17 @@ namespace Umbraco.Tests.Integration.Implementations
_httpContextAccessor = Mock.Of(x => x.HttpContext == httpContext);
_ipResolver = new AspNetCoreIpResolver(_httpContextAccessor);
+ var contentRoot = Assembly.GetExecutingAssembly().GetRootDirectorySafe();
var hostEnvironment = new Mock();
- hostEnvironment.Setup(x => x.ApplicationName).Returns("UmbracoIntegrationTests");
- hostEnvironment.Setup(x => x.ContentRootPath)
- .Returns(() => Assembly.GetExecutingAssembly().GetRootDirectorySafe());
+ // this must be the assembly name for the WebApplicationFactory to work
+ hostEnvironment.Setup(x => x.ApplicationName).Returns(GetType().Assembly.GetName().Name);
+ hostEnvironment.Setup(x => x.ContentRootPath).Returns(() => contentRoot);
+ hostEnvironment.Setup(x => x.ContentRootFileProvider).Returns(() => new PhysicalFileProvider(contentRoot));
hostEnvironment.Setup(x => x.WebRootPath).Returns(() => WorkingDirectory);
+ hostEnvironment.Setup(x => x.WebRootFileProvider).Returns(() => new PhysicalFileProvider(WorkingDirectory));
+ // we also need to expose it as the obsolete interface since netcore's WebApplicationFactory casts it
+ hostEnvironment.As();
+
_hostEnvironment = hostEnvironment.Object;
_hostingLifetime = new AspNetCoreApplicationShutdownRegistry(Mock.Of());
diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs
index 2766f4b044..c081caedc1 100644
--- a/src/Umbraco.Tests.Integration/RuntimeTests.cs
+++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs
@@ -64,7 +64,7 @@ namespace Umbraco.Tests.Integration
var coreRuntime = new CoreRuntime(configs, globalSettings, connectionStrings, testHelper.GetUmbracoVersion(),
testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker,
testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator,
- testHelper.MainDom, testHelper.GetTypeFinder(), NoAppCache.Instance);
+ testHelper.MainDom, testHelper.GetTypeFinder(), AppCaches.NoCache);
// boot it!
var factory = coreRuntime.Configure(umbracoContainer);
@@ -111,7 +111,7 @@ namespace Umbraco.Tests.Integration
[Test]
public async Task AddUmbracoCore()
{
- var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
+ var umbracoContainer = UmbracoIntegrationTest.CreateUmbracoContainer(out var serviceProviderFactory);
var testHelper = new TestHelper();
var hostBuilder = new HostBuilder()
@@ -124,7 +124,7 @@ namespace Umbraco.Tests.Integration
// Add it!
services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
+ services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, AppCaches.NoCache, testHelper.GetLoggingConfiguration(), out _);
});
var host = await hostBuilder.StartAsync();
@@ -151,7 +151,7 @@ namespace Umbraco.Tests.Integration
[Test]
public async Task UseUmbracoCore()
{
- var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
+ var umbracoContainer = UmbracoIntegrationTest.CreateUmbracoContainer(out var serviceProviderFactory);
var testHelper = new TestHelper();
var hostBuilder = new HostBuilder()
@@ -164,7 +164,7 @@ namespace Umbraco.Tests.Integration
// Add it!
services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
+ services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, AppCaches.NoCache, testHelper.GetLoggingConfiguration(), out _);
});
var host = await hostBuilder.StartAsync();
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs
index a9a272ac59..9a1b335c62 100644
--- a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs
+++ b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs
@@ -18,7 +18,6 @@ using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Tests.Integration.TestServerTest.Controllers
{
[TestFixture]
- [Explicit("Need to figure out whats wrong with these tests when executed all in one run.")]
public class ContentControllerTests : UmbracoTestServerTestBase
{
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/UsersControllerTests.cs
index e2d66373ec..c001eb2d92 100644
--- a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/UsersControllerTests.cs
+++ b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/UsersControllerTests.cs
@@ -149,7 +149,7 @@ namespace Umbraco.Tests.Integration.TestServerTest.Controllers
}
[Test]
- public async Task PostUnlockUsers_When_User_Does_Not_Exist_Expect_InvalidOperationException()
+ public async Task PostUnlockUsers_When_User_Does_Not_Exist_Expect_Zero_Users_Message()
{
var userId = 42; // Must not exist
var url = PrepareUrl(x => x.PostUnlockUsers(new []{userId}));
@@ -158,18 +158,15 @@ namespace Umbraco.Tests.Integration.TestServerTest.Controllers
var response = await Client.PostAsync(url, new StringContent(string.Empty));
var body = await response.Content.ReadAsStringAsync();
body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix);
- Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
- var actual = JsonConvert.DeserializeObject(body, new JsonSerializerSettings
+ var actual = JsonConvert.DeserializeObject(body, new JsonSerializerSettings
{
ContractResolver = new IgnoreRequiredAttributesResolver()
});
Assert.Multiple(() =>
{
- var expected = new InvalidOperationException();
- Assert.IsNotNull(actual);
- Assert.AreEqual(expected.GetType(), actual.ExceptionType);
- Assert.AreEqual(expected.Message, actual.ExceptionMessage);
+ Assert.AreEqual($"Unlocked 0 users", actual.Message);
});
}
@@ -230,8 +227,6 @@ namespace Umbraco.Tests.Integration.TestServerTest.Controllers
userService.Save(user);
}
-
-
var url = PrepareUrl(x => x.PostUnlockUsers(users.Select(x=>x.Id).ToArray()));
// Act
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs
new file mode 100644
index 0000000000..08cd49bd81
--- /dev/null
+++ b/src/Umbraco.Tests.Integration/TestServerTest/TestAuthHandler.cs
@@ -0,0 +1,40 @@
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Umbraco.Core;
+using Umbraco.Core.BackOffice;
+using Umbraco.Core.Mapping;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Services;
+using Umbraco.Web.Common.Security;
+
+namespace Umbraco.Tests.Integration.TestServerTest
+{
+ public class TestAuthHandler : AuthenticationHandler
+ {
+ private readonly BackOfficeSignInManager _backOfficeSignInManager;
+
+ private readonly BackOfficeIdentityUser _fakeUser;
+ public TestAuthHandler(IOptionsMonitor options,
+ ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, BackOfficeSignInManager backOfficeSignInManager, IUserService userService, UmbracoMapper umbracoMapper)
+ : base(options, logger, encoder, clock)
+ {
+ _backOfficeSignInManager = backOfficeSignInManager;
+
+ var user = userService.GetUserById(Constants.Security.SuperUserId);
+ _fakeUser = umbracoMapper.Map(user);
+ _fakeUser.SecurityStamp = "Needed";
+ }
+
+ protected override async Task HandleAuthenticateAsync()
+ {
+
+ var principal = await _backOfficeSignInManager.CreateUserPrincipalAsync(_fakeUser);
+ var ticket = new AuthenticationTicket(principal, "Test");
+
+ return AuthenticateResult.Success(ticket);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoBuilderExtensions.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoBuilderExtensions.cs
new file mode 100644
index 0000000000..49c4f224ee
--- /dev/null
+++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoBuilderExtensions.cs
@@ -0,0 +1,57 @@
+using System;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Composing.LightInject;
+using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Runtime;
+using Umbraco.Extensions;
+using Umbraco.Tests.Integration.Implementations;
+using Umbraco.Tests.Integration.Testing;
+using Umbraco.Web.Common.Builder;
+
+namespace Umbraco.Tests.Integration.TestServerTest
+{
+ public static class UmbracoBuilderExtensions
+ {
+ ///
+ /// Uses a test version of Umbraco Core with a test IRuntime
+ ///
+ ///
+ ///
+ public static IUmbracoBuilder WithTestCore(this IUmbracoBuilder builder, TestHelper testHelper, LightInjectContainer container,
+ Action dbInstallEventHandler)
+ {
+ return builder.AddWith(nameof(global::Umbraco.Web.Common.Builder.UmbracoBuilderExtensions.WithCore),
+ () =>
+ {
+ builder.Services.AddUmbracoCore(
+ builder.WebHostEnvironment,
+ container,
+ typeof(UmbracoBuilderExtensions).Assembly,
+ AppCaches.NoCache, // Disable caches in integration tests
+ testHelper.GetLoggingConfiguration(),
+ // TODO: Yep that's extremely ugly
+ (configs, globalSettings, connectionStrings, umbVersion, ioHelper, logger, profiler, hostingEnv, backOfficeInfo, typeFinder, appCaches, dbProviderFactoryCreator) =>
+ {
+ var runtime = UmbracoIntegrationTest.CreateTestRuntime(
+ configs,
+ globalSettings,
+ connectionStrings,
+ umbVersion,
+ ioHelper,
+ logger,
+ profiler,
+ hostingEnv,
+ backOfficeInfo,
+ typeFinder,
+ appCaches,
+ dbProviderFactoryCreator,
+ testHelper.MainDom, // SimpleMainDom
+ dbInstallEventHandler); // DB Installation event handler
+
+ return runtime;
+ },
+ out _);
+ });
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs
index b666cc40f2..08be22c07b 100644
--- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs
+++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs
@@ -2,20 +2,26 @@
using System;
using System.Linq.Expressions;
using System.Net.Http;
-using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using Umbraco.Composing;
+using Umbraco.Core;
using Umbraco.Extensions;
using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.Testing;
using Umbraco.Web;
+using Umbraco.Web.Common.Builder;
using Umbraco.Web.Common.Controllers;
using Umbraco.Web.Editors;
-
+using Microsoft.Extensions.Hosting;
namespace Umbraco.Tests.Integration.TestServerTest
{
@@ -24,19 +30,55 @@ namespace Umbraco.Tests.Integration.TestServerTest
public abstract class UmbracoTestServerTestBase : UmbracoIntegrationTest
{
[SetUp]
- public void SetUp()
+ public override Task Setup()
{
- Factory = new UmbracoWebApplicationFactory(TestDBConnectionString);
- Client = Factory.CreateClient(new WebApplicationFactoryClientOptions(){
+ InMemoryConfiguration["ConnectionStrings:" + Constants.System.UmbracoConnectionName] = null;
+ InMemoryConfiguration["Umbraco:CMS:Hosting:Debug"] = "true";
+
+ // create new WebApplicationFactory specifying 'this' as the IStartup instance
+ var factory = new UmbracoWebApplicationFactory(CreateHostBuilder);
+
+ // additional host configuration for web server integration tests
+ Factory = factory.WithWebHostBuilder(builder =>
+ {
+ // Executes after the standard ConfigureServices method
+ builder.ConfigureTestServices(services =>
+ {
+ services.AddAuthentication("Test").AddScheme("Test", options => { });
+ });
+ });
+
+ Client = Factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
AllowAutoRedirect = false
});
+
LinkGenerator = Factory.Services.GetRequiredService();
+
+ return Task.CompletedTask;
}
- ///
- /// Get the service from the underlying container that is also used by the .
- ///
- protected T GetRequiredService() => Factory.Services.GetRequiredService();
+ public override IHostBuilder CreateHostBuilder()
+ {
+ var builder = base.CreateHostBuilder();
+ builder.ConfigureWebHost(builder =>
+ {
+ // need to configure the IWebHostEnvironment too
+ builder.ConfigureServices((c, s) =>
+ {
+ c.HostingEnvironment = TestHelper.GetWebHostEnvironment();
+ });
+
+ // call startup
+ builder.Configure(app =>
+ {
+ Services = app.ApplicationServices;
+ Configure(app);
+ });
+ }).UseEnvironment(Environments.Development);
+
+ return builder;
+ }
///
/// Prepare a url before using .
@@ -67,13 +109,14 @@ namespace Umbraco.Tests.Integration.TestServerTest
return url;
}
- protected HttpClient Client { get; set; }
- protected LinkGenerator LinkGenerator { get; set; }
- protected UmbracoWebApplicationFactory Factory { get; set; }
+ protected HttpClient Client { get; private set; }
+ protected LinkGenerator LinkGenerator { get; private set; }
+ protected WebApplicationFactory Factory { get; private set; }
[TearDown]
- public void TearDown()
+ public override void TearDown()
{
+ base.TearDown();
Factory.Dispose();
@@ -81,7 +124,36 @@ namespace Umbraco.Tests.Integration.TestServerTest
{
Current.IsInitialized = false;
}
-
}
+
+ #region IStartup
+
+ public override void ConfigureServices(IServiceCollection services)
+ {
+ var umbracoBuilder = services.AddUmbraco(TestHelper.GetWebHostEnvironment(), Configuration);
+ umbracoBuilder
+ .WithConfiguration()
+ .WithTestCore(TestHelper, UmbracoContainer, UseTestLocalDb) // This is the important one!
+ .WithWebComponents()
+ .WithRuntimeMinifier()
+ .WithBackOffice()
+ .WithBackOfficeIdentity()
+ //.WithMiniProfiler() // we don't want this running in tests
+ .WithMvcAndRazor(mvcBuilding: mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPart(typeof(ContentController).Assembly);
+ })
+ .WithWebServer()
+ .Build();
+ }
+
+ public override void Configure(IApplicationBuilder app)
+ {
+ app.UseUmbraco();
+ }
+
+ #endregion
+
+
}
}
diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs
index ea2b64c790..251f155d2c 100644
--- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs
+++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs
@@ -1,97 +1,23 @@
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Hosting;
+using System;
using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Umbraco.Core;
-using Umbraco.Core.BackOffice;
-using Umbraco.Core.Composing;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Mapping;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Core.Services;
-using Umbraco.Web.Common.Security;
-using Umbraco.Web.UI.NetCore;
namespace Umbraco.Tests.Integration.TestServerTest
{
- public class UmbracoWebApplicationFactory : CustomWebApplicationFactory
+
+ public class UmbracoWebApplicationFactory : WebApplicationFactory where TStartup : class
{
- public UmbracoWebApplicationFactory(string testDbConnectionString) :base(testDbConnectionString)
- {
- }
- }
+ private readonly Func _createHostBuilder;
- public abstract class CustomWebApplicationFactory : WebApplicationFactory where TStartup : class
- {
- private readonly string _testDbConnectionString;
-
- protected CustomWebApplicationFactory(string testDbConnectionString)
+ ///
+ /// Constructor to create a new WebApplicationFactory
+ ///
+ /// Method to create the IHostBuilder
+ public UmbracoWebApplicationFactory(Func createHostBuilder)
{
- _testDbConnectionString = testDbConnectionString;
+ _createHostBuilder = createHostBuilder;
}
- protected override void ConfigureWebHost(IWebHostBuilder builder)
- {
- base.ConfigureWebHost(builder);
-
- builder.ConfigureTestServices(services =>
- {
- services.AddAuthentication("Test").AddScheme("Test", options => {});
- });
-
- builder.ConfigureAppConfiguration(x =>
- {
- x.AddInMemoryCollection(new Dictionary()
- {
- ["ConnectionStrings:"+ Constants.System.UmbracoConnectionName] = _testDbConnectionString,
- ["Umbraco:CMS:Hosting:Debug"] = "true",
- });
- });
-
- }
-
- protected override IHostBuilder CreateHostBuilder()
- {
-
- var builder = base.CreateHostBuilder();
- builder.UseUmbraco();
- return builder;
- }
- }
-
- public class TestAuthHandler : AuthenticationHandler
- {
- private readonly BackOfficeSignInManager _backOfficeSignInManager;
-
- private readonly BackOfficeIdentityUser _fakeUser;
- public TestAuthHandler(IOptionsMonitor options,
- ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, BackOfficeSignInManager backOfficeSignInManager, IUserService userService, UmbracoMapper umbracoMapper)
- : base(options, logger, encoder, clock)
- {
- _backOfficeSignInManager = backOfficeSignInManager;
-
- var user = userService.GetUserById(Constants.Security.SuperUserId);
- _fakeUser = umbracoMapper.Map(user);
- _fakeUser.SecurityStamp = "Needed";
- }
-
- protected override async Task HandleAuthenticateAsync()
- {
-
- var principal = await _backOfficeSignInManager.CreateUserPrincipalAsync(_fakeUser);
- var ticket = new AuthenticationTicket(principal, "Test");
-
- return AuthenticateResult.Success(ticket);
- }
+ protected override IHostBuilder CreateHostBuilder() => _createHostBuilder();
}
}
diff --git a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs
new file mode 100644
index 0000000000..69819c9bef
--- /dev/null
+++ b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs
@@ -0,0 +1,43 @@
+using Examine;
+using Examine.LuceneEngine.Providers;
+using Umbraco.Core.Composing;
+using Umbraco.Examine;
+
+namespace Umbraco.Tests.Integration.Testing
+{
+ ///
+ /// A component to customize some services to work nicely with integration tests
+ ///
+ public class IntegrationTestComponent : IComponent
+ {
+ private readonly IExamineManager _examineManager;
+
+ public IntegrationTestComponent(IExamineManager examineManager)
+ {
+ _examineManager = examineManager;
+ }
+
+ public void Initialize()
+ {
+ ConfigureExamineIndexes();
+ }
+
+ public void Terminate()
+ {
+ }
+
+ ///
+ /// Configure all indexes to run sync (non-backbround threads) and to use RAMDirectory
+ ///
+ private void ConfigureExamineIndexes()
+ {
+ foreach (var index in _examineManager.Indexes)
+ {
+ if (index is LuceneIndex luceneIndex)
+ {
+ luceneIndex.ProcessNonAsync();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComposer.cs b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComposer.cs
index 844877b1fd..e9b38741e2 100644
--- a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComposer.cs
+++ b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComposer.cs
@@ -1,7 +1,23 @@
using Moq;
+using NUnit.Framework;
+using System;
+using System.IO;
+using System.Linq;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Hosting;
+using Umbraco.Core.IO;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Services;
+using Umbraco.Core.Services.Implement;
using Umbraco.Core.WebAssets;
+using Umbraco.Examine;
+using Umbraco.Web.Compose;
+using Umbraco.Web.PublishedCache.NuCache;
+using Umbraco.Web.Scheduling;
+using Umbraco.Web.Search;
namespace Umbraco.Tests.Integration.Testing
{
@@ -13,11 +29,74 @@ namespace Umbraco.Tests.Integration.Testing
/// This is a IUserComposer so that it runs after all core composers
///
[RuntimeLevel(MinLevel = RuntimeLevel.Boot)]
- public class IntegrationTestComposer : IUserComposer
+ public class IntegrationTestComposer : ComponentComposer
{
- public void Compose(Composition composition)
+ public override void Compose(Composition composition)
{
+ base.Compose(composition);
+
+ composition.Components().Remove();
+ composition.Components().Remove();
+ composition.RegisterUnique();
composition.RegisterUnique(factory => Mock.Of());
+
+ // we don't want persisted nucache files in tests
+ composition.Register(factory => new PublishedSnapshotServiceOptions { IgnoreLocalDb = true });
+
+ // ensure all lucene indexes are using RAM directory (no file system)
+ composition.RegisterUnique();
+
+ // replace this service so that it can lookup the correct file locations
+ composition.RegisterUnique(GetLocalizedTextService);
}
+
+ ///
+ /// Used to register a replacement for where the file sources are the ones within the netcore project so
+ /// we don't need to copy files
+ ///
+ ///
+ private ILocalizedTextService GetLocalizedTextService(IFactory factory)
+ {
+ var configs = factory.GetInstance();
+ var logger = factory.GetInstance();
+ var appCaches = factory.GetInstance();
+
+ var localizedTextService = new LocalizedTextService(
+ new Lazy(() =>
+ {
+ // get the src folder
+ var currFolder = new DirectoryInfo(TestContext.CurrentContext.TestDirectory);
+ while(!currFolder.Name.Equals("src", StringComparison.InvariantCultureIgnoreCase))
+ {
+ currFolder = currFolder.Parent;
+ }
+ var netcoreUI = currFolder.GetDirectories("Umbraco.Web.UI.NetCore", SearchOption.TopDirectoryOnly).First();
+ var mainLangFolder = new DirectoryInfo(Path.Combine(netcoreUI.FullName, configs.Global().UmbracoPath.TrimStart("~/"), "config", "lang"));
+
+ return new LocalizedTextServiceFileSources(
+ logger,
+ appCaches,
+ mainLangFolder);
+
+ }),
+ logger);
+
+ return localizedTextService;
+ }
+
+ // replace the default so there is no background index rebuilder
+ private class TestBackgroundIndexRebuilder : BackgroundIndexRebuilder
+ {
+ public TestBackgroundIndexRebuilder(IMainDom mainDom, IProfilingLogger logger, IApplicationShutdownRegistry hostingEnvironment, IndexRebuilder indexRebuilder)
+ : base(mainDom, logger, hostingEnvironment, indexRebuilder)
+ {
+ }
+
+ public override void RebuildIndexes(bool onlyEmptyIndexes, int waitMilliseconds = 0)
+ {
+ // noop
+ }
+ }
+
}
}
diff --git a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs
index f628437507..01abe90b32 100644
--- a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs
+++ b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs
@@ -26,7 +26,6 @@ namespace Umbraco.Tests.Integration.Testing
public const string DatabaseName = "UmbracoTests";
private readonly ILogger _logger;
- private readonly GlobalSettings _globalSettings;
private readonly LocalDb _localDb;
private readonly IUmbracoVersion _umbracoVersion;
private static LocalDb.Instance _instance;
@@ -39,11 +38,10 @@ namespace Umbraco.Tests.Integration.Testing
private DatabasePool _currentPool;
//It's internal because `Umbraco.Core.Persistence.LocalDb` is internal
- internal LocalDbTestDatabase(ILogger logger, GlobalSettings globalSettings, LocalDb localDb, string filesPath, IUmbracoDatabaseFactory dbFactory)
+ internal LocalDbTestDatabase(ILogger logger, LocalDb localDb, string filesPath, IUmbracoDatabaseFactory dbFactory)
{
_umbracoVersion = new UmbracoVersion();
_logger = logger;
- _globalSettings = globalSettings;
_localDb = localDb;
_filesPath = filesPath;
_dbFactory = dbFactory;
@@ -131,7 +129,7 @@ namespace Umbraco.Tests.Integration.Testing
using var trans = database.GetTransaction();
- var creator = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, _globalSettings);
+ var creator = new DatabaseSchemaCreator(database, _logger, _umbracoVersion);
creator.InitializeDatabaseSchema();
trans.Complete(); // commit it
diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
index 562480df83..975710f460 100644
--- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
+++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
@@ -3,7 +3,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
@@ -22,11 +21,21 @@ using Umbraco.Extensions;
using Umbraco.Tests.Testing;
using Umbraco.Web;
using ILogger = Umbraco.Core.Logging.ILogger;
+using Umbraco.Core.Runtime;
+using Umbraco.Core;
+using Moq;
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using System.Data.SqlClient;
+using System.Data.Common;
+using System.IO;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
+using ConnectionStrings = Umbraco.Core.Configuration.Models.ConnectionStrings;
namespace Umbraco.Tests.Integration.Testing
{
+
///
/// Abstract class for integration tests
///
@@ -37,7 +46,7 @@ namespace Umbraco.Tests.Integration.Testing
[NonParallelizable]
public abstract class UmbracoIntegrationTest
{
- public static LightInjectContainer GetUmbracoContainer(out UmbracoServiceProviderFactory serviceProviderFactory)
+ public static LightInjectContainer CreateUmbracoContainer(out UmbracoServiceProviderFactory serviceProviderFactory)
{
var container = UmbracoServiceProviderFactory.CreateServiceContainer();
serviceProviderFactory = new UmbracoServiceProviderFactory(container, false);
@@ -45,6 +54,232 @@ namespace Umbraco.Tests.Integration.Testing
return umbracoContainer;
}
+ private List _testTeardown = null;
+ private List _fixtureTeardown = new List();
+
+ public void OnTestTearDown(Action tearDown)
+ {
+ if (_testTeardown == null)
+ _testTeardown = new List();
+ _testTeardown.Add(tearDown);
+ }
+
+ public void OnFixtureTearDown(Action tearDown) => _fixtureTeardown.Add(tearDown);
+
+ [OneTimeTearDown]
+ public void FixtureTearDown()
+ {
+ foreach (var a in _fixtureTeardown) a();
+ }
+
+ [TearDown]
+ public virtual void TearDown()
+ {
+ foreach (var a in _testTeardown) a();
+ _testTeardown = null;
+ }
+
+ [SetUp]
+ public virtual async Task Setup()
+ {
+ var hostBuilder = CreateHostBuilder();
+ var host = await hostBuilder.StartAsync();
+ Services = host.Services;
+ var app = new ApplicationBuilder(host.Services);
+ Configure(app);
+ }
+
+ #region Generic Host Builder and Runtime
+
+ ///
+ /// Create the Generic Host and execute startup ConfigureServices/Configure calls
+ ///
+ ///
+ public virtual IHostBuilder CreateHostBuilder()
+ {
+ UmbracoContainer = CreateUmbracoContainer(out var serviceProviderFactory);
+ _serviceProviderFactory = serviceProviderFactory;
+
+ var hostBuilder = Host.CreateDefaultBuilder()
+ // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work
+ // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will
+ // 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(_serviceProviderFactory)
+ .ConfigureAppConfiguration((context, configBuilder) =>
+ {
+ context.HostingEnvironment = TestHelper.GetWebHostEnvironment();
+ Configuration = context.Configuration;
+ configBuilder.AddInMemoryCollection(InMemoryConfiguration);
+ })
+ .ConfigureServices((hostContext, services) =>
+ {
+ ConfigureServices(services);
+ });
+ return hostBuilder;
+ }
+
+ ///
+ /// Creates a instance for testing and registers an event handler for database install
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public CoreRuntime CreateTestRuntime(
+ Configs configs,
+ GlobalSettings globalSettings,
+ ConnectionStrings connectionStrings,
+ IUmbracoVersion umbracoVersion, IIOHelper ioHelper,
+ ILogger logger, IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo,
+ ITypeFinder typeFinder, AppCaches appCaches, IDbProviderFactoryCreator dbProviderFactoryCreator)
+ {
+ var runtime = CreateTestRuntime(configs,
+ globalSettings,
+ connectionStrings,
+ umbracoVersion,
+ ioHelper,
+ logger,
+ profiler,
+ hostingEnvironment,
+ backOfficeInfo,
+ typeFinder,
+ appCaches,
+ dbProviderFactoryCreator,
+ TestHelper.MainDom, // SimpleMainDom
+ UseTestLocalDb // DB Installation event handler
+ );
+
+ return runtime;
+ }
+
+ ///
+ /// Creates a instance for testing and registers an event handler for database install
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The event handler used for DB installation
+ ///
+ ///
+ public static CoreRuntime CreateTestRuntime(
+ Configs configs,
+ GlobalSettings globalSettings,
+ ConnectionStrings connectionStrings,
+ IUmbracoVersion umbracoVersion, IIOHelper ioHelper,
+ ILogger logger, IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo,
+ ITypeFinder typeFinder, AppCaches appCaches, IDbProviderFactoryCreator dbProviderFactoryCreator,
+ IMainDom mainDom, Action eventHandler)
+ {
+ var runtime = new CoreRuntime(
+ configs,
+ globalSettings,
+ connectionStrings,
+ umbracoVersion,
+ ioHelper,
+ logger,
+ profiler,
+ Mock.Of(),
+ hostingEnvironment,
+ backOfficeInfo,
+ dbProviderFactoryCreator,
+ mainDom,
+ typeFinder,
+ appCaches);
+
+ runtime.RuntimeEssentials += (sender, args) => eventHandler(sender, args);
+
+ return runtime;
+ }
+
+ #endregion
+
+ #region IStartup
+
+ public virtual void ConfigureServices(IServiceCollection services)
+ {
+ services.AddSingleton(TestHelper.DbProviderFactoryCreator);
+ var webHostEnvironment = TestHelper.GetWebHostEnvironment();
+ services.AddRequiredNetCoreServices(TestHelper, webHostEnvironment);
+
+ // Add it!
+ services.AddUmbracoConfiguration(Configuration);
+ services.AddUmbracoCore(
+ webHostEnvironment,
+ UmbracoContainer,
+ GetType().Assembly,
+ AppCaches.NoCache, // Disable caches for integration tests
+ TestHelper.GetLoggingConfiguration(),
+ CreateTestRuntime,
+ out _);
+
+ services.AddUmbracoWebComponents();
+ services.AddUmbracoRuntimeMinifier(Configuration);
+ services.AddUmbracoBackOffice();
+ services.AddUmbracoBackOfficeIdentity();
+
+ services.AddMvc();
+
+ services.AddSingleton(new ConsoleLogger(new MessageTemplates()));
+
+ CustomTestSetup(services);
+ }
+
+ public virtual void Configure(IApplicationBuilder app)
+ {
+ Services.GetRequiredService().EnsureUmbracoContext();
+
+ // get the currently set ptions
+ var testOptions = TestOptionAttributeBase.GetTestOptions();
+ if (testOptions.Boot)
+ {
+ app.UseUmbracoCore();
+ }
+ }
+
+ #endregion
+
+ #region LocalDb
+
+
+ private static readonly object _dbLocker = new object();
+ private static LocalDbTestDatabase _dbInstance;
+
+ ///
+ /// Event handler for the to install the database and register the to Terminate
+ ///
+ ///
+ ///
+ protected void UseTestLocalDb(CoreRuntime runtime, RuntimeEssentialsEventArgs args)
+ {
+ // MUST be terminated on teardown
+ OnTestTearDown(() => runtime.Terminate());
+
+ // This will create a db, install the schema and ensure the app is configured to run
+ InstallTestLocalDb(args.DatabaseFactory, runtime.ProfilingLogger, runtime.State, TestHelper.WorkingDirectory, out var connectionString);
+ TestDBConnectionString = connectionString;
+ InMemoryConfiguration["ConnectionStrings:" + Constants.System.UmbracoConnectionName] = TestDBConnectionString;
+ }
+
///
/// Get or create an instance of
///
@@ -56,7 +291,7 @@ namespace Umbraco.Tests.Integration.Testing
///
/// There must only be ONE instance shared between all tests in a session
///
- public static LocalDbTestDatabase GetOrCreate(string filesPath, ILogger logger, GlobalSettings globalSettings, IUmbracoDatabaseFactory dbFactory)
+ private static LocalDbTestDatabase GetOrCreateDatabase(string filesPath, ILogger logger, IUmbracoDatabaseFactory dbFactory)
{
lock (_dbLocker)
{
@@ -65,91 +300,109 @@ namespace Umbraco.Tests.Integration.Testing
var localDb = new LocalDb();
if (localDb.IsAvailable == false)
throw new InvalidOperationException("LocalDB is not available.");
- _dbInstance = new LocalDbTestDatabase(logger, globalSettings, localDb, filesPath, dbFactory);
+ _dbInstance = new LocalDbTestDatabase(logger, localDb, filesPath, dbFactory);
return _dbInstance;
}
}
- private static readonly object _dbLocker = new object();
- private static LocalDbTestDatabase _dbInstance;
-
- private Action _testTeardown = null;
- private Action _fixtureTeardown = null;
-
- public void OnTestTearDown(Action tearDown)
+ ///
+ /// Creates a LocalDb instance to use for the test
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void InstallTestLocalDb(
+ IUmbracoDatabaseFactory databaseFactory, IProfilingLogger logger,
+ IRuntimeState runtimeState, string workingDirectory, out string connectionString)
{
- _testTeardown = tearDown;
- }
+ connectionString = null;
+ var dbFilePath = Path.Combine(workingDirectory, "LocalDb");
- public void OnFixtureTearDown(Action tearDown)
- {
- _fixtureTeardown = tearDown;
- }
-
- [OneTimeTearDown]
- public void FixtureTearDown()
- {
- _fixtureTeardown?.Invoke();
- }
-
- [TearDown]
- public void TearDown()
- {
- _testTeardown?.Invoke();
- }
-
- [SetUp]
- public async Task Setup()
- {
- var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory);
- var testHelper = new TestHelper();
// get the currently set db options
var testOptions = TestOptionAttributeBase.GetTestOptions();
- var hostBuilder = new HostBuilder()
- .UseUmbraco(serviceProviderFactory)
- .ConfigureServices((hostContext, services) =>
- {
- services.AddSingleton(testHelper.DbProviderFactoryCreator);
- var webHostEnvironment = testHelper.GetWebHostEnvironment();
- services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
+ if (testOptions.Database == UmbracoTestOptions.Database.None)
+ return;
- // Add it!
- services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
- services.AddUmbracoWebComponents();
- services.AddUmbracoRuntimeMinifier(hostContext.Configuration);
- services.AddUmbracoBackOffice();
- services.AddUmbracoBackOfficeIdentity();
+ // need to manually register this factory
+ DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
- services.AddMvc();
+ if (!Directory.Exists(dbFilePath))
+ Directory.CreateDirectory(dbFilePath);
+ var db = GetOrCreateDatabase(dbFilePath, logger, databaseFactory);
- services.AddSingleton(new ConsoleLogger(new MessageTemplates()));
-
- CustomTestSetup(services);
- });
-
- var host = await hostBuilder.StartAsync();
- var app = new ApplicationBuilder(host.Services);
- Services = app.ApplicationServices;
-
- Services.GetRequiredService().EnsureUmbracoContext();
- // This will create a db, install the schema and ensure the app is configured to run
- app.UseTestLocalDb(testHelper.WorkingDirectory, this, out var connectionString);
- TestDBConnectionString = connectionString;
-
- if (testOptions.Boot)
+ switch (testOptions.Database)
{
- app.UseUmbracoCore();
- }
+ case UmbracoTestOptions.Database.NewSchemaPerTest:
+ // New DB + Schema
+ var newSchemaDbId = db.AttachSchema();
+
+ // Add teardown callback
+ OnTestTearDown(() => db.Detach(newSchemaDbId));
+
+ // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
+ if (!databaseFactory.Configured)
+ {
+ databaseFactory.Configure(db.ConnectionString, Constants.DatabaseProviders.SqlServer);
+ }
+
+ // re-run the runtime level check
+ runtimeState.DetermineRuntimeLevel();
+
+ Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level);
+
+ break;
+ case UmbracoTestOptions.Database.NewEmptyPerTest:
+
+ var newEmptyDbId = db.AttachEmpty();
+
+ // Add teardown callback
+ OnTestTearDown(() => db.Detach(newEmptyDbId));
+
+
+ break;
+ case UmbracoTestOptions.Database.NewSchemaPerFixture:
+
+ // New DB + Schema
+ var newSchemaFixtureDbId = db.AttachSchema();
+
+ // Add teardown callback
+ OnFixtureTearDown(() => db.Detach(newSchemaFixtureDbId));
+
+ break;
+ case UmbracoTestOptions.Database.NewEmptyPerFixture:
+
+ throw new NotImplementedException();
+
+ //// Add teardown callback
+ //integrationTest.OnFixtureTearDown(() => db.Detach());
+
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null);
+ }
+ connectionString = db.ConnectionString;
}
- protected T GetRequiredService() => Services.GetRequiredService();
+ #endregion
#region Common services
+ protected LightInjectContainer UmbracoContainer { get; private set; }
+ private UmbracoServiceProviderFactory _serviceProviderFactory;
+
+ protected virtual T GetRequiredService() => Services.GetRequiredService();
+
+ public Dictionary InMemoryConfiguration { get; } = new Dictionary();
+
+ public IConfiguration Configuration { get; protected set; }
+
+ public TestHelper TestHelper = new TestHelper();
+
protected string TestDBConnectionString { get; private set; }
protected virtual Action CustomTestSetup => services => { };
@@ -157,7 +410,7 @@ namespace Umbraco.Tests.Integration.Testing
///
/// Returns the DI container
///
- protected IServiceProvider Services { get; private set; }
+ protected IServiceProvider Services { get; set; }
///
/// Returns the
diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
index ba986ebd86..072f15bcd1 100644
--- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
+++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs b/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs
index bba2649af3..90b2da5500 100644
--- a/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs
+++ b/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs
@@ -43,8 +43,7 @@ namespace Umbraco.Tests.Migrations
upgrader.Execute(ScopeProvider, builder, Mock.Of(), logger);
- var globalSettings = new GlobalSettingsBuilder().Build();
- var helper = new DatabaseSchemaCreator(scope.Database, logger, UmbracoVersion, globalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, logger, UmbracoVersion);
var exists = helper.TableExists("umbracoUser");
Assert.IsTrue(exists);
diff --git a/src/Umbraco.Tests/Migrations/MigrationPlanTests.cs b/src/Umbraco.Tests/Migrations/MigrationPlanTests.cs
index 6fc06d0618..31ceb1bdbf 100644
--- a/src/Umbraco.Tests/Migrations/MigrationPlanTests.cs
+++ b/src/Umbraco.Tests/Migrations/MigrationPlanTests.cs
@@ -139,8 +139,7 @@ namespace Umbraco.Tests.Migrations
[Test]
public void ValidateUmbracoPlan()
{
- var globalSettings = new GlobalSettingsBuilder().Build();
- var plan = new UmbracoPlan(TestHelper.GetUmbracoVersion(), globalSettings);
+ var plan = new UmbracoPlan(TestHelper.GetUmbracoVersion());
plan.Validate();
Console.WriteLine(plan.FinalState);
Assert.IsFalse(plan.FinalState.IsNullOrWhiteSpace());
diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
index d1ee5ac690..7483b5c611 100644
--- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
+++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs
@@ -96,7 +96,7 @@ namespace Umbraco.Tests.Persistence
using (var database = _databaseFactory.CreateDatabase())
using (var transaction = database.GetTransaction())
{
- schemaHelper = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, new GlobalSettingsBuilder().Build());
+ schemaHelper = new DatabaseSchemaCreator(database, _logger, _umbracoVersion);
schemaHelper.InitializeDatabaseSchema();
transaction.Complete();
}
diff --git a/src/Umbraco.Tests/Persistence/SchemaValidationTest.cs b/src/Umbraco.Tests/Persistence/SchemaValidationTest.cs
index 27bab47968..704d64823c 100644
--- a/src/Umbraco.Tests/Persistence/SchemaValidationTest.cs
+++ b/src/Umbraco.Tests/Persistence/SchemaValidationTest.cs
@@ -23,8 +23,7 @@ namespace Umbraco.Tests.Persistence
using (var scope = ScopeProvider.CreateScope())
{
- var globalSettings = new GlobalSettingsBuilder().Build();
- var schema = new DatabaseSchemaCreator(scope.Database, Logger, UmbracoVersion, globalSettings);
+ var schema = new DatabaseSchemaCreator(scope.Database, Logger, UmbracoVersion);
result = schema.ValidateSchema(
//TODO: When we remove the xml cache from tests we can remove this too
DatabaseSchemaCreator.OrderedTables.Concat(new []{typeof(ContentXmlDto), typeof(PreviewXmlDto)}));
diff --git a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs
index 337f1a7289..1fe7153dd0 100644
--- a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs
+++ b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs
@@ -25,7 +25,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -52,7 +52,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -67,7 +67,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -81,7 +81,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -96,7 +96,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -110,7 +110,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -126,7 +126,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -142,7 +142,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -156,7 +156,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
@@ -169,7 +169,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -184,7 +184,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -198,7 +198,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable();
@@ -215,7 +215,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
- var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion, GlobalSettings);
+ var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of(), UmbracoVersion);
helper.CreateTable();
helper.CreateTable