Creates ITypeFinder interface and simplifies type finder with TypeFinderExtensions, moves GetTypeByName to TypeHelper using assembly name parsing instead of scanning every assembly.

This commit is contained in:
Shannon
2019-11-08 14:26:06 +11:00
parent 05569440c4
commit 6398f1e216
25 changed files with 377 additions and 774 deletions

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Core.Composing
{
public interface ITypeFinder
{
/// <summary>
/// Return a list of found local Assemblies that Umbraco should scan for type finding
/// </summary>
/// <value></value>
IEnumerable<Assembly> AssembliesToScan { get; }
/// <summary>
/// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute
/// </summary>
/// <param name="assignTypeFrom"></param>
/// <param name="attributeType"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
IEnumerable<Type> FindClassesOfTypeWithAttribute(
Type assignTypeFrom,
Type attributeType,
IEnumerable<Assembly> assemblies = null,
bool onlyConcreteClasses = true);
/// <summary>
/// Returns all types found of in the assemblies specified of type T
/// </summary>
/// <param name="assignTypeFrom"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies = null, bool onlyConcreteClasses = true);
/// <summary>
/// Finds any classes with the attribute.
/// </summary>
/// <param name="attributeType">The attribute type </param>
/// <param name="assemblies">The assemblies.</param>
/// <param name="onlyConcreteClasses">if set to <c>true</c> only concrete classes.</param>
/// <returns></returns>
IEnumerable<Type> FindClassesWithAttribute(
Type attributeType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses);
}
}

View File

