From f7b8a93a35e6f5acbd79e74bc15bbdbe5f0846d6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 16:15:37 -0600 Subject: [PATCH] Fixes typefinder/loader dependency refs, allows config to specify additional entry assemblies --- .../DefaultUmbracoAssemblyProvider.cs | 11 ++- .../FindAssembliesWithReferencesTo.cs | 7 +- src/Umbraco.Core/Composing/ITypeFinder.cs | 11 +-- src/Umbraco.Core/Composing/TypeFinder.cs | 23 +++--- .../Composing/TypeFinderConfig.cs | 10 +-- src/Umbraco.Core/Composing/TypeLoader.cs | 33 +++++--- .../Models/TypeFinderSettings.cs | 9 +++ .../TypeFinderBenchmarks.cs | 4 +- .../TypeLoaderBenchmarks.cs | 5 +- src/Umbraco.Tests.Common/TestHelperBase.cs | 4 +- .../Umbraco.Core/Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Umbraco.Core/Composing/TypeFinderTests.cs | 4 +- .../Umbraco.Core/Composing/TypeLoaderTests.cs | 1 + .../UmbracoBuilderExtensions.cs | 9 ++- .../Extensions/ServiceCollectionExtensions.cs | 77 +++++++++++-------- 16 files changed, 127 insertions(+), 87 deletions(-) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 52967a58e7..dce85264d1 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -17,12 +17,17 @@ namespace Umbraco.Cms.Core.Composing { private readonly Assembly _entryPointAssembly; private readonly ILoggerFactory _loggerFactory; + private readonly IEnumerable _additionalTargetAssemblies; private List _discovered; - public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly, ILoggerFactory loggerFactory) + public DefaultUmbracoAssemblyProvider( + Assembly entryPointAssembly, + ILoggerFactory loggerFactory, + IEnumerable additionalTargetAssemblies = null) { _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); _loggerFactory = loggerFactory; + _additionalTargetAssemblies = additionalTargetAssemblies; } // TODO: It would be worth investigating a netcore3 version of this which would use @@ -40,7 +45,9 @@ namespace Umbraco.Cms.Core.Composing return _discovered; } - var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, Constants.Composing.UmbracoCoreAssemblyNames, true, _loggerFactory); + IEnumerable additionalTargetAssemblies = _additionalTargetAssemblies.Concat(Constants.Composing.UmbracoCoreAssemblyNames); + + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, additionalTargetAssemblies.ToArray(), true, _loggerFactory); _discovered = finder.Find().ToList(); return _discovered; diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs index 5b554a5321..78cdb80f58 100644 --- a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -18,6 +18,7 @@ namespace Umbraco.Cms.Core.Composing private readonly string[] _targetAssemblies; private readonly bool _includeTargets; private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; /// /// Constructor @@ -32,6 +33,7 @@ namespace Umbraco.Cms.Core.Composing _targetAssemblies = targetAssemblyNames; _includeTargets = includeTargets; _loggerFactory = loggerFactory; + _logger = _loggerFactory.CreateLogger(); } public IEnumerable Find() @@ -50,9 +52,10 @@ namespace Umbraco.Cms.Core.Composing { referenceItems.Add(Assembly.Load(target)); } - catch (FileNotFoundException) + catch (FileNotFoundException ex) { // occurs if we cannot load this ... for example in a test project where we aren't currently referencing Umbraco.Web, etc... + _logger.LogDebug(ex, "Could not load assembly " + target); } } } diff --git a/src/Umbraco.Core/Composing/ITypeFinder.cs b/src/Umbraco.Core/Composing/ITypeFinder.cs index 2cf6ca23f7..2522c2f593 100644 --- a/src/Umbraco.Core/Composing/ITypeFinder.cs +++ b/src/Umbraco.Core/Composing/ITypeFinder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; @@ -51,14 +51,5 @@ namespace Umbraco.Cms.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/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 97b8e3847c..4299328b5d 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -17,7 +17,6 @@ namespace Umbraco.Cms.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(); @@ -27,13 +26,12 @@ namespace Umbraco.Cms.Core.Composing // used for benchmark tests internal bool QueryWithReferencingAssemblies { get; set; } = true; - public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, IRuntimeHash runtimeHash, ITypeFinderConfig typeFinderConfig = null) + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _assemblyProvider = assemblyProvider; - _runtimeHash = runtimeHash; _typeFinderConfig = typeFinderConfig; - } + } private string[] _assembliesAcceptingLoadExceptions = null; @@ -64,9 +62,12 @@ namespace Umbraco.Cms.Core.Composing var name = a.GetName().Name; // simple name of the assembly return AssembliesAcceptingLoadExceptions.Any(pattern => { - if (pattern.Length > name.Length) return false; // pattern longer than name - if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical - if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot + if (pattern.Length > name.Length) + return false; // pattern longer than name + if (pattern.Length == name.Length) + return pattern.InvariantEquals(name); // same length, must be identical + if (pattern[pattern.Length] != '.') + return false; // pattern is shorter than name, must end with dot return name.StartsWith(pattern); // and name must start with pattern }); } @@ -112,6 +113,8 @@ namespace Umbraco.Cms.Core.Composing && exclusionFilter.Any(f => x.FullName.StartsWith(f)) == false); } + // TODO: Kill this + /// /// this is our assembly filter to filter out known types that def don't contain types we'd like to find or plugins /// @@ -232,9 +235,6 @@ namespace Umbraco.Cms.Core.Composing return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses); } - /// - public string GetRuntimeHash() => _runtimeHash.GetHashValue(); - /// /// Returns a Type for the string type name /// @@ -455,7 +455,8 @@ namespace Umbraco.Cms.Core.Composing var ex = new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString()); // rethrow with new message, unless accepted - if (AcceptsLoadExceptions(a) == false) throw ex; + if (AcceptsLoadExceptions(a) == false) + throw ex; // log a warning, and return what we can lock (_notifiedLoadExceptionAssemblies) diff --git a/src/Umbraco.Core/Composing/TypeFinderConfig.cs b/src/Umbraco.Core/Composing/TypeFinderConfig.cs index 7940773231..8e63958a06 100644 --- a/src/Umbraco.Core/Composing/TypeFinderConfig.cs +++ b/src/Umbraco.Core/Composing/TypeFinderConfig.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Configuration.UmbracoSettings; @@ -15,17 +16,16 @@ namespace Umbraco.Cms.Core.Composing private readonly TypeFinderSettings _settings; private IEnumerable _assembliesAcceptingLoadExceptions; - public TypeFinderConfig(IOptions settings) - { - _settings = settings.Value; - } + public TypeFinderConfig(IOptions settings) => _settings = settings.Value; public IEnumerable AssembliesAcceptingLoadExceptions { get { if (_assembliesAcceptingLoadExceptions != null) + { return _assembliesAcceptingLoadExceptions; + } var s = _settings.AssembliesAcceptingLoadExceptions; return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index fa66780914..5e6808fb13 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -25,9 +25,8 @@ namespace Umbraco.Cms.Core.Composing /// on a hash of the DLLs in the ~/bin folder to check for cache expiration. /// public sealed class TypeLoader - { - internal const string CacheKey = "umbraco-types.list"; - + { + private readonly IRuntimeHash _runtimeHash; private readonly IAppPolicyCache _runtimeCache; private readonly ILogger _logger; private readonly IProfilingLogger _profilingLogger; @@ -44,7 +43,9 @@ namespace Umbraco.Cms.Core.Composing private bool _reportedChange; private readonly DirectoryInfo _localTempPath; private readonly Lazy _fileBasePath; - private readonly Dictionary<(string, string), IEnumerable> EmptyCache = new Dictionary<(string, string), IEnumerable>(); + private readonly Dictionary<(string, string), IEnumerable> _emptyCache = new Dictionary<(string, string), IEnumerable>(); + private string _typesListFilePath; + private string _typesHashFilePath; /// /// Initializes a new instance of the class. @@ -54,8 +55,8 @@ namespace Umbraco.Cms.Core.Composing /// Files storage location. /// A profiling logger. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, IEnumerable assembliesToScan = null) - : this(typeFinder, runtimeCache, localTempPath, logger, profiler, true, assembliesToScan) + public TypeLoader(ITypeFinder typeFinder, IRuntimeHash runtimeHash, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, IEnumerable assembliesToScan = null) + : this(typeFinder, runtimeHash, runtimeCache, localTempPath, logger, profiler, true, assembliesToScan) { } /// @@ -67,14 +68,18 @@ namespace Umbraco.Cms.Core.Composing /// A profiling logger. /// Whether to detect changes using hashes. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, bool detectChanges, IEnumerable assembliesToScan = null) + public TypeLoader(ITypeFinder typeFinder, IRuntimeHash runtimeHash, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, bool detectChanges, IEnumerable assembliesToScan = null) { if (profiler is null) { throw new ArgumentNullException(nameof(profiler)); } + var runtimeHashValue = runtimeHash.GetHashValue(); + CacheKey = runtimeHashValue + "umbraco-types.list"; + TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + _runtimeHash = runtimeHash; _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); _localTempPath = localTempPath; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -119,6 +124,8 @@ namespace Umbraco.Cms.Core.Composing } } + internal string CacheKey { get; } + /// /// Returns the underlying /// @@ -202,9 +209,11 @@ namespace Umbraco.Cms.Core.Composing get { if (_currentAssembliesHash != null) + { return _currentAssembliesHash; + } - _currentAssembliesHash = TypeFinder.GetRuntimeHash(); + _currentAssembliesHash = _runtimeHash.GetHashValue(); return _currentAssembliesHash; } @@ -268,7 +277,7 @@ namespace Umbraco.Cms.Core.Composing } } - return EmptyCache; + return _emptyCache; } // internal for tests @@ -277,7 +286,7 @@ namespace Umbraco.Cms.Core.Composing var typesListFilePath = GetTypesListFilePath(); if (typesListFilePath == null || File.Exists(typesListFilePath) == false) { - return EmptyCache; + return _emptyCache; } var cache = new Dictionary<(string, string), IEnumerable>(); @@ -332,9 +341,9 @@ namespace Umbraco.Cms.Core.Composing } // internal for tests - public string GetTypesListFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".list"; + public string GetTypesListFilePath() => _typesListFilePath ??= _fileBasePath.Value == null ? null : _fileBasePath.Value + ".list"; - private string GetTypesHashFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".hash"; + private string GetTypesHashFilePath() => _typesHashFilePath ??= _fileBasePath.Value == null ? null : _fileBasePath.Value + ".hash"; /// /// Used to produce the Lazy value of _fileBasePath diff --git a/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs b/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs index d942f47b4f..e41921de8c 100644 --- a/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs @@ -1,6 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System.Collections; +using System.Collections.Generic; + namespace Umbraco.Cms.Core.Configuration.Models { /// @@ -13,5 +16,11 @@ namespace Umbraco.Cms.Core.Configuration.Models /// Gets or sets a value for the assemblies that accept load exceptions during type finder operations. /// public string AssembliesAcceptingLoadExceptions { get; set; } + + /// + /// By default the entry assemblies for scanning plugin types is the Umbraco DLLs. If you require + /// scanning for plugins based on different root referenced assemblies you can add the assembly name to this list. + /// + public IEnumerable AdditionalEntryAssemblies { get; set; } } } diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs index 54dc414762..c3ba9ad393 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -16,8 +16,8 @@ namespace Umbraco.Tests.Benchmarks public TypeFinderBenchmarks() { - _typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); - _typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()) + _typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); + _typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)) { QueryWithReferencingAssemblies = false }; diff --git a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs index 00d6820302..1bd875142d 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs @@ -21,12 +21,12 @@ namespace Umbraco.Tests.Benchmarks { var typeFinder1 = new TypeFinder( new NullLogger(), - new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), - new VaryingRuntimeHash()); + new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); var cache = new ObjectCacheAppCache(); _typeLoader1 = new TypeLoader( typeFinder1, + new VaryingRuntimeHash(), cache, null, new NullLogger(), @@ -40,6 +40,7 @@ namespace Umbraco.Tests.Benchmarks _typeLoader2 = new TypeLoader( typeFinder1, + new VaryingRuntimeHash(), NoAppCache.Instance, null, new NullLogger(), diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index ec76e0ba08..5a7bd1abc7 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -44,13 +44,13 @@ namespace Umbraco.Cms.Tests.Common protected TestHelperBase(Assembly entryAssembly) { MainDom = new SimpleMainDom(); - _typeFinder = new TypeFinder(NullLoggerFactory.Instance.CreateLogger(), new DefaultUmbracoAssemblyProvider(entryAssembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + _typeFinder = new TypeFinder(NullLoggerFactory.Instance.CreateLogger(), new DefaultUmbracoAssemblyProvider(entryAssembly, NullLoggerFactory.Instance)); } public ITypeFinder GetTypeFinder() => _typeFinder; public TypeLoader GetMockedTypeLoader() => - new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + new TypeLoader(Mock.Of(), new VaryingRuntimeHash(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); //// public Configs GetConfigs() => GetConfigsFactory().Create(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index bd8fc3103f..af221514e3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components private static IServiceCollection MockRegister() => new ServiceCollection(); - private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), new VaryingRuntimeHash(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); [Test] public void Boot1A() @@ -438,7 +438,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components public void AllComposers() { ITypeFinder typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + var typeLoader = new TypeLoader(typeFinder, new VaryingRuntimeHash(), AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); IServiceCollection register = MockRegister(); var builder = new UmbracoBuilder(register, Mock.Of(), TestHelper.GetMockedTypeLoader()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs index 3a38f0db11..ac536b7aee 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing public void Initialize() { var typeFinder = TestHelper.GetTypeFinder(); - TypeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of(), false, AssembliesToScan); + TypeLoader = new TypeLoader(typeFinder, new VaryingRuntimeHash(), NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of(), false, AssembliesToScan); } protected virtual IEnumerable AssembliesToScan diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs index 027b851529..bbe6519817 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); IEnumerable typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); IEnumerable typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs index e81fcfdabd..cea18c5176 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs @@ -48,6 +48,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing }; _typeLoader = new TypeLoader( typeFinder, + new VaryingRuntimeHash(), NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 5f9027db46..957d5976e1 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -106,7 +106,14 @@ namespace Umbraco.Extensions IProfiler profiler = GetWebProfiler(config); ILoggerFactory loggerFactory = LoggerFactory.Create(cfg => cfg.AddSerilog(Log.Logger, false)); - TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), tempHostingEnvironment, loggerFactory, appCaches, config, profiler); + + TypeLoader typeLoader = services.AddTypeLoader( + Assembly.GetEntryAssembly(), + tempHostingEnvironment, + loggerFactory, + appCaches, + config, + profiler); // adds the umbraco startup filter which will call UseUmbraco early on before // other start filters are applied (depending on the ordering of IStartupFilters in DI). diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index d5a02027a2..df7ffd4328 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Serilog; @@ -60,37 +61,22 @@ namespace Umbraco.Extensions return services; } - internal static ITypeFinder AddTypeFinder( - this IServiceCollection services, - ILoggerFactory loggerFactory, - Assembly entryAssembly, - IConfiguration config, - IProfiler profiler) - { - TypeFinderSettings typeFinderSettings = config.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); - - var assemblyProvider = new DefaultUmbracoAssemblyProvider(entryAssembly, loggerFactory); - - RuntimeHashPaths runtimeHashPaths = new RuntimeHashPaths().AddAssemblies(assemblyProvider); - - var runtimeHash = new RuntimeHash( - new ProfilingLogger( - loggerFactory.CreateLogger(), - profiler), - runtimeHashPaths); - - var typeFinder = new TypeFinder( - loggerFactory.CreateLogger(), - assemblyProvider, - runtimeHash, - new TypeFinderConfig(Options.Create(typeFinderSettings)) - ); - - services.AddUnique(typeFinder); - - return typeFinder; - } - + /// + /// Called to create the to assign to the + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// This should never be called in a web project. It is used internally by Umbraco but could be used in unit tests. + /// If called in a web project it will have no affect except to create and return a new TypeLoader but this will not + /// be the instance in DI. + /// public static TypeLoader AddTypeLoader( this IServiceCollection services, Assembly entryAssembly, @@ -100,17 +86,42 @@ namespace Umbraco.Extensions IConfiguration configuration, IProfiler profiler) { - ITypeFinder typeFinder = services.AddTypeFinder(loggerFactory, entryAssembly, configuration, profiler); + TypeFinderSettings typeFinderSettings = configuration.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); + + var assemblyProvider = new DefaultUmbracoAssemblyProvider( + entryAssembly, + loggerFactory, + typeFinderSettings.AdditionalEntryAssemblies); + + RuntimeHashPaths runtimeHashPaths = new RuntimeHashPaths().AddAssemblies(assemblyProvider); + + var runtimeHash = new RuntimeHash( + new ProfilingLogger( + loggerFactory.CreateLogger(), + profiler), + runtimeHashPaths); + + var typeFinderConfig = new TypeFinderConfig(Options.Create(typeFinderSettings)); + + var typeFinder = new TypeFinder( + loggerFactory.CreateLogger(), + assemblyProvider, + typeFinderConfig + ); var typeLoader = new TypeLoader( typeFinder, + runtimeHash, appCaches.RuntimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), loggerFactory.CreateLogger(), profiler ); - services.AddUnique(typeLoader); + // This will add it ONCE and not again which is what we want since we don't actually want people to call this method + // in the web project. + services.TryAddSingleton(typeFinder); + services.TryAddSingleton(typeLoader); return typeLoader; }