Merge pull request #7778 from umbraco/netcore/feature/typefinder-netstandard
Creates a netstandard compliant TypeFinder
This commit is contained in:
47
src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs
Normal file
47
src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of scannable assemblies based on an entry point assembly and it's references
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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<Assembly> Assemblies
|
||||
{
|
||||
get
|
||||
{
|
||||
var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true);
|
||||
return finder.Find();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs
Normal file
55
src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames
|
||||
/// </summary>
|
||||
/// <remarkes>
|
||||
/// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs
|
||||
/// </remarkes>
|
||||
internal class FindAssembliesWithReferencesTo
|
||||
{
|
||||
private readonly Assembly[] _referenceAssemblies;
|
||||
private readonly string[] _targetAssemblies;
|
||||
private readonly bool _includeTargets;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="referenceAssemblies">Entry point assemblies</param>
|
||||
/// <param name="targetAssemblyNames">Used to check if the entry point or it's transitive assemblies reference these assembly names</param>
|
||||
/// <param name="includeTargets">If true will also use the target assembly names as entry point assemblies</param>
|
||||
public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets)
|
||||
{
|
||||
_referenceAssemblies = referenceAssemblies;
|
||||
_targetAssemblies = targetAssemblyNames;
|
||||
_includeTargets = includeTargets;
|
||||
}
|
||||
|
||||
public IEnumerable<Assembly> Find()
|
||||
{
|
||||
var referenceItems = new List<Assembly>();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
13
src/Umbraco.Core/Composing/IAssemblyProvider.cs
Normal file
13
src/Umbraco.Core/Composing/IAssemblyProvider.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a list of assemblies that can be scanned
|
||||
/// </summary>
|
||||
public interface IAssemblyProvider
|
||||
{
|
||||
IEnumerable<Assembly> Assemblies { get; }
|
||||
}
|
||||
}
|
||||
173
src/Umbraco.Core/Composing/ReferenceResolver.cs
Normal file
173
src/Umbraco.Core/Composing/ReferenceResolver.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs
|
||||
/// </remarks>
|
||||
internal class ReferenceResolver
|
||||
{
|
||||
private readonly HashSet<string> _umbracoAssemblies;
|
||||
private readonly IReadOnlyList<Assembly> _assemblies;
|
||||
private readonly Dictionary<Assembly, Classification> _classifications;
|
||||
private readonly List<Assembly> _lookup = new List<Assembly>();
|
||||
|
||||
public ReferenceResolver(IReadOnlyList<string> targetAssemblies, IReadOnlyList<Assembly> entryPointAssemblies)
|
||||
{
|
||||
_umbracoAssemblies = new HashSet<string>(targetAssemblies, StringComparer.Ordinal);
|
||||
_assemblies = entryPointAssemblies;
|
||||
_classifications = new Dictionary<Assembly, Classification>();
|
||||
|
||||
foreach (var item in entryPointAssemblies)
|
||||
{
|
||||
_lookup.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of assemblies that directly reference or transitively reference the targetAssemblies
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This includes all assemblies in the same location as the entry point assemblies
|
||||
/// </remarks>
|
||||
public IEnumerable<Assembly> ResolveAssemblies()
|
||||
{
|
||||
var applicationParts = new List<Assembly>();
|
||||
|
||||
var assemblies = new HashSet<Assembly>(_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<string> GetAssemblyFolders(IEnumerable<Assembly> 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<Assembly> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
/// <inheritdoc cref="ITypeFinder"/>
|
||||
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<string>();
|
||||
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
|
||||
{
|
||||
HashSet<Assembly> 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<Assembly>();
|
||||
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<HashSet<Assembly>> _allAssemblies;
|
||||
private readonly IAssemblyProvider _assemblyProvider;
|
||||
private volatile HashSet<Assembly> _localFilteredAssemblyCache;
|
||||
private readonly object _localFilteredAssemblyCacheLocker = new object();
|
||||
private readonly List<string> _notifiedLoadExceptionAssemblies = new List<string>();
|
||||
private static readonly ConcurrentDictionary<string, Type> TypeNamesCache= new ConcurrentDictionary<string, Type>();
|
||||
private string _rootDir = "";
|
||||
private static readonly ConcurrentDictionary<string, Type> TypeNamesCache = new ConcurrentDictionary<string, Type>();
|
||||
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<string>();
|
||||
}
|
||||
|
||||
private bool AcceptsLoadExceptions(Assembly a)
|
||||
@@ -119,22 +48,8 @@ namespace Umbraco.Core.Composing
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
private IEnumerable<Assembly> GetAllAssemblies()
|
||||
{
|
||||
return _allAssemblies.Value;
|
||||
}
|
||||
|
||||
private IEnumerable<Assembly> GetAllAssemblies() => _assemblyProvider.Assemblies;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Assembly> 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"
|
||||
/// </remarks>
|
||||
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",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -290,6 +215,11 @@ namespace Umbraco.Core.Composing
|
||||
/// <returns></returns>
|
||||
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<Assembly>();
|
||||
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<Type> 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<Assembly>();
|
||||
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<Type> 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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
36
src/Umbraco.Core/Composing/TypeFinderConfig.cs
Normal file
36
src/Umbraco.Core/Composing/TypeFinderConfig.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// TypeFinder config via appSettings
|
||||
/// </summary>
|
||||
internal class TypeFinderConfig : ITypeFinderConfig
|
||||
{
|
||||
private readonly ITypeFinderSettings _settings;
|
||||
private IEnumerable<string> _assembliesAcceptingLoadExceptions;
|
||||
|
||||
public TypeFinderConfig(ITypeFinderSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AssembliesAcceptingLoadExceptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_assembliesAcceptingLoadExceptions != null)
|
||||
return _assembliesAcceptingLoadExceptions;
|
||||
|
||||
var s = _settings.AssembliesAcceptingLoadExceptions;
|
||||
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
|
||||
? Array.Empty<string>()
|
||||
: s.Split(',').Select(x => x.Trim()).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
/// </remarks>
|
||||
public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable<Assembly> assemblies)
|
||||
public static IReadOnlyList<Assembly> GetReferencingAssemblies(Assembly assembly, IEnumerable<Assembly> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -516,29 +516,29 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
#region Get Assembly Attributes
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly attributes of the specified type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The attribute type.</typeparam>
|
||||
/// <returns>
|
||||
/// The assembly attributes of the specified type <typeparamref name="T" />.
|
||||
/// </returns>
|
||||
public IEnumerable<T> GetAssemblyAttributes<T>()
|
||||
where T : Attribute
|
||||
{
|
||||
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes<T>()).ToList();
|
||||
}
|
||||
///// <summary>
|
||||
///// Gets the assembly attributes of the specified type <typeparamref name="T" />.
|
||||
///// </summary>
|
||||
///// <typeparam name="T">The attribute type.</typeparam>
|
||||
///// <returns>
|
||||
///// The assembly attributes of the specified type <typeparamref name="T" />.
|
||||
///// </returns>
|
||||
//public IEnumerable<T> GetAssemblyAttributes<T>()
|
||||
// where T : Attribute
|
||||
//{
|
||||
// return AssembliesToScan.SelectMany(a => a.GetCustomAttributes<T>()).ToList();
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the assembly attributes.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// All assembly attributes.
|
||||
/// </returns>
|
||||
public IEnumerable<Attribute> GetAssemblyAttributes()
|
||||
{
|
||||
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList();
|
||||
}
|
||||
///// <summary>
|
||||
///// Gets all the assembly attributes.
|
||||
///// </summary>
|
||||
///// <returns>
|
||||
///// All assembly attributes.
|
||||
///// </returns>
|
||||
//public IEnumerable<Attribute> GetAssemblyAttributes()
|
||||
//{
|
||||
// return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList();
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly attributes of the specified <paramref name="attributeTypes" />.
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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()));
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
30
src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs
Normal file
30
src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs
Normal file
@@ -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<IDiscoverable>().Count();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void WithoutGetReferencingAssembliesCheck()
|
||||
{
|
||||
var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
|
||||
typeFinder2.QueryWithReferencingAssemblies = false;
|
||||
var found = typeFinder2.FindClassesOfType<IDiscoverable>().Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@
|
||||
<Compile Include="SqlTemplatesBenchmark.cs" />
|
||||
<Compile Include="StringReplaceManyBenchmarks.cs" />
|
||||
<Compile Include="TryConvertToBenchmarks.cs" />
|
||||
<Compile Include="TypeFinderBenchmarks.cs" />
|
||||
<Compile Include="XmlBenchmarks.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
_memberCache = new ObjectCacheAppCache(typeFinder);
|
||||
|
||||
_provider = new DeepCloneAppCache(_memberCache);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
_ctx = new FakeHttpContextFactory("http://localhost/test");
|
||||
_appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder);
|
||||
}
|
||||
|
||||
@@ -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<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
_provider = new ObjectCacheAppCache(typeFinder);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components
|
||||
var mock = new Mock<IFactory>();
|
||||
|
||||
var logger = Mock.Of<ILogger>();
|
||||
var typeFinder = new TypeFinder(logger);
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var f = new UmbracoDatabaseFactory(logger, new Lazy<IMapperCollection>(() => new MapperCollection(Enumerable.Empty<BaseMapper>())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator);
|
||||
var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings());
|
||||
var coreDebug = Mock.Of<ICoreDebug>();
|
||||
@@ -371,14 +371,15 @@ namespace Umbraco.Tests.Components
|
||||
public void AllComposers()
|
||||
{
|
||||
var ioHelper = TestHelper.IOHelper;
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());
|
||||
|
||||
var register = MockRegister();
|
||||
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(),
|
||||
MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache);
|
||||
|
||||
var types = typeLoader.GetTypes<IComposer>().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web"));
|
||||
var allComposers = typeLoader.GetTypes<IComposer>().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<Attribute>(), Mock.Of<IProfilingLogger>());
|
||||
var requirements = composers.GetRequirements();
|
||||
var report = Composers.GetComposersReport(requirements);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing
|
||||
{
|
||||
ProfilingLogger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing
|
||||
.Returns(() => factoryFactory?.Invoke(mockedFactory));
|
||||
|
||||
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var ioHelper = TestHelper.IOHelper;
|
||||
var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of<IAppPolicyCache>(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger);
|
||||
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache);
|
||||
|
||||
@@ -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<TestEditor, MyTestAttribute>(_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<TreeAttribute>(_assemblies);
|
||||
Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree]
|
||||
|
||||
typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>(new[] { typeof (UmbracoContext).Assembly });
|
||||
Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
|
||||
|
||||
typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>();
|
||||
Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
|
||||
}
|
||||
|
||||
private static IProfilingLogger GetTestProfilingLogger()
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing
|
||||
public void Initialize()
|
||||
{
|
||||
// this ensures it's reset
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
_typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance,
|
||||
new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), false,
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
//we DO want cache enabled for these tests
|
||||
var cacheHelper = new AppCaches(
|
||||
new ObjectCacheAppCache(typeFinder),
|
||||
|
||||
@@ -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<ILogger>()));
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var runtimeCache = new ObjectCacheAppCache(typeFinder);
|
||||
runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content);
|
||||
|
||||
var proflog = GetTestProfilingLogger();
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published
|
||||
|
||||
var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes);
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var elementsCache = new FastDictionaryAppCache(typeFinder);
|
||||
var snapshotCache = new FastDictionaryAppCache(typeFinder);
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
// create a data source for NuCache
|
||||
_source = new TestDataSource(kits);
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var settings = Mock.Of<INuCacheSettings>();
|
||||
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
// create a variation accessor
|
||||
_variationAccesor = new TestVariationContextAccessor();
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var settings = Mock.Of<INuCacheSettings>();
|
||||
|
||||
// at last, create the complete NuCache snapshot service!
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<IMapperCollection>(() => factory.GetInstance<IMapperCollection>()), 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<IHostingEnvironment>();
|
||||
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<IUmbracoDatabaseFactory>();
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
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<IRuntimeState>();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping
|
||||
var memberRepository = Mock.Of<IMemberRepository>();
|
||||
var hostingEnvironment = TestHelper.GetHostingEnvironment();
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var settings = Mock.Of<INuCacheSettings>();
|
||||
|
||||
return new PublishedSnapshotService(
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services
|
||||
var memberRepository = Mock.Of<IMemberRepository>();
|
||||
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var settings = Mock.Of<INuCacheSettings>();
|
||||
|
||||
return new PublishedSnapshotService(
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
var ioHelper = TestHelper.IOHelper;
|
||||
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance,
|
||||
new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")),
|
||||
logger,
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs
|
||||
{
|
||||
if (_factory != null) return _factory(requestContext);
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var types = typeFinder.FindClassesOfType<ControllerBase>(new[] { Assembly.GetExecutingAssembly() });
|
||||
|
||||
var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
@@ -43,6 +43,14 @@ namespace Umbraco.Tests.TestHelpers
|
||||
public static class TestHelper
|
||||
{
|
||||
|
||||
public static ITypeFinder GetTypeFinder()
|
||||
{
|
||||
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>(),
|
||||
new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly));
|
||||
return typeFinder;
|
||||
}
|
||||
|
||||
public static TypeLoader GetMockedTypeLoader()
|
||||
{
|
||||
return new TypeLoader(IOHelper, Mock.Of<ITypeFinder>(), Mock.Of<IAppPolicyCache>(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy<IMapperCollection>(() => 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<IMediaFileSystem>();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<IFactory>();
|
||||
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
|
||||
var typeFinder = TestHelper.GetTypeFinder();
|
||||
var ioHelper = TestHelper.IOHelper;
|
||||
container
|
||||
.Setup(x => x.GetInstance(typeof(TypeLoader)))
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This finder will also try to resolve dynamic assemblies created from App_Code
|
||||
/// </remarks>
|
||||
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<HashSet<Assembly>>(() =>
|
||||
{
|
||||
var isHosted = hostingEnvironment.IsHosted;
|
||||
try
|
||||
{
|
||||
if (isHosted)
|
||||
{
|
||||
var assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
|
||||
|
||||
//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<Assembly>(base.AssembliesToScan);
|
||||
});
|
||||
}
|
||||
|
||||
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly implement and return result from BuildManager
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false);
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly implement and return result from BuildManager
|
||||
/// </summary>
|
||||
IEnumerable<Assembly> ITypeFinder.AssembliesToScan => _allAssemblies.Value;
|
||||
|
||||
/// <summary>
|
||||
/// TypeFinder config via appSettings
|
||||
/// </summary>
|
||||
internal class TypeFinderConfig : ITypeFinderConfig
|
||||
{
|
||||
private readonly ITypeFinderSettings _settings;
|
||||
private IEnumerable<string> _assembliesAcceptingLoadExceptions;
|
||||
|
||||
public TypeFinderConfig(ITypeFinderSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AssembliesAcceptingLoadExceptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_assembliesAcceptingLoadExceptions != null)
|
||||
return _assembliesAcceptingLoadExceptions;
|
||||
|
||||
var s = _settings.AssembliesAcceptingLoadExceptions;
|
||||
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
|
||||
? Array.Empty<string>()
|
||||
: s.Split(',').Select(x => x.Trim()).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,6 @@ namespace Umbraco.Web.Runtime
|
||||
/// <remarks>On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco.</remarks>
|
||||
public class WebRuntime : CoreRuntime
|
||||
{
|
||||
private BuildManagerTypeFinder _typeFinder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebRuntime"/> class.
|
||||
/// </summary>
|
||||
@@ -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)
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
<Compile Include="Compose\AuditEventsComposer.cs" />
|
||||
<Compile Include="Compose\BackOfficeUserAuditEventsComponent.cs" />
|
||||
<Compile Include="Compose\BackOfficeUserAuditEventsComposer.cs" />
|
||||
<Compile Include="Composing\BuildManagerTypeFinder.cs" />
|
||||
<Compile Include="Composing\CompositionExtensions\Installer.cs" />
|
||||
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />
|
||||
<Compile Include="Models\NoNodesViewModel.cs" />
|
||||
|
||||
Reference in New Issue
Block a user