diff --git a/src/Umbraco.Core/AssemblyExtensions.cs b/src/Umbraco.Core/AssemblyExtensions.cs index 666dce18fc..fff444c34a 100644 --- a/src/Umbraco.Core/AssemblyExtensions.cs +++ b/src/Umbraco.Core/AssemblyExtensions.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; namespace Umbraco.Core @@ -66,5 +68,31 @@ namespace Umbraco.Core return new FileInfo(path); } + /// + /// Gets the objects for all the assemblies recursively referenced by a specified assembly. + /// + /// The assembly. + /// The objects for all the assemblies recursively referenced by the specified assembly. + public static IEnumerable GetDeepReferencedAssemblies(this Assembly assembly) + { + var allAssemblies = TypeFinder.GetAllAssemblies(); + var visiting = new Stack(); + var visited = new HashSet(); + + visiting.Push(assembly); + visited.Add(assembly); + while (visiting.Count > 0) + { + var visAsm = visiting.Pop(); + foreach (var refAsm in visAsm.GetReferencedAssemblies() + .Select(refAsmName => allAssemblies.SingleOrDefault(x => string.Equals(x.GetName().Name, refAsmName.Name, StringComparison.Ordinal))) + .Where(x => x != null && visited.Contains(x) == false)) + { + yield return refAsm.GetName(); + visiting.Push(refAsm); + visited.Add(refAsm); + } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index efb525e575..5cb82b3863 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -62,8 +63,17 @@ namespace Umbraco.Core /// private static bool HasReferenceToAssemblyWithName(Assembly assembly, string expectedAssemblyName) { + // The following code was not recursive ie if assembly.1 -> assembly.2 -> assembly.3, and expected is assembly.3, + // it would not consider that assembly.1 has a reference to assembly.3. Can cause issues eg when assembly.3 + // contains an attribute, assembly.2 defines a new attribute that inherit from that attribute, and assembly.1 + // uses the attribute in assembly.2. When looking for assemblies that have a chance to use the attribute, we + // look for assemblies that reference assembly.3, and we would fail to consider assembly.1. + // + // Better get deep-referenced assemblies. Has an impact on perfs obviously but only when the app starts. + return assembly - .GetReferencedAssemblies() + //.GetReferencedAssemblies() + .GetDeepReferencedAssemblies() .Select(a => a.Name) .Contains(expectedAssemblyName, StringComparer.Ordinal); }