@@ -6,11 +6,9 @@ using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
{
@@ -18,14 +16,107 @@ namespace Umbraco.Core.Composing
/// A utility class to find all classes of a certain type by reflection in the current bin folder
/// of the web application.
/// </summary>
public static class TypeFinder
public class TypeFinder : ITypeFinder
{
private static volatile HashSet<Assembly> _localFilteredAssemblyCache;
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
private static readonly List<string> NotifiedLoadExceptionAssemblies = new List<string>();
private static string[] _assembliesAcceptingLoadExceptions;
private readonly ILogger _logger;
private static string[] AssembliesAcceptingLoadExceptions
public TypeFinder(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
HashSet<Assembly> assemblies = null;
try
{
var isHosted = IOHelper.IsHosted;
try
{
if (isHosted)
{
assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
if (assemblies == null)
{
//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 = IOHelper.GetRootDirectoryBinFolder();
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);
}
//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;
}
return assemblies;
});
}
//Lazy access to the all assemblies list
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
private volatile HashSet<Assembly> _localFilteredAssemblyCache;
private readonly object _localFilteredAssemblyCacheLocker = new object();
private readonly List<string> _notifiedLoadExceptionAssemblies = new List<string>();
private string[] _assembliesAcceptingLoadExceptions;
private string[] AssembliesAcceptingLoadExceptions
{
get
{
@@ -39,7 +130,7 @@ namespace Umbraco.Core.Composing
}
}
private static bool AcceptsLoadExceptions(Assembly a)
private bool AcceptsLoadExceptions(Assembly a)
{
if (AssembliesAcceptingLoadExceptions.Length == 0)
return false;
@@ -67,118 +158,25 @@ namespace Umbraco.Core.Composing
/// 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>
internal static HashSet<Assembly> GetAllAssemblies()
private IEnumerable<Assembly> GetAllAssemblies()
{
return AllAssemblies.Value;
return _allAssemblies.Value;
}
//Lazy access to the all assemblies list
private static readonly Lazy<HashSet<Assembly>> AllAssemblies = new Lazy<HashSet<Assembly>>(() =>
/// <inheritdoc />
public IEnumerable<Assembly> AssembliesToScan
{
HashSet<Assembly> assemblies = null;
try
get
{
var isHosted = IOHelper.IsHosted;
try
lock (_localFilteredAssemblyCacheLocker)
{
if (isHosted)
{
assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
if (_localFilteredAssemblyCache != null)
return _localFilteredAssemblyCache;
if (assemblies == null)
{
//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 = IOHelper.GetRootDirectoryBinFolder();
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;
}
}
}
}
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
if (assemblies.Any() == false)
{
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
assemblies.Add(a);
}
}
//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
Current.Logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code");
}
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
return assemblies;
});
/// <summary>
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
/// and excluding the ones passed in and excluding the exclusion list filter, the results of this are
/// cached for performance reasons.
/// </summary>
/// <param name="excludeFromResults"></param>
/// <returns></returns>
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
IEnumerable<Assembly> excludeFromResults = null)
{
lock (LocalFilteredAssemblyCacheLocker)
{
if (_localFilteredAssemblyCache != null)
var assemblies = GetFilteredAssemblies(null, KnownAssemblyExclusionFilter);
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
return _localFilteredAssemblyCache;
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
return _localFilteredAssemblyCache;
}
}
}
@@ -188,7 +186,7 @@ namespace Umbraco.Core.Composing
/// <param name="excludeFromResults"></param>
/// <param name="exclusionFilter"></param>
/// <returns></returns>
private static IEnumerable<Assembly> GetFilteredAssemblies(
private IEnumerable<Assembly> GetFilteredAssemblies(
IEnumerable<Assembly> excludeFromResults = null,
string[] exclusionFilter = null)
{
@@ -210,7 +208,7 @@ 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>
internal static readonly string[] KnownAssemblyExclusionFilter = {
private static readonly string[] KnownAssemblyExclusionFilter = {
"Antlr3.",
"AutoMapper,",
"AutoMapper.",
@@ -261,115 +259,40 @@ namespace Umbraco.Core.Composing
};
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute
/// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TAttribute"></typeparam>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>()
where TAttribute : Attribute
{
return FindClassesOfTypeWithAttribute<T, TAttribute>(GetAssembliesWithKnownExclusions(), true);
}
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="assemblies"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>(IEnumerable<Assembly> assemblies)
where TAttribute : Attribute
{
return FindClassesOfTypeWithAttribute<T, TAttribute>(assemblies, true);
}
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="assignTypeFrom"></param>
/// <param name="attributeType"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>(
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
where TAttribute : Attribute
public IEnumerable<Type> FindClassesOfTypeWithAttribute(
Type assignTypeFrom,
Type attributeType,
IEnumerable<Assembly> assemblies = null,
bool onlyConcreteClasses = true)
{
return FindClassesOfTypeWithAttribute<TAttribute>(typeof(T), assemblies, onlyConcreteClasses);
var assemblyList = (assemblies ?? AssembliesToScan).ToList();
return GetClassesWithBaseType(assignTypeFrom, assemblyList, onlyConcreteClasses,
//the additional filter will ensure that any found types also have the attribute applied.
t => t.GetCustomAttributes(attributeType, false).Any());
}
/// <summary>
/// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute
/// Returns all types found of in the assemblies specified of type T
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="assignTypeFrom"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<TAttribute>(
Type assignTypeFrom,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
where TAttribute : Attribute
public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies = null, bool onlyConcreteClasses = true)
{
if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
var assemblyList = (assemblies ?? AssembliesToScan).ToList();
return GetClassesWithBaseType(assignTypeFrom, assemblies, onlyConcreteClasses,
//the additional filter will ensure that any found types also have the attribute applied.
t => t.GetCustomAttributes<TAttribute>(false).Any());
return GetClassesWithBaseType(assignTypeFrom, assemblyList, onlyConcreteClasses);
}
/// <summary>
/// Searches all filtered local assemblies specified for classes of the type passed in.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfType<T>()
{
return FindClassesOfType<T>(GetAssembliesWithKnownExclusions(), true);
}
/// <summary>
/// Returns all types found of in the assemblies specified of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
{
if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses);
}
/// <summary>
/// Returns all types found of in the assemblies specified of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies)
{
return FindClassesOfType<T>(assemblies, true);
}
/// <summary>
/// Finds the classes with attribute.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies">The assemblies.</param>
/// <param name="onlyConcreteClasses">if set to <c>true</c> only concrete classes.</param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
where T : Attribute
{
return FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses);
}
/// <summary>
/// Finds any classes with the attribute.
/// </summary>
@@ -377,40 +300,19 @@ namespace Umbraco.Core.Composing
/// <param name="assemblies">The assemblies.</param>
/// <param name="onlyConcreteClasses">if set to <c>true</c> only concrete classes.</param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute(
public IEnumerable<Type> FindClassesWithAttribute(
Type attributeType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
IEnumerable<Assembly> assemblies = null,
bool onlyConcreteClasses = true)
{
return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses);
}
var assemblyList = (assemblies ?? AssembliesToScan).ToList();
/// <summary>
/// Finds the classes with attribute.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies">The assemblies.</param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute<T>(IEnumerable<Assembly> assemblies)
where T : Attribute
{
return FindClassesWithAttribute<T>(assemblies, true);
}
/// <summary>
/// Finds the classes with attribute in filtered local assemblies
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute<T>()
where T : Attribute
{
return FindClassesWithAttribute<T>(GetAssembliesWithKnownExclusions());
return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses);
}
#region Private methods
private static IEnumerable<Type> GetClassesWithAttribute(
private IEnumerable<Type> GetClassesWithAttribute(
Type attributeType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
@@ -441,7 +343,7 @@ namespace Umbraco.Core.Composing
}
catch (TypeLoadException ex)
{
Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly);
_logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly);
continue;
}
@@ -477,7 +379,7 @@ namespace Umbraco.Core.Composing
/// <param name="onlyConcreteClasses"></param>
/// <param name="additionalFilter">An additional filter to apply for what types will actually be included in the return value</param>
/// <returns></returns>
private static IEnumerable<Type> GetClassesWithBaseType(
private IEnumerable<Type> GetClassesWithBaseType(
Type baseType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses,
@@ -507,7 +409,7 @@ namespace Umbraco.Core.Composing
}
catch (TypeLoadException ex)
{
Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly);
_logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly);
continue;
}
@@ -533,7 +435,7 @@ namespace Umbraco.Core.Composing
return types;
}
internal static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
private IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
{
//if the assembly is dynamic, do not try to scan it
if (a.IsDynamic)
@@ -569,12 +471,12 @@ namespace Umbraco.Core.Composing
if (AcceptsLoadExceptions(a) == false) throw ex;
// log a warning, and return what we can
lock (NotifiedLoadExceptionAssemblies)
lock (_notifiedLoadExceptionAssemblies)
{
if (NotifiedLoadExceptionAssemblies.Contains(a.FullName) == false)
if (_notifiedLoadExceptionAssemblies.Contains(a.FullName) == false)
{
NotifiedLoadExceptionAssemblies.Add(a.FullName);
Current.Logger.Warn(typeof (TypeFinder), ex, "Could not load all types from {TypeName}.", a.GetName().Name);
_notifiedLoadExceptionAssemblies.Add(a.FullName);
_logger.Warn(typeof (TypeFinder), ex, "Could not load all types from {TypeName}.", a.GetName().Name);
}
}
return rex.Types.WhereNotNull().ToArray();
@@ -595,8 +497,7 @@ namespace Umbraco.Core.Composing
sb.Append(". ");
sb.Append(loaderException.GetType().FullName);
var tloadex = loaderException as TypeLoadException;
if (tloadex != null)
if (loaderException is TypeLoadException tloadex)
{
sb.Append(" on ");
sb.Append(tloadex.TypeName);
@@ -609,24 +510,5 @@ namespace Umbraco.Core.Composing
#endregion
public static Type GetTypeByName(string typeName)
{
var type = BuildManager.GetType(typeName, false);
if (type != null) return type;
// TODO: This isn't very elegant, and will have issues since the AppDomain.CurrentDomain
// doesn't actualy load in all assemblies, only the types that have been referenced so far.
// However, in a web context, the BuildManager will have executed which will force all assemblies
// to be loaded so it's fine for now.
// It could be fairly easy to parse the typeName to get the assembly name and then do Assembly.Load and
// find the type from there.
//now try fall back procedures.
type = Type.GetType(typeName);
if (type != null) return type;
return AppDomain.CurrentDomain.GetAssemblies()
.Select(x => x.GetType(typeName))
.FirstOrDefault(x => x != null);
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Core.Composing
{
public static class TypeFinderExtensions
{
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="typeFinder"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>(this ITypeFinder typeFinder, IEnumerable<Assembly> assemblies = null, bool onlyConcreteClasses = true)
where TAttribute : Attribute
=> typeFinder.FindClassesOfTypeWithAttribute(typeof(T), typeof(TAttribute), assemblies, onlyConcreteClasses);
/// <summary>
/// Returns all types found of in the assemblies specified of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="typeFinder"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesOfType<T>(this ITypeFinder typeFinder, IEnumerable<Assembly> assemblies = null, bool onlyConcreteClasses = true)
=> typeFinder.FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses);
/// <summary>
/// Finds the classes with attribute.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="typeFinder"></param>
/// <param name="assemblies">The assemblies.</param>
/// <param name="onlyConcreteClasses">if set to <c>true</c> only concrete classes.</param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute<T>(this ITypeFinder typeFinder, IEnumerable<Assembly> assemblies = null, bool onlyConcreteClasses = true)
where T : Attribute
=> typeFinder.FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses);
}
}

View File

@@ -20,7 +20,7 @@ namespace Umbraco.Core.Composing
/// Provides methods to find and instantiate types.
/// </summary>
/// <remarks>
/// <para>This class should be used to get all types, the <see cref="TypeFinder"/> class should never be used directly.</para>
/// <para>This class should be used to get all types, the <see cref="Composing.TypeFinder"/> class should never be used directly.</para>
/// <para>In most cases this class is not used directly but through extension methods that retrieve specific types.</para>
/// <para>This class caches the types it knows to avoid excessive assembly scanning and shorten startup times, relying
/// on a hash of the DLLs in the ~/bin folder to check for cache expiration.</para>
@@ -48,22 +48,25 @@ namespace Umbraco.Core.Composing
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="typeFinder"></param>
/// <param name="runtimeCache">The application runtime cache.</param>
/// <param name="localTempPath">Files storage location.</param>
/// <param name="logger">A profiling logger.</param>
public TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger)
: this(runtimeCache, localTempPath, logger, true)
public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger)
: this(typeFinder, runtimeCache, localTempPath, logger, true)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="typeFinder"></param>
/// <param name="runtimeCache">The application runtime cache.</param>
/// <param name="localTempPath">Files storage location.</param>
/// <param name="logger">A profiling logger.</param>
/// <param name="detectChanges">Whether to detect changes using hashes.</param>
internal TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger, bool detectChanges)
internal TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger, bool detectChanges)
{
TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder));
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
_localTempPath = localTempPath;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -105,6 +108,12 @@ namespace Umbraco.Core.Composing
internal TypeLoader()
{ }
/// <summary>
/// Returns the underlying <see cref="ITypeFinder"/>
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
public ITypeFinder TypeFinder { get; }
/// <summary>
/// Gets or sets the set of assemblies to scan.
/// </summary>
@@ -117,7 +126,7 @@ namespace Umbraco.Core.Composing
// internal for tests
internal IEnumerable<Assembly> AssembliesToScan
{
get => _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions());
get => _assemblies ?? (_assemblies = TypeFinder.AssembliesToScan);
set => _assemblies = value;
}