diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs new file mode 100644 index 0000000000..61d7cff240 --- /dev/null +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Returns a list of scannable assemblies based on an entry point assembly and it's references + /// + /// + /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies and their references + /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. + /// + public class DefaultUmbracoAssemblyProvider : IAssemblyProvider + { + private readonly Assembly _entryPointAssembly; + private static readonly string[] UmbracoCoreAssemblyNames = new[] + { + "Umbraco.Core", + "Umbraco.Web", + "Umbraco.Infrastructure", + "Umbraco.PublishedCache.NuCache", + "Umbraco.ModelsBuilder.Embedded", + "Umbraco.Examine.Lucene", + }; + + public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) + { + _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); + } + + // TODO: It would be worth investigating a netcore3 version of this which would use + // var allAssemblies = System.Runtime.Loader.AssemblyLoadContext.All.SelectMany(x => x.Assemblies); + // that will still only resolve Assemblies that are already loaded but it would also make it possible to + // query dynamically generated assemblies once they are added. It would also provide the ability to probe + // assembly locations that are not in the same place as the entry point assemblies. + + public IEnumerable Assemblies + { + get + { + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); + return finder.Find(); + } + } + } +} diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs new file mode 100644 index 0000000000..91225ff973 --- /dev/null +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames + /// + /// + /// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs + /// + internal class FindAssembliesWithReferencesTo + { + private readonly Assembly[] _referenceAssemblies; + private readonly string[] _targetAssemblies; + private readonly bool _includeTargets; + + /// + /// Constructor + /// + /// Entry point assemblies + /// Used to check if the entry point or it's transitive assemblies reference these assembly names + /// If true will also use the target assembly names as entry point assemblies + public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets) + { + _referenceAssemblies = referenceAssemblies; + _targetAssemblies = targetAssemblyNames; + _includeTargets = includeTargets; + } + + public IEnumerable Find() + { + var referenceItems = new List(); + foreach (var assembly in _referenceAssemblies) + { + referenceItems.Add(assembly); + } + + if (_includeTargets) + { + foreach(var target in _targetAssemblies) + { + referenceItems.Add(Assembly.Load(target)); + } + } + + var provider = new ReferenceResolver(_targetAssemblies, referenceItems); + var assemblyNames = provider.ResolveAssemblies(); + return assemblyNames.ToList(); + } + + } +} diff --git a/src/Umbraco.Core/Composing/IAssemblyProvider.cs b/src/Umbraco.Core/Composing/IAssemblyProvider.cs new file mode 100644 index 0000000000..bde97a9556 --- /dev/null +++ b/src/Umbraco.Core/Composing/IAssemblyProvider.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Provides a list of assemblies that can be scanned + /// + public interface IAssemblyProvider + { + IEnumerable Assemblies { get; } + } +} diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs new file mode 100644 index 0000000000..65dba8bf23 --- /dev/null +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. + /// + /// + /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs + /// + internal class ReferenceResolver + { + private readonly HashSet _umbracoAssemblies; + private readonly IReadOnlyList _assemblies; + private readonly Dictionary _classifications; + private readonly List _lookup = new List(); + + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList entryPointAssemblies) + { + _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _assemblies = entryPointAssemblies; + _classifications = new Dictionary(); + + foreach (var item in entryPointAssemblies) + { + _lookup.Add(item); + } + } + + /// + /// Returns a list of assemblies that directly reference or transitively reference the targetAssemblies + /// + /// + /// + /// This includes all assemblies in the same location as the entry point assemblies + /// + public IEnumerable ResolveAssemblies() + { + var applicationParts = new List(); + + var assemblies = new HashSet(_assemblies); + + // Get the unique directories of the assemblies + var assemblyLocations = GetAssemblyFolders(assemblies).ToList(); + + // Load in each assembly in the directory of the entry assembly to be included in the search + // for Umbraco dependencies/transitive dependencies + foreach(var dir in assemblyLocations) + { + foreach(var dll in Directory.EnumerateFiles(dir, "*.dll")) + { + var assemblyName = AssemblyName.GetAssemblyName(dll); + + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assemblyName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + continue; + + // don't include this item if it's Umbraco + // TODO: We should maybe pass an explicit list of these names in? + if (assemblyName.FullName.StartsWith("Umbraco.")) + continue; + + var assembly = Assembly.Load(assemblyName); + assemblies.Add(assembly); + } + } + + foreach (var item in assemblies) + { + var classification = Resolve(item); + if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) + { + applicationParts.Add(item); + } + } + + return applicationParts; + } + + + private IEnumerable GetAssemblyFolders(IEnumerable assemblies) + { + return assemblies.Select(x => Path.GetDirectoryName(GetAssemblyLocation(x)).ToLowerInvariant()).Distinct(); + } + + // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs + private string GetAssemblyLocation(Assembly assembly) + { + if (Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out var result) && + result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) + { + return result.LocalPath; + } + + return assembly.Location; + } + + private Classification Resolve(Assembly assembly) + { + if (_classifications.TryGetValue(assembly, out var classification)) + { + return classification; + } + + // Initialize the dictionary with a value to short-circuit recursive references. + classification = Classification.Unknown; + _classifications[assembly] = classification; + + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assembly.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + { + // if its part of the filter it doesn't reference umbraco + classification = Classification.DoesNotReferenceUmbraco; + } + else if (_umbracoAssemblies.Contains(assembly.GetName().Name)) + { + classification = Classification.IsUmbraco; + } + else + { + classification = Classification.DoesNotReferenceUmbraco; + foreach (var reference in GetReferences(assembly)) + { + // recurse + var referenceClassification = Resolve(reference); + + if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) + { + classification = Classification.ReferencesUmbraco; + break; + } + } + } + + Debug.Assert(classification != Classification.Unknown); + _classifications[assembly] = classification; + return classification; + } + + protected virtual IEnumerable GetReferences(Assembly assembly) + { + foreach (var referenceName in assembly.GetReferencedAssemblies()) + { + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + continue; + + var reference = Assembly.Load(referenceName); + + if (!_lookup.Contains(reference)) + { + // A dependency references an item that isn't referenced by this project. + // We'll add this reference so that we can calculate the classification. + + _lookup.Add(reference); + } + yield return reference; + } + } + + protected enum Classification + { + Unknown, + DoesNotReferenceUmbraco, + ReferencesUmbraco, + IsUmbraco, + } + } +} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 9d88153b0a..79fddad1ca 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,106 +1,35 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Configuration; -using System.IO; using System.Linq; using System.Reflection; using System.Security; using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing { + /// public class TypeFinder : ITypeFinder { private readonly ILogger _logger; - - public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); - _allAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = GetRootDirectorySafe(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; - //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - - //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - return assemblies; - }); - } - - //Lazy access to the all assemblies list - private readonly Lazy> _allAssemblies; + private readonly IAssemblyProvider _assemblyProvider; private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); - private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); - private string _rootDir = ""; + private static readonly ConcurrentDictionary TypeNamesCache = new ConcurrentDictionary(); private readonly string[] _assembliesAcceptingLoadExceptions; - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() + // used for benchmark tests + internal bool QueryWithReferencingAssemblies = true; + + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { - if (string.IsNullOrEmpty(_rootDir) == false) - { - return _rootDir; - } - - var codeBase = Assembly.GetExecutingAssembly().CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - var baseDirectory = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(baseDirectory)) - throw new PanicException("No root directory could be resolved."); - - _rootDir = baseDirectory.Contains("bin") - ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) - : baseDirectory; - - return _rootDir; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assemblyProvider = assemblyProvider; + _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); } private bool AcceptsLoadExceptions(Assembly a) @@ -119,22 +48,8 @@ namespace Umbraco.Core.Composing }); } - /// - /// lazily load a reference to all assemblies and only local assemblies. - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - private IEnumerable GetAllAssemblies() - { - return _allAssemblies.Value; - } + + private IEnumerable GetAllAssemblies() => _assemblyProvider.Assemblies; /// public IEnumerable AssembliesToScan @@ -181,7 +96,10 @@ namespace Umbraco.Core.Composing /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// - private static readonly string[] KnownAssemblyExclusionFilter = { + internal static readonly string[] KnownAssemblyExclusionFilter = { + "mscorlib,", + "netstandard,", + "System,", "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -228,7 +146,14 @@ namespace Umbraco.Core.Composing "WebDriver,", "itextsharp,", "mscorlib,", - "nunit.framework,", + "NUnit,", + "NUnit.", + "NUnit3.", + "Selenium.", + "ImageProcessor", + "MiniProfiler.", + "Owin,", + "SQLite", }; /// @@ -290,6 +215,11 @@ namespace Umbraco.Core.Composing /// public virtual Type GetTypeByName(string name) { + + //NOTE: This will not find types in dynamic assemblies unless those assemblies are already loaded + //into the appdomain. + + // This is exactly what the BuildManager does, if the type is an assembly qualified type // name it will find it. if (TypeNameContainsAssembly(name)) @@ -340,18 +270,24 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(attributeType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate) { // get all assembly types that can be assigned to baseType try { assemblyTypes = GetTypesWithFormattedException(assembly) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -371,10 +307,13 @@ namespace Umbraco.Core.Composing if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } @@ -405,19 +344,25 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(baseType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); // get all assembly types that can be assigned to baseType - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate) { try { assemblyTypes = GetTypesWithFormattedException(assembly) .Where(baseType.IsAssignableFrom) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -437,10 +382,13 @@ namespace Umbraco.Core.Composing if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed)) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } @@ -522,6 +470,5 @@ namespace Umbraco.Core.Composing #endregion - } } diff --git a/src/Umbraco.Core/Composing/TypeFinderConfig.cs b/src/Umbraco.Core/Composing/TypeFinderConfig.cs new file mode 100644 index 0000000000..3dc672b27c --- /dev/null +++ b/src/Umbraco.Core/Composing/TypeFinderConfig.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Composing +{ + /// + /// TypeFinder config via appSettings + /// + internal class TypeFinderConfig : ITypeFinderConfig + { + private readonly ITypeFinderSettings _settings; + private IEnumerable _assembliesAcceptingLoadExceptions; + + public TypeFinderConfig(ITypeFinderSettings settings) + { + _settings = settings; + } + + public IEnumerable AssembliesAcceptingLoadExceptions + { + get + { + if (_assembliesAcceptingLoadExceptions != null) + return _assembliesAcceptingLoadExceptions; + + var s = _settings.AssembliesAcceptingLoadExceptions; + return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) + ? Array.Empty() + : s.Split(',').Select(x => x.Trim()).ToArray(); + } + } + } +} diff --git a/src/Umbraco.Core/Composing/TypeHelper.cs b/src/Umbraco.Core/Composing/TypeHelper.cs index 28eab6a5ec..1987a4059c 100644 --- a/src/Umbraco.Core/Composing/TypeHelper.cs +++ b/src/Umbraco.Core/Composing/TypeHelper.cs @@ -82,9 +82,9 @@ namespace Umbraco.Core.Composing /// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot /// reference that assembly, same with the global.asax assembly. /// - public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) + public static IReadOnlyList GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) { - if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) + if (assembly.IsDynamic || assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) return EmptyAssemblies; @@ -92,7 +92,7 @@ namespace Umbraco.Core.Composing // should only be scanning those assemblies because any other assembly will definitely not // contain sub type's of the one we're currently looking for var name = assembly.GetName().Name; - return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray(); + return assemblies.Where(x => x == assembly || HasReference(x, name)).ToList(); } /// diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 76d00c472d..4d8b5c984c 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -516,29 +516,29 @@ namespace Umbraco.Core.Composing #region Get Assembly Attributes - /// - /// Gets the assembly attributes of the specified type . - /// - /// The attribute type. - /// - /// The assembly attributes of the specified type . - /// - public IEnumerable GetAssemblyAttributes() - where T : Attribute - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets the assembly attributes of the specified type . + ///// + ///// The attribute type. + ///// + ///// The assembly attributes of the specified type . + ///// + //public IEnumerable GetAssemblyAttributes() + // where T : Attribute + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} - /// - /// Gets all the assembly attributes. - /// - /// - /// All assembly attributes. - /// - public IEnumerable GetAssemblyAttributes() - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets all the assembly attributes. + ///// + ///// + ///// All assembly attributes. + ///// + //public IEnumerable GetAssemblyAttributes() + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} /// /// Gets the assembly attributes of the specified . diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 38731ce21c..7b1aff1a4b 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Reflection; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; @@ -369,7 +370,21 @@ namespace Umbraco.Core.Runtime /// /// protected virtual ITypeFinder GetTypeFinder() - => new TypeFinder(Logger); + // 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( + // 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() + // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. + // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own + // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting + // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance + // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point + // 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())); /// diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs new file mode 100644 index 0000000000..7b4322bfac --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -0,0 +1,30 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [MediumRunJob] + [MemoryDiagnoser] + public class TypeFinderBenchmarks + { + + [Benchmark(Baseline = true)] + public void WithGetReferencingAssembliesCheck() + { + var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var found = typeFinder1.FindClassesOfType().Count(); + } + + [Benchmark] + public void WithoutGetReferencingAssembliesCheck() + { + var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + typeFinder2.QueryWithReferencingAssemblies = false; + var found = typeFinder2.FindClassesOfType().Count(); + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 7566d8ab85..84ec535b9d 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 3a504ad4e2..e4844cc6be 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Tests.Collections; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Cache; namespace Umbraco.Tests.Cache @@ -28,7 +29,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _memberCache = new ObjectCacheAppCache(typeFinder); _provider = new DeepCloneAppCache(_memberCache); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index b9c948c1de..dbda6fb429 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _ctx = new FakeHttpContextFactory("http://localhost/test"); _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index 772e92cabe..7957026ad8 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -1,10 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using Moq; +using System.Linq; using NUnit.Framework; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Cache { @@ -21,7 +18,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _provider = new ObjectCacheAppCache(typeFinder); } diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index bef72e5fb7..7dc6025b0a 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components var mock = new Mock(); var logger = Mock.Of(); - var typeFinder = new TypeFinder(logger); + var typeFinder = TestHelper.GetTypeFinder(); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Mock.Of(); @@ -371,14 +371,15 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache); - var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); + var allComposers = typeLoader.GetTypes().ToList(); + var types = allComposers.Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")).ToList(); var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index ac7dd8be2a..6c5ccd5510 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + 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); } diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index 4dfaf6871d..ce3cdfac17 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(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 composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 5fe4c241d6..3bdfd09752 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()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -65,12 +65,15 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = typeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + + typesFound = typeFinder.FindClassesWithAttribute(); + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static IProfilingLogger GetTestProfilingLogger() diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 6658c689e1..d0181563a8 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index 66adfc4a97..d6ed8f33c2 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros [SetUp] public void Setup() { - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); //we DO want cache enabled for these tests var cacheHelper = new AppCaches( new ObjectCacheAppCache(typeFinder), diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index d5117c6a69..07b7ae7cba 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -22,6 +22,7 @@ using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Models { @@ -269,7 +270,8 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of())); + var typeFinder = TestHelper.GetTypeFinder(); + var runtimeCache = new ObjectCacheAppCache(typeFinder); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index d3e6dae26b..769985d515 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var elementsCache = new FastDictionaryAppCache(typeFinder); var snapshotCache = new FastDictionaryAppCache(typeFinder); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index c1f571aa91..bea0dce979 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index c5c2f678ee..7abd4179f0 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index e4d0e596ae..9fb77ef59d 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -125,6 +125,10 @@ namespace Umbraco.Tests.Runtimes } + // override because we cannot use Assembly.GetEntryAssembly in Nunit tests since that is always null + protected override ITypeFinder GetTypeFinder() + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + // must override the database factory // else BootFailedException because U cannot connect to the configured db protected internal override IUmbracoDatabaseFactory GetDatabaseFactory() diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 225506093e..2560ae8e94 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); - var typeFinder = new TypeFinder(logger); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = Mock.Of(); - var typeFinder = new TypeFinder(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 runtimeState = Mock.Of(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 7a388417bc..41c8d59105 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping var memberRepository = Mock.Of(); var hostingEnvironment = TestHelper.GetHostingEnvironment(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 1b6b632a10..33e8b0010e 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services var memberRepository = Mock.Of(); var hostingEnvironment = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index 2bd36947c6..e035eaa807 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index c79a0f5c47..f5d18e05ba 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { if (_factory != null) return _factory(requestContext); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 309ba9a5fc..b201959de6 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -43,6 +43,14 @@ namespace Umbraco.Tests.TestHelpers public static class TestHelper { + public static ITypeFinder GetTypeFinder() + { + + var typeFinder = new TypeFinder(Mock.Of(), + new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); + return typeFinder; + } + public static TypeLoader GetMockedTypeLoader() { return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 2af409e024..bb342c9939 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -247,7 +247,7 @@ namespace Umbraco.Tests.TestHelpers databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy(() => mappers), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); } - typeFinder = typeFinder ?? new TypeFinder(logger); + typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Current.Configs.CoreDebug(); var mediaFileSystem = Mock.Of(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 41c05276fb..58a6fa6d41 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -171,7 +171,7 @@ namespace Umbraco.Tests.Testing var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; - TypeFinder = new TypeFinder(logger); + TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var appCaches = GetAppCaches(); var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); var settings = SettingsForTests.GenerateMockWebRoutingSettings(); diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index b479961896..62d7e941d7 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Web { // FIXME: bad in a unit test - but Udi has a static ctor that wants it?! var container = new Mock(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs deleted file mode 100644 index 994e8c26c4..0000000000 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Web.Compilation; -using Umbraco.Core.Configuration; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Composing -{ - /// - /// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites - /// - /// - /// This finder will also try to resolve dynamic assemblies created from App_Code - /// - internal class BuildManagerTypeFinder : TypeFinder, ITypeFinder - { - - public BuildManagerTypeFinder( - IIOHelper ioHelper, - IHostingEnvironment hostingEnvironment, - ILogger logger, - ITypeFinderConfig typeFinderConfig = null) : base(logger, typeFinderConfig) - { - if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper)); - if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); - if (logger == null) throw new ArgumentNullException(nameof(logger)); - - _allAssemblies = new Lazy>(() => - { - var isHosted = hostingEnvironment.IsHosted; - try - { - if (isHosted) - { - var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code"))); - //check if the folder exists and if there are any files in it with the supported file extensions - if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) - { - try - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - catch (FileNotFoundException ex) - { - //this will occur if it cannot load the assembly - logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); - } - } - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - // Not hosted, just use the default implementation - return new HashSet(base.AssembliesToScan); - }); - } - - private readonly Lazy> _allAssemblies; - - /// - /// Explicitly implement and return result from BuildManager - /// - /// - /// - Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false); - - /// - /// Explicitly implement and return result from BuildManager - /// - IEnumerable ITypeFinder.AssembliesToScan => _allAssemblies.Value; - - /// - /// TypeFinder config via appSettings - /// - internal class TypeFinderConfig : ITypeFinderConfig - { - private readonly ITypeFinderSettings _settings; - private IEnumerable _assembliesAcceptingLoadExceptions; - - public TypeFinderConfig(ITypeFinderSettings settings) - { - _settings = settings; - } - - public IEnumerable AssembliesAcceptingLoadExceptions - { - get - { - if (_assembliesAcceptingLoadExceptions != null) - return _assembliesAcceptingLoadExceptions; - - var s = _settings.AssembliesAcceptingLoadExceptions; - return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) - ? Array.Empty() - : s.Split(',').Select(x => x.Trim()).ToArray(); - } - } - } - } -} diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 582f35db70..ea08dc9135 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -21,8 +21,6 @@ namespace Umbraco.Web.Runtime /// On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco. public class WebRuntime : CoreRuntime { - private BuildManagerTypeFinder _typeFinder; - /// /// Initializes a new instance of the class. /// @@ -92,8 +90,6 @@ namespace Umbraco.Web.Runtime #region Getters - protected override ITypeFinder GetTypeFinder() => _typeFinder ?? (_typeFinder = new BuildManagerTypeFinder(IOHelper, HostingEnvironment, Logger, new BuildManagerTypeFinder.TypeFinderConfig(new TypeFinderSettings()))); - protected override AppCaches GetAppCaches() => new AppCaches( // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 504f36786a..df6233431a 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,7 +143,6 @@ -