From 9d320f79da870b9d817948d30e9ce97646b44dc0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 3 Apr 2020 01:08:52 +1100 Subject: [PATCH] New IRuntimeHash to fix type scanning in netcore in order to look at the right runtime bits to creaet a hash for --- .../Legacy/GlobalSettings.cs | 10 +- .../Models/GlobalSettings.cs | 2 - src/Umbraco.Core/Composing/IRuntimeHash.cs | 14 +++ src/Umbraco.Core/Composing/ITypeFinder.cs | 9 ++ src/Umbraco.Core/Composing/RuntimeHash.cs | 91 ++++++++++++++ .../Composing/RuntimeHashPaths.cs | 20 +++ src/Umbraco.Core/Composing/TypeFinder.cs | 7 +- src/Umbraco.Core/Composing/TypeLoader.cs | 116 ++---------------- .../Composing/VaryingRuntimeHash.cs | 19 +++ .../Configuration/IGlobalSettings.cs | 8 +- src/Umbraco.Core/Constants-AppSettings.cs | 15 +-- .../Hosting/IHostingEnvironment.cs | 15 +++ src/Umbraco.Core/IO/IOHelper.cs | 2 +- .../Runtime/CoreRuntime.cs | 2 +- .../TypeFinderBenchmarks.cs | 6 +- src/Umbraco.Tests.Common/SettingsForTests.cs | 1 - src/Umbraco.Tests.Common/TestHelperBase.cs | 4 +- .../Implementations/TestHelper.cs | 3 +- .../Implementations/TestHostingEnvironment.cs | 4 +- .../Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Composing/CompositionTests.cs | 2 +- .../Composing/TypeFinderTests.cs | 4 +- .../Composing/TypeLoaderTests.cs | 39 +++++- .../Configurations/GlobalSettingsTests.cs | 2 +- .../PublishedContentSnapshotTestBase.cs | 2 +- .../PublishedContent/PublishedContentTests.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 4 +- .../TestHelpers/BaseUsingSqlCeSyntax.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 2 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 10 +- src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 1 - .../UmbracoCoreServiceCollectionExtensions.cs | 19 ++- .../RuntimeMinification/SmidgeComposer.cs | 3 +- .../SmidgeHelperAccessor.cs | 29 +++++ .../SmidgeRuntimeMinifier.cs | 14 +-- src/Umbraco.Web.UI.NetCore/Startup.cs | 10 +- src/Umbraco.Web/UmbracoApplicationBase.cs | 15 ++- 38 files changed, 322 insertions(+), 192 deletions(-) create mode 100644 src/Umbraco.Core/Composing/IRuntimeHash.cs create mode 100644 src/Umbraco.Core/Composing/RuntimeHash.cs create mode 100644 src/Umbraco.Core/Composing/RuntimeHashPaths.cs create mode 100644 src/Umbraco.Core/Composing/VaryingRuntimeHash.cs create mode 100644 src/Umbraco.Web.Common/RuntimeMinification/SmidgeHelperAccessor.cs diff --git a/src/Umbraco.Configuration/Legacy/GlobalSettings.cs b/src/Umbraco.Configuration/Legacy/GlobalSettings.cs index cb3fd9cf47..d93297ca90 100644 --- a/src/Umbraco.Configuration/Legacy/GlobalSettings.cs +++ b/src/Umbraco.Configuration/Legacy/GlobalSettings.cs @@ -131,8 +131,8 @@ namespace Umbraco.Core.Configuration.Legacy if (_reservedPaths != null) return _reservedPaths; var reservedPaths = StaticReservedPaths; - var umbPath = ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.Path) && !ConfigurationManager.AppSettings[Constants.AppSettings.Path].IsNullOrWhiteSpace() - ? ConfigurationManager.AppSettings[Constants.AppSettings.Path] + var umbPath = ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.UmbracoPath) && !ConfigurationManager.AppSettings[Constants.AppSettings.UmbracoPath].IsNullOrWhiteSpace() + ? ConfigurationManager.AppSettings[Constants.AppSettings.UmbracoPath] : "~/umbraco"; //always add the umbraco path to the list reservedPaths += umbPath.EnsureEndsWith(','); @@ -146,12 +146,6 @@ namespace Umbraco.Core.Configuration.Legacy } } - /// - /// Gets the path to umbraco's root directory (/umbraco by default). - /// - /// The path. - public string Path => ConfigurationManager.AppSettings[Constants.AppSettings.Path]; - /// /// Gets or sets the configuration status. This will return the version number of the currently installed umbraco instance. /// diff --git a/src/Umbraco.Configuration/Models/GlobalSettings.cs b/src/Umbraco.Configuration/Models/GlobalSettings.cs index 68d293d104..4b30813bd5 100644 --- a/src/Umbraco.Configuration/Models/GlobalSettings.cs +++ b/src/Umbraco.Configuration/Models/GlobalSettings.cs @@ -30,8 +30,6 @@ namespace Umbraco.Configuration.Models public string ReservedUrls => _configuration.GetValue(Prefix + "ReservedUrls", StaticReservedUrls); public string ReservedPaths => _configuration.GetValue(Prefix + "ReservedPaths", StaticReservedPaths); - public string Path => _configuration.GetValue(Prefix + "Path"); - // TODO: https://github.com/umbraco/Umbraco-CMS/issues/4238 - stop having version in web.config appSettings public string ConfigurationStatus { diff --git a/src/Umbraco.Core/Composing/IRuntimeHash.cs b/src/Umbraco.Core/Composing/IRuntimeHash.cs new file mode 100644 index 0000000000..c2fb829cc6 --- /dev/null +++ b/src/Umbraco.Core/Composing/IRuntimeHash.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Core.Composing +{ + /// + /// Used to create a hash value of the current runtime + /// + /// + /// This is used to detect if the runtime itself has changed, like a DLL has changed or another dynamically compiled + /// part of the application has changed. This is used to detect if we need to re-type scan. + /// + public interface IRuntimeHash + { + string GetHashValue(); + } +} diff --git a/src/Umbraco.Core/Composing/ITypeFinder.cs b/src/Umbraco.Core/Composing/ITypeFinder.cs index f302976dd6..7ed6084074 100644 --- a/src/Umbraco.Core/Composing/ITypeFinder.cs +++ b/src/Umbraco.Core/Composing/ITypeFinder.cs @@ -51,5 +51,14 @@ namespace Umbraco.Core.Composing Type attributeType, IEnumerable assemblies, bool onlyConcreteClasses); + + /// + /// Gets a hash value of the current runtime + /// + /// + /// This is used to detect if the runtime itself has changed, like a DLL has changed or another dynamically compiled + /// part of the application has changed. This is used to detect if we need to re-type scan. + /// + string GetRuntimeHash(); } } diff --git a/src/Umbraco.Core/Composing/RuntimeHash.cs b/src/Umbraco.Core/Composing/RuntimeHash.cs new file mode 100644 index 0000000000..16665384c6 --- /dev/null +++ b/src/Umbraco.Core/Composing/RuntimeHash.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Composing +{ + /// + /// Determines the runtime hash based on file system paths to scan + /// + public class RuntimeHash : IRuntimeHash + { + private readonly IProfilingLogger _logger; + private readonly RuntimeHashPaths _paths; + + public RuntimeHash(IProfilingLogger logger, RuntimeHashPaths paths) + { + _logger = logger; + _paths = paths; + } + + + public string GetHashValue() + { + var allPaths = _paths.GetFolders() + .Select(x => ((FileSystemInfo) x, false)) + .Concat(_paths.GetFiles().Select(x => ((FileSystemInfo) x.Key, x.Value))); + + var hash = GetFileHash(allPaths); + + return hash; + } + + /// + /// Returns a unique hash for a combination of FileInfo objects. + /// + /// A collection of files. + /// The hash. + /// Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the + /// file properties (false) or the file contents (true). + private string GetFileHash(IEnumerable<(FileSystemInfo fileOrFolder, bool scanFileContent)> filesAndFolders) + { + using (_logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) + { + // get the distinct file infos to hash + var uniqInfos = new HashSet(); + var uniqContent = new HashSet(); + + using var generator = new HashGenerator(); + + foreach (var (fileOrFolder, scanFileContent) in filesAndFolders) + { + if (scanFileContent) + { + // add each unique file's contents to the hash + // normalize the content for cr/lf and case-sensitivity + if (uniqContent.Add(fileOrFolder.FullName)) + { + if (File.Exists(fileOrFolder.FullName) == false) continue; + var content = RemoveCrLf(File.ReadAllText(fileOrFolder.FullName)); + generator.AddCaseInsensitiveString(content); + } + } + else + { + // add each unique folder/file to the hash + if (uniqInfos.Add(fileOrFolder.FullName)) + { + generator.AddFileSystemItem(fileOrFolder); + } + } + } + return generator.GenerateHash(); + } + } + + // fast! (yes, according to benchmarks) + private static string RemoveCrLf(string s) + { + var buffer = new char[s.Length]; + var count = 0; + // ReSharper disable once ForCanBeConvertedToForeach - no! + for (var i = 0; i < s.Length; i++) + { + if (s[i] != '\r' && s[i] != '\n') + buffer[count++] = s[i]; + } + return new string(buffer, 0, count); + } + } +} diff --git a/src/Umbraco.Core/Composing/RuntimeHashPaths.cs b/src/Umbraco.Core/Composing/RuntimeHashPaths.cs new file mode 100644 index 0000000000..8b5af064af --- /dev/null +++ b/src/Umbraco.Core/Composing/RuntimeHashPaths.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.IO; + +namespace Umbraco.Core.Composing +{ + /// + /// Paths used to determine the + /// + public sealed class RuntimeHashPaths + { + private readonly List _paths = new List(); + private readonly Dictionary _files = new Dictionary(); + + public void AddFolder(DirectoryInfo pathInfo) => _paths.Add(pathInfo); + public void AddFile(FileInfo fileInfo, bool scanFileContent = false) => _files.Add(fileInfo, scanFileContent); + + public IEnumerable GetFolders() => _paths; + public IReadOnlyDictionary GetFiles() => _files; + } +} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 79fddad1ca..645182d66b 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -16,6 +16,7 @@ namespace Umbraco.Core.Composing { private readonly ILogger _logger; private readonly IAssemblyProvider _assemblyProvider; + private readonly IRuntimeHash _runtimeHash; private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); @@ -25,10 +26,11 @@ namespace Umbraco.Core.Composing // used for benchmark tests internal bool QueryWithReferencingAssemblies = true; - public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, IRuntimeHash runtimeHash, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _assemblyProvider = assemblyProvider; + _runtimeHash = runtimeHash; _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); } @@ -208,6 +210,9 @@ namespace Umbraco.Core.Composing return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses); } + /// + public string GetRuntimeHash() => _runtimeHash.GetHashValue(); + /// /// Returns a Type for the string type name /// diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 4d8b5c984c..ba6470b060 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -14,8 +14,6 @@ using File = System.IO.File; namespace Umbraco.Core.Composing { - - /// /// Provides methods to find and instantiate types. /// @@ -29,7 +27,6 @@ namespace Umbraco.Core.Composing { private const string CacheKey = "umbraco-types.list"; - private readonly IIOHelper _ioHelper; private readonly IAppPolicyCache _runtimeCache; private readonly IProfilingLogger _logger; @@ -49,30 +46,29 @@ namespace Umbraco.Core.Composing /// /// Initializes a new instance of the class. /// - /// + /// /// /// The application runtime cache. /// Files storage location. /// A profiling logger. /// - public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, IEnumerable assembliesToScan = null) - : this(ioHelper, typeFinder, runtimeCache, localTempPath, logger, true, assembliesToScan) + public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, IEnumerable assembliesToScan = null) + : this(typeFinder, runtimeCache, localTempPath, logger, true, assembliesToScan) { } /// /// Initializes a new instance of the class. /// - /// + /// /// /// The application runtime cache. /// Files storage location. /// A profiling logger. /// Whether to detect changes using hashes. /// - public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, bool detectChanges, IEnumerable assembliesToScan = null) + public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, bool detectChanges, IEnumerable assembliesToScan = null) { TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); - _ioHelper = ioHelper; _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); _localTempPath = localTempPath; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -124,7 +120,7 @@ namespace Umbraco.Core.Composing /// This is for unit tests. /// // internal for tests - public IEnumerable AssembliesToScan => _assemblies ?? (_assemblies = TypeFinder.AssembliesToScan); + public IEnumerable AssembliesToScan => _assemblies ??= TypeFinder.AssembliesToScan; /// /// Gets the type lists. @@ -183,19 +179,7 @@ namespace Umbraco.Core.Composing if (_currentAssembliesHash != null) return _currentAssembliesHash; - _currentAssembliesHash = GetFileHash(new List> - { - // TODO: Would be nicer to abstract this logic out into IAssemblyHash - - // TODO: Use constants from SystemDirectories when we can (once it's ported to netstandard lib) - - // the bin folder and everything in it - new Tuple(new DirectoryInfo(_ioHelper.MapPath("~/bin")), false), - // the app code folder and everything in it - new Tuple(new DirectoryInfo(_ioHelper.MapPath("~/App_Code")), false), - // global.asax (the app domain also monitors this, if it changes will do a full restart) - new Tuple(new FileInfo(_ioHelper.MapPath("~/global.asax")), false) - }, _logger); + _currentAssembliesHash = TypeFinder.GetRuntimeHash(); return _currentAssembliesHash; } @@ -210,92 +194,6 @@ namespace Umbraco.Core.Composing File.WriteAllText(typesHashFilePath, CurrentAssembliesHash, Encoding.UTF8); } - /// - /// Returns a unique hash for a combination of FileInfo objects. - /// - /// A collection of files. - /// A profiling logger. - /// The hash. - /// Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the - /// file properties (false) or the file contents (true). - private static string GetFileHash(IEnumerable> filesAndFolders, IProfilingLogger logger) - { - using (logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) - { - // get the distinct file infos to hash - var uniqInfos = new HashSet(); - var uniqContent = new HashSet(); - using (var generator = new HashGenerator()) - { - foreach (var fileOrFolder in filesAndFolders) - { - var info = fileOrFolder.Item1; - if (fileOrFolder.Item2) - { - // add each unique file's contents to the hash - // normalize the content for cr/lf and case-sensitivity - if (uniqContent.Add(info.FullName)) - { - if (File.Exists(info.FullName) == false) continue; - var content = RemoveCrLf(File.ReadAllText(info.FullName)); - generator.AddCaseInsensitiveString(content); - } - } - else - { - // add each unique folder/file to the hash - if (uniqInfos.Add(info.FullName)) - { - generator.AddFileSystemItem(info); - } - } - } - return generator.GenerateHash(); - } - } - } - - // fast! (yes, according to benchmarks) - private static string RemoveCrLf(string s) - { - var buffer = new char[s.Length]; - var count = 0; - // ReSharper disable once ForCanBeConvertedToForeach - no! - for (var i = 0; i < s.Length; i++) - { - if (s[i] != '\r' && s[i] != '\n') - buffer[count++] = s[i]; - } - return new string(buffer, 0, count); - } - - /// - /// Returns a unique hash for a combination of FileInfo objects. - /// - /// A collection of files. - /// A profiling logger. - /// The hash. - // internal for tests - public static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) - { - using (logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) - { - using (var generator = new HashGenerator()) - { - // get the distinct file infos to hash - var uniqInfos = new HashSet(); - - foreach (var fileOrFolder in filesAndFolders) - { - if (uniqInfos.Contains(fileOrFolder.FullName)) continue; - uniqInfos.Add(fileOrFolder.FullName); - generator.AddFileSystemItem(fileOrFolder); - } - return generator.GenerateHash(); - } - } - } - #endregion #region Cache diff --git a/src/Umbraco.Core/Composing/VaryingRuntimeHash.cs b/src/Umbraco.Core/Composing/VaryingRuntimeHash.cs new file mode 100644 index 0000000000..034af3b80c --- /dev/null +++ b/src/Umbraco.Core/Composing/VaryingRuntimeHash.cs @@ -0,0 +1,19 @@ +using System; + +namespace Umbraco.Core.Composing +{ + /// + /// A runtime hash this is always different on each app startup + /// + public sealed class VaryingRuntimeHash : IRuntimeHash + { + private readonly string _hash; + + public VaryingRuntimeHash() + { + _hash = DateTime.Now.Ticks.ToString(); + } + + public string GetHashValue() => _hash; + } +} diff --git a/src/Umbraco.Core/Configuration/IGlobalSettings.cs b/src/Umbraco.Core/Configuration/IGlobalSettings.cs index 3cb211a7a7..434aef6e68 100644 --- a/src/Umbraco.Core/Configuration/IGlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/IGlobalSettings.cs @@ -20,11 +20,6 @@ /// The reserved paths. string ReservedPaths { get; } - /// - /// Gets the path to umbraco's root directory. - /// - string Path { get; } - /// /// Gets or sets the configuration status. This will return the version number of the currently installed umbraco instance. /// @@ -61,6 +56,9 @@ /// The version check period in days (0 = never). int VersionCheckPeriod { get; } + /// + /// Gets the path to umbraco's root directory. + /// string UmbracoPath { get; } string UmbracoCssPath { get; } string UmbracoScriptsPath { get; } diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index c55b4b0314..3551aa1c31 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -41,32 +41,27 @@ namespace Umbraco.Core /// /// Gets the path to umbraco's root directory (/umbraco by default). /// - public const string Path = "Umbraco.Core.Path"; + public const string UmbracoPath = "Umbraco.Core.Path"; /// /// The reserved urls from web.config. /// public const string ReservedUrls = "Umbraco.Core.ReservedUrls"; - - /// - /// The path of backoffice. - /// - public const string UmbracoPath = "umbracoPath"; - + /// /// The path of the stylesheet folder. /// - public const string UmbracoCssPath = "umbracoCssPath"; + public const string UmbracoCssPath = "Umbraco.Web.CssPath"; /// /// The path of script folder. /// - public const string UmbracoScriptsPath = "umbracoScriptsPath"; + public const string UmbracoScriptsPath = "Umbraco.Core.ScriptsPath"; /// /// The path of media folder. /// - public const string UmbracoMediaPath = "umbracoMediaPath"; + public const string UmbracoMediaPath = "Umbraco.Core.MediaPath"; /// /// The reserved paths from web.config diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index b12ffbc138..a7fad8cbea 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -18,6 +18,21 @@ namespace Umbraco.Core.Hosting bool IsHosted { get; } Version IISVersion { get; } string MapPath(string path); + + /// + /// Maps a virtual path to the application's web root + /// + /// The virtual path. Must start with either ~/ or / else an exception is thrown. + /// The absolute web root value. Must start with / else an exception is thrown. + /// + /// + /// This maps the virtual path syntax to the web root. For example when hosting in a virtual directory called "site" and the value "~/pages/test" is passed in, it will + /// map to "/site/pages/test" where "/site" is the value of root. + /// + /// + /// If virtualPath does not start with ~/ or / + /// If root does not start with / + /// string ToAbsolute(string virtualPath, string root); } } diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index b68c58e91d..33deabf1e8 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.IO { get { - var path = _globalSettings.Path; + var path = _globalSettings.UmbracoPath; return string.IsNullOrEmpty(path) ? string.Empty : ResolveUrl(path); } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 5d2a4b4c64..1cedca96dc 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -163,7 +163,7 @@ namespace Umbraco.Core.Runtime var databaseFactory = GetDatabaseFactory(); // type finder/loader - var typeLoader = new TypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, new DirectoryInfo(HostingEnvironment.LocalTempPath), ProfilingLogger); + var typeLoader = new TypeLoader(TypeFinder, appCaches.RuntimeCache, new DirectoryInfo(HostingEnvironment.LocalTempPath), ProfilingLogger); // create the composition composition = new Composition(register, typeLoader, ProfilingLogger, _state, Configs, IOHelper, appCaches); diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs index 7b4322bfac..1571f63500 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -15,16 +15,18 @@ namespace Umbraco.Tests.Benchmarks [Benchmark(Baseline = true)] public void WithGetReferencingAssembliesCheck() { - var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); var found = typeFinder1.FindClassesOfType().Count(); } [Benchmark] public void WithoutGetReferencingAssembliesCheck() { - var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); typeFinder2.QueryWithReferencingAssemblies = false; var found = typeFinder2.FindClassesOfType().Count(); } + + } } diff --git a/src/Umbraco.Tests.Common/SettingsForTests.cs b/src/Umbraco.Tests.Common/SettingsForTests.cs index f7427009ba..c3ba47e61b 100644 --- a/src/Umbraco.Tests.Common/SettingsForTests.cs +++ b/src/Umbraco.Tests.Common/SettingsForTests.cs @@ -24,7 +24,6 @@ namespace Umbraco.Tests.Common settings.ConfigurationStatus == semanticVersion.ToSemanticString() && settings.UseHttps == false && settings.HideTopLevelNodeFromPath == false && - settings.Path == "~/umbraco" && settings.TimeOutInMinutes == 20 && settings.DefaultUILanguage == "en" && settings.ReservedPaths == (GlobalSettings.StaticReservedPaths + "~/umbraco") && diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index b14bd5fd5d..42b1e6c0dd 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -36,14 +36,14 @@ namespace Umbraco.Tests.Common { SettingsForTests = new SettingsForTests(); MainDom = new SimpleMainDom(); - _typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(entryAssembly)); + _typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(entryAssembly), new VaryingRuntimeHash()); } public ITypeFinder GetTypeFinder() => _typeFinder; public TypeLoader GetMockedTypeLoader() { - return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); + return new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); } public Configs GetConfigs() => GetConfigsFactory().Create(); diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index e756ee5062..d46c01acba 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -106,8 +106,7 @@ namespace Umbraco.Tests.Integration.Implementations public override IHostingEnvironment GetHostingEnvironment() => _hostingEnvironment ??= new TestHostingEnvironment( SettingsForTests.GetDefaultHostingSettings(), - _hostEnvironment, - _httpContextAccessor); + _hostEnvironment); public override IApplicationShutdownRegistry GetHostingEnvironmentLifetime() => _hostingLifetime; diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs index 4a54dd2823..6430291bc2 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs @@ -8,8 +8,8 @@ namespace Umbraco.Tests.Integration.Implementations public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Umbraco.Core.Hosting.IHostingEnvironment { - public TestHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) - : base(hostingSettings, webHostEnvironment, httpContextAccessor) + public TestHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment) + : base(hostingSettings, webHostEnvironment) { } diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index cb8da6e23d..807c12973c 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -56,7 +56,7 @@ namespace Umbraco.Tests.Components private static TypeLoader MockTypeLoader() { var ioHelper = TestHelper.IOHelper; - return new TypeLoader(ioHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); + return new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); } public static IRuntimeState MockRuntimeState(RuntimeLevel level) @@ -372,7 +372,7 @@ namespace Umbraco.Tests.Components { var ioHelper = TestHelper.IOHelper; var typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); + var typeLoader = new TypeLoader(typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); var composition = new Composition(register, typeLoader, Mock.Of(), diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index 6c5ccd5510..1977a5dfc1 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -24,7 +24,7 @@ namespace Umbraco.Tests.Composing var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; - TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); + TypeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } [TearDown] diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index ce3cdfac17..380511eaaa 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Composing var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; - var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); + var typeLoader = new TypeLoader(typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); // create the factory, ensure it is the mocked factory diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 3bdfd09752..aad8337d00 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index d0181563a8..980878d6f2 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Composing { // this ensures it's reset var typeFinder = TestHelper.GetTypeFinder(); - _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, + _typeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, @@ -217,7 +217,7 @@ AnotherContentFinder } [Test] - public void Get_Plugins_Hash() + public void Get_Plugins_Hash_With_Hash_Generator() { //Arrange var dir = PrepareFolder(); @@ -244,16 +244,16 @@ AnotherContentFinder var list3 = new[] { f1, f3, f5, f7 }; //Act - var hash1 = TypeLoader.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of())); - var hash2 = TypeLoader.GetFileHash(list2, new ProfilingLogger(Mock.Of(), Mock.Of())); - var hash3 = TypeLoader.GetFileHash(list3, new ProfilingLogger(Mock.Of(), Mock.Of())); + var hash1 = GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of())); + var hash2 = GetFileHash(list2, new ProfilingLogger(Mock.Of(), Mock.Of())); + var hash3 = GetFileHash(list3, new ProfilingLogger(Mock.Of(), Mock.Of())); //Assert Assert.AreNotEqual(hash1, hash2); Assert.AreNotEqual(hash1, hash3); Assert.AreNotEqual(hash2, hash3); - Assert.AreEqual(hash1, TypeLoader.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of()))); + Assert.AreEqual(hash1, GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of()))); } [Test] @@ -316,5 +316,32 @@ AnotherContentFinder } + + /// + /// Returns a unique hash for a combination of FileInfo objects. + /// + /// A collection of files. + /// A profiling logger. + /// The hash. + // internal for tests + private static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) + { + using (logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) + { + using (var generator = new HashGenerator()) + { + // get the distinct file infos to hash + var uniqInfos = new HashSet(); + + foreach (var fileOrFolder in filesAndFolders) + { + if (uniqInfos.Contains(fileOrFolder.FullName)) continue; + uniqInfos.Add(fileOrFolder.FullName); + generator.AddFileSystemItem(fileOrFolder); + } + return generator.GenerateHash(); + } + } + } } } diff --git a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs index b171199e25..a1f1e6a66d 100644 --- a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Configurations var ioHelper = new IOHelper(TestHelper.GetHostingEnvironment(), globalSettings); var globalSettingsMock = Mock.Get(globalSettings); - globalSettingsMock.Setup(x => x.Path).Returns(() => path); + globalSettingsMock.Setup(x => x.UmbracoPath).Returns(() => path); ioHelper.Root = rootPath; Assert.AreEqual(outcome, ioHelper.GetUmbracoMvcAreaNoCache()); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index 06a53db4ff..756f452f71 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.PublishedContent { var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, hostingEnvironment); - return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, + return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, // this is so the model factory looks into the test assembly baseLoader.AssembliesToScan .Union(new[] {typeof(PublishedContentMoreTests).Assembly}) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2667848c57..013ae712cc 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.PublishedContent { var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, hostingEnvironment); - return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, + return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, // this is so the model factory looks into the test assembly baseLoader.AssembliesToScan .Union(new[] { typeof(PublishedContentTests).Assembly }) diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 913fc59ad6..1b6526c24b 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Runtimes var databaseFactory = new UmbracoDatabaseFactory(logger,globalSettings, connectionStrings, new Lazy(() => factory.GetInstance()), TestHelper.DbProviderFactoryCreator); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); - var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); + var typeLoader = new TypeLoader(typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var mainDom = new SimpleMainDom(); var umbracoVersion = TestHelper.GetUmbracoVersion(); var backOfficeInfo = TestHelper.GetBackOfficeInfo(); @@ -262,7 +262,7 @@ namespace Umbraco.Tests.Runtimes var databaseFactory = Mock.Of(); var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; - var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); + var typeLoader = new TypeLoader(typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); var hostingEnvironment = Mock.Of(); var backOfficeInfo = TestHelper.GetBackOfficeInfo(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index e035eaa807..bd7df73172 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); var typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, + var typeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, false); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index e982b5c0d1..cc61c84abd 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -253,7 +253,7 @@ namespace Umbraco.Tests.TestHelpers TestHelper.DbProviderFactoryCreator); } - typeFinder ??= new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + typeFinder ??= new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); fileSystems ??= new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = TestHelper.CoreDebugSettings; var mediaFileSystem = Mock.Of(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 880ae62e4a..56f0534cf4 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -173,7 +173,7 @@ namespace Umbraco.Tests.Testing var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; - TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly), new VaryingRuntimeHash()); var appCaches = GetAppCaches(); var globalSettings = TestHelpers.SettingsForTests.GetDefaultGlobalSettings(); var settings = TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings(); @@ -376,7 +376,7 @@ namespace Umbraco.Tests.Testing switch (option) { case UmbracoTestOptions.TypeLoader.Default: - return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(ioHelper, typeFinder, runtimeCache, logger, hostingEnvironment)); + return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(typeFinder, runtimeCache, logger, hostingEnvironment)); case UmbracoTestOptions.TypeLoader.PerFixture: return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, hostingEnvironment)); case UmbracoTestOptions.TypeLoader.PerTest: @@ -388,13 +388,13 @@ namespace Umbraco.Tests.Testing protected virtual TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IProfilingLogger logger, IHostingEnvironment hostingEnvironment) { - return CreateCommonTypeLoader(ioHelper, typeFinder, runtimeCache, logger, hostingEnvironment); + return CreateCommonTypeLoader(typeFinder, runtimeCache, logger, hostingEnvironment); } // common to all tests = cannot be overriden - private static TypeLoader CreateCommonTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IProfilingLogger logger, IHostingEnvironment hostingEnvironment) + private static TypeLoader CreateCommonTypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IProfilingLogger logger, IHostingEnvironment hostingEnvironment) { - return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, new[] + return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, false, new[] { Assembly.Load("Umbraco.Core"), Assembly.Load("Umbraco.Web"), diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index 62d7e941d7..b17943e63e 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -33,7 +33,6 @@ namespace Umbraco.Tests.Web container .Setup(x => x.GetInstance(typeof(TypeLoader))) .Returns(new TypeLoader( - ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs index d8f8fc1aed..f82144f9a7 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Data.Common; +using System.IO; using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -100,10 +101,6 @@ namespace Umbraco.Web.Common.Extensions var globalSettings = configs.Global(); var umbracoVersion = new UmbracoVersion(globalSettings); - // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however - // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(entryAssembly)); - var coreRuntime = GetCoreRuntime( configs, umbracoVersion, @@ -112,13 +109,23 @@ namespace Umbraco.Web.Common.Extensions profiler, hostingEnvironment, backOfficeInfo, - typeFinder); + CreateTypeFinder(logger, profiler, webHostEnvironment, entryAssembly)); factory = coreRuntime.Configure(container); return services; } + private static ITypeFinder CreateTypeFinder(ILogger logger, IProfiler profiler, IWebHostEnvironment webHostEnvironment, Assembly entryAssembly) + { + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. + var runtimeHashPaths = new RuntimeHashPaths(); + runtimeHashPaths.AddFolder(new DirectoryInfo(Path.Combine(webHostEnvironment.ContentRootPath, "bin"))); + var runtimeHash = new RuntimeHash(new ProfilingLogger(logger, profiler), runtimeHashPaths); + return new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(entryAssembly), runtimeHash); + } + private static IRuntime GetCoreRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, ITypeFinder typeFinder) @@ -161,7 +168,7 @@ namespace Umbraco.Web.Common.Extensions var coreDebug = configs.CoreDebug(); var globalSettings = configs.Global(); - hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment, httpContextAccessor); + hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment); ioHelper = new IOHelper(hostingEnvironment, globalSettings); logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetCoreSessionIdResolver(httpContextAccessor), diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs index 748f3239e8..284b8e91da 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs @@ -9,7 +9,8 @@ namespace Umbraco.Web.Common.RuntimeMinification { public void Compose(Composition composition) { - composition.Register(Core.Composing.Lifetime.Scope); + composition.RegisterUnique(); + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeHelperAccessor.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeHelperAccessor.cs new file mode 100644 index 0000000000..c46a948bc2 --- /dev/null +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeHelperAccessor.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Smidge; + +namespace Umbraco.Web.Common.RuntimeMinification +{ + // work around for SmidgeHelper being request/scope lifetime + public sealed class SmidgeHelperAccessor + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public SmidgeHelperAccessor(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public SmidgeHelper SmidgeHelper + { + get + { + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext == null) + throw new InvalidOperationException($"Cannot get a {nameof(SmidgeHelper)} instance since there is no current http request"); + return httpContext.RequestServices.GetService(); + } + } + } +} diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index c279fd0280..b73a8d38bd 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -22,15 +22,15 @@ namespace Umbraco.Web.Common.RuntimeMinification private readonly ISmidgeConfig _smidgeConfig; private readonly IConfigManipulator _configManipulator; private readonly PreProcessPipelineFactory _preProcessPipelineFactory; - private readonly BundleManager _bundles; - private readonly SmidgeHelper _smidge; + private readonly IBundleManager _bundles; + private readonly SmidgeHelperAccessor _smidge; private PreProcessPipeline _jsPipeline; private PreProcessPipeline _cssPipeline; public SmidgeRuntimeMinifier( - BundleManager bundles, - SmidgeHelper smidge, + IBundleManager bundles, + SmidgeHelperAccessor smidge, PreProcessPipelineFactory preProcessPipelineFactory, IHostingEnvironment hostingEnvironment, ISmidgeConfig smidgeConfig, @@ -65,7 +65,7 @@ namespace Umbraco.Web.Common.RuntimeMinification // affect this or vice versa. } - public string RenderCssHere(string bundleName) => _smidge.CssHereAsync(bundleName, _hostingEnvironment.IsDebugMode).ToString(); + public string RenderCssHere(string bundleName) => _smidge.SmidgeHelper.CssHereAsync(bundleName, _hostingEnvironment.IsDebugMode).ToString(); public void CreateJsBundle(string bundleName, params string[] filePaths) { @@ -82,9 +82,9 @@ namespace Umbraco.Web.Common.RuntimeMinification // affect this or vice versa. } - public string RenderJsHere(string bundleName) => _smidge.JsHereAsync(bundleName, _hostingEnvironment.IsDebugMode).ToString(); + public string RenderJsHere(string bundleName) => _smidge.SmidgeHelper.JsHereAsync(bundleName, _hostingEnvironment.IsDebugMode).ToString(); - public async Task> GetAssetPathsAsync(string bundleName) => await _smidge.GenerateJsUrlsAsync(bundleName, _hostingEnvironment.IsDebugMode); + public async Task> GetAssetPathsAsync(string bundleName) => await _smidge.SmidgeHelper.GenerateJsUrlsAsync(bundleName, _hostingEnvironment.IsDebugMode); public async Task MinifyAsync(string fileContent, AssetType assetType) { diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 51ab151463..75b2d6f48e 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.UI.BackOffice { public class Startup { - private readonly IWebHostEnvironment _webHostEnvironment; + private readonly IWebHostEnvironment _env; private readonly IConfiguration _config; /// @@ -34,7 +34,7 @@ namespace Umbraco.Web.UI.BackOffice /// public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) { - _webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); + _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); _config = config ?? throw new ArgumentNullException(nameof(config)); } @@ -44,7 +44,7 @@ namespace Umbraco.Web.UI.BackOffice { services.AddUmbracoConfiguration(_config); services.AddUmbracoRuntimeMinifier(_config); - services.AddUmbracoCore(_webHostEnvironment, out var factory); + services.AddUmbracoCore(_env, out var factory); services.AddUmbracoWebsite(); services.AddMvc(); @@ -66,12 +66,12 @@ namespace Umbraco.Web.UI.BackOffice } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app) { // app.UseMiniProfiler(); app.UseUmbracoRequest(); - if (env.IsDevelopment()) + if (_env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index 0963ad3c07..5e1c3cd954 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Reflection; using System.Threading; using System.Web; @@ -80,9 +81,18 @@ namespace Umbraco.Web /// /// protected virtual ITypeFinder GetTypeFinder() + { // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( + var runtimeHashPaths = new RuntimeHashPaths(); + // the bin folder and everything in it + runtimeHashPaths.AddFolder(new DirectoryInfo(Current.IOHelper.MapPath("~/bin"))); + // the app code folder and everything in it + runtimeHashPaths.AddFile(new FileInfo(Current.IOHelper.MapPath("~/App_Code"))); + // global.asax (the app domain also monitors this, if it changes will do a full restart) + runtimeHashPaths.AddFile(new FileInfo(Current.IOHelper.MapPath("~/global.asax"))); + var runtimeHash = new RuntimeHash(new ProfilingLogger(Current.Logger, Current.Profiler), runtimeHashPaths); + return new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() @@ -94,7 +104,8 @@ namespace Umbraco.Web // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are // getting rid of netframework. - Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())); + Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly()), runtimeHash); + } /// /// Gets a runtime.