From d92fc8736ab136c67acadade9b1143d31ec57ca2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 13:31:56 +1100 Subject: [PATCH] More cleanup, cleans up tests, cleans up the BuildManagerAssemblyProvider (even though it's not used) --- .../Composing/BruteForceAssemblyProvider.cs | 102 ++++++ .../DefaultUmbracoAssemblyProvider.cs | 36 +++ .../FindAssembliesWithReferencesTo.cs | 55 ++++ .../Composing/IAssemblyProvider.cs | 13 + .../Composing/ReferenceResolver.cs | 115 +++++++ src/Umbraco.Core/Composing/TypeFinder.cs | 300 +----------------- src/Umbraco.Core/Umbraco.Core.csproj | 1 - .../Cache/DeepCloneAppCacheTests.cs | 3 +- .../Cache/HttpRequestAppCacheTests.cs | 2 +- .../Cache/ObjectAppCacheTests.cs | 9 +- .../Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Composing/CompositionTests.cs | 2 +- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Macros/MacroTests.cs | 2 +- src/Umbraco.Tests/Models/ContentTests.cs | 4 +- .../Published/PropertyCacheLevelTests.cs | 2 +- .../PublishedContent/NuCacheChildrenTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../ContentTypeServiceVariantsTests.cs | 2 +- .../TestHelpers/BaseUsingSqlCeSyntax.cs | 2 +- .../Stubs/TestControllerFactory.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 6 + src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 2 +- .../Composing/BuildManagerAssemblyProvider.cs | 70 ++++ .../Composing/BuildManagerTypeFinder.cs | 57 +--- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 29 files changed, 426 insertions(+), 378 deletions(-) create mode 100644 src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs create mode 100644 src/Umbraco.Core/Composing/IAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/ReferenceResolver.cs create mode 100644 src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs diff --git a/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs b/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs new file mode 100644 index 0000000000..04d0d70475 --- /dev/null +++ b/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security; +using Umbraco.Core.Exceptions; + +namespace Umbraco.Core.Composing +{ + /// + /// lazily load a reference to all local assemblies and gac 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 + /// + public class BruteForceAssemblyProvider : IAssemblyProvider + { + public BruteForceAssemblyProvider() + { + _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(); + 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; + }); + } + + private readonly Lazy> _allAssemblies; + private string _rootDir = string.Empty; + + public IEnumerable Assemblies => _allAssemblies.Value; + + // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here + private string GetRootDirectorySafe() + { + 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; + } + } +} diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs new file mode 100644 index 0000000000..1322dcbfa2 --- /dev/null +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -0,0 +1,36 @@ +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 (Core/Web) 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" }; + + public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) + { + _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); + } + + public IEnumerable Assemblies + { + get + { + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); + foreach(var found in finder.Find()) + { + yield return found; + } + } + } + } +} 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..6d77afd414 --- /dev/null +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +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 _assemblyItems; + private readonly Dictionary _classifications; + private readonly List _lookup = new List(); + + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) + { + _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _assemblyItems = assemblyItems; + _classifications = new Dictionary(); + + foreach (var item in assemblyItems) + { + _lookup.Add(item); + } + } + + public IEnumerable ResolveAssemblies() + { + var applicationParts = new List(); + + foreach (var item in _assemblyItems) + { + var classification = Resolve(item); + if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) + { + applicationParts.Add(item); + } + } + + return applicationParts; + } + + private Classification Resolve(Assembly assemblyItem) + { + if (_classifications.TryGetValue(assemblyItem, out var classification)) + { + return classification; + } + + // Initialize the dictionary with a value to short-circuit recursive references. + classification = Classification.Unknown; + _classifications[assemblyItem] = classification; + + if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) + { + classification = Classification.IsUmbraco; + } + else + { + classification = Classification.DoesNotReferenceUmbraco; + foreach (var reference in GetReferences(assemblyItem)) + { + // recurse + var referenceClassification = Resolve(reference); + + if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) + { + classification = Classification.ReferencesUmbraco; + break; + } + } + } + + Debug.Assert(classification != Classification.Unknown); + _classifications[assemblyItem] = 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))) + continue; + + var reference = Assembly.Load(referenceName); + if (!_lookup.Contains(reference)) + { + // A dependency references an item that isn't referenced by this project. + // We'll construct an item for so that we can calculate the classification based on it's name. + + _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 a0aab7c088..bc15cb63f2 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,16 +1,11 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; using System.Security; using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing @@ -21,11 +16,6 @@ namespace Umbraco.Core.Composing private readonly ILogger _logger; private readonly IAssemblyProvider _assemblyProvider; - //public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) - // : this(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name), typeFinderConfig) - //{ - //} - public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -151,7 +141,9 @@ namespace Umbraco.Core.Composing "WebDriver,", "itextsharp,", "mscorlib,", - "nunit.framework,", + "NUnit,", + "NUnit3TestAdapter,", + "Selenium.", }; /// @@ -446,290 +438,4 @@ namespace Umbraco.Core.Composing #endregion } - - /// - /// lazily load a reference to all local assemblies and gac 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 - /// - public class BruteForceAssemblyProvider : IAssemblyProvider - { - public BruteForceAssemblyProvider() - { - _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(); - 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; - }); - } - - private readonly Lazy> _allAssemblies; - private string _rootDir = string.Empty; - - public IEnumerable Assemblies => _allAssemblies.Value; - - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() - { - 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; - } - } - - /// - /// Provides a list of assemblies that can be scanned - /// - public interface IAssemblyProvider - { - IEnumerable Assemblies { get; } - } - - /// - /// 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 (Core/Web) 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" }; - - public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) - { - _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); - } - - public IEnumerable Assemblies - { - get - { - var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); - foreach(var found in finder.Find()) - { - yield return found; - } - } - } - } - - /// - /// 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 _assemblyItems; - private readonly Dictionary _classifications; - private readonly List _lookup = new List(); - - public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) - { - _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); - _assemblyItems = assemblyItems; - _classifications = new Dictionary(); - - foreach (var item in assemblyItems) - { - _lookup.Add(item); - } - } - - public IEnumerable ResolveAssemblies() - { - var applicationParts = new List(); - - foreach (var item in _assemblyItems) - { - var classification = Resolve(item); - if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) - { - applicationParts.Add(item); - } - } - - return applicationParts; - } - - private Classification Resolve(Assembly assemblyItem) - { - if (_classifications.TryGetValue(assemblyItem, out var classification)) - { - return classification; - } - - // Initialize the dictionary with a value to short-circuit recursive references. - classification = Classification.Unknown; - _classifications[assemblyItem] = classification; - - if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) - { - classification = Classification.IsUmbraco; - } - else - { - classification = Classification.DoesNotReferenceUmbraco; - foreach (var reference in GetReferences(assemblyItem)) - { - // recurse - var referenceClassification = Resolve(reference); - - if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) - { - classification = Classification.ReferencesUmbraco; - break; - } - } - } - - Debug.Assert(classification != Classification.Unknown); - _classifications[assemblyItem] = 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))) - continue; - - var reference = Assembly.Load(referenceName); - if (!_lookup.Contains(reference)) - { - // A dependency references an item that isn't referenced by this project. - // We'll construct an item for so that we can calculate the classification based on it's name. - - _lookup.Add(reference); - - yield return reference; - } - } - } - - protected enum Classification - { - Unknown, - DoesNotReferenceUmbraco, - ReferencesUmbraco, - IsUmbraco, - } - } - - - /// - /// 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/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 49d5090070..7a15e7fbed 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index dec895a432..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 28d9b42ab0..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 e8fc420f58..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 0b179d6d85..cf54b8e9ec 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, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly)); + 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,7 +371,7 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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(); diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index 5e90b777fe..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 7a71afa625..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 7d68f616fa..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 addc5afde0..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 c25197d79e..e33f707ee1 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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly))); + 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 d0c348bc0c..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 9947f76fb0..e8296320a5 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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 be2ba1b48b..1f6895296f 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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 2bcfd2d98b..ecc5094166 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 b2252e705d..aada99da71 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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 a8d045c5e8..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 19b45c8ad5..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 b2650695ac..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + 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 41b97ac580..177fc2d518 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -43,6 +43,12 @@ 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/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index 7846bf366b..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(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) diff --git a/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs b/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs new file mode 100644 index 0000000000..3c5cebe03e --- /dev/null +++ b/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs @@ -0,0 +1,70 @@ +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.Composing; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Composing +{ + /// + /// Uses the BuildManager to provide a list of assemblies to scan + /// + internal class BuildManagerAssemblyProvider : BruteForceAssemblyProvider, IAssemblyProvider + { + private readonly Lazy> _allAssemblies; + + public BuildManagerAssemblyProvider(IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + ILogger 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"); + } + } + + return assemblies; + } + } + catch (InvalidOperationException e) + { + if (e.InnerException is SecurityException == false) + throw; + } + + // Not hosted, just use the default implementation + return new HashSet(base.Assemblies); + }); + } + + IEnumerable IAssemblyProvider.Assemblies => _allAssemblies.Value; + } +} diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs index bd583bf29d..d3502c36fb 100644 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs @@ -1,19 +1,15 @@ 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 /// @@ -24,60 +20,13 @@ namespace Umbraco.Web.Composing { public BuildManagerTypeFinder( - IIOHelper ioHelper, - IHostingEnvironment hostingEnvironment, ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) : base(logger, assemblyProvider, 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"); - } - } - - return assemblies; - } - } - 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 /// @@ -85,10 +34,6 @@ namespace Umbraco.Web.Composing /// 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 diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fad79b0296..27ea9443a0 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,6 +143,7 @@ +