diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 1322dcbfa2..c13a42ece7 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -8,13 +8,21 @@ 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 + /// 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" }; + 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) { @@ -26,10 +34,7 @@ namespace Umbraco.Core.Composing get { var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); - foreach(var found in finder.Find()) - { - yield return found; - } + return finder.Find(); } } } diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs index c705af685a..65dba8bf23 100644 --- a/src/Umbraco.Core/Composing/ReferenceResolver.cs +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -46,14 +46,12 @@ namespace Umbraco.Core.Composing var assemblies = new HashSet(_assemblies); // Get the unique directories of the assemblies - var assemblyLocations = GetAssemblyLocations(assemblies).ToList(); + 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 location in assemblyLocations) - { - var dir = Path.GetDirectoryName(location); - + foreach(var dir in assemblyLocations) + { foreach(var dll in Directory.EnumerateFiles(dir, "*.dll")) { var assemblyName = AssemblyName.GetAssemblyName(dll); @@ -85,9 +83,9 @@ namespace Umbraco.Core.Composing } - private IEnumerable GetAssemblyLocations(IEnumerable assemblies) + private IEnumerable GetAssemblyFolders(IEnumerable assemblies) { - return assemblies.Select(x => GetAssemblyLocation(x).ToLowerInvariant()).Distinct(); + 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 @@ -102,25 +100,30 @@ namespace Umbraco.Core.Composing return assembly.Location; } - private Classification Resolve(Assembly assemblyItem) + private Classification Resolve(Assembly assembly) { - if (_classifications.TryGetValue(assemblyItem, out var classification)) + if (_classifications.TryGetValue(assembly, 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)) + _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(assemblyItem)) + foreach (var reference in GetReferences(assembly)) { // recurse var referenceClassification = Resolve(reference); @@ -134,7 +137,7 @@ namespace Umbraco.Core.Composing } Debug.Assert(classification != Classification.Unknown); - _classifications[assemblyItem] = classification; + _classifications[assembly] = classification; return classification; } @@ -147,15 +150,15 @@ namespace Umbraco.Core.Composing 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. + // We'll add this reference so that we can calculate the classification. - _lookup.Add(reference); - - yield return reference; - } + _lookup.Add(reference); + } + yield return reference; } } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 1813c8d8f4..27b13a8365 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -94,7 +94,9 @@ namespace Umbraco.Core.Composing /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// internal static readonly string[] KnownAssemblyExclusionFilter = { - "mscorlib", + "mscorlib,", + "netstandard,", + "System,", "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -148,6 +150,7 @@ namespace Umbraco.Core.Composing "ImageProcessor", "MiniProfiler.", "Owin,", + "SQLite", }; /// diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index cf54b8e9ec..7dc6025b0a 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -378,7 +378,8 @@ namespace Umbraco.Tests.Components 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/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 729184eb1d..4dc94cc50a 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/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 177fc2d518..2475072bd6 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -45,7 +45,9 @@ namespace Umbraco.Tests.TestHelpers public static ITypeFinder GetTypeFinder() { - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); + + var typeFinder = new TypeFinder(Mock.Of(), + new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); return typeFinder; }