Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB3649-move-manifest-stuff

This commit is contained in:
Bjarke Berg
2019-11-12 10:42:48 +01:00
64 changed files with 805 additions and 1002 deletions
@@ -10,24 +10,33 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Implements a fast <see cref="IAppCache"/> on top of a concurrent dictionary.
/// </summary>
internal class FastDictionaryAppCache : IAppCache
public class FastDictionaryAppCache : IAppCache
{
private readonly ITypeFinder _typeFinder;
public FastDictionaryAppCache(ITypeFinder typeFinder)
{
_typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder));
}
/// <summary>
/// Gets the internal items dictionary, for tests only!
/// </summary>
internal readonly ConcurrentDictionary<string, Lazy<object>> Items = new ConcurrentDictionary<string, Lazy<object>>();
private readonly ConcurrentDictionary<string, Lazy<object>> _items = new ConcurrentDictionary<string, Lazy<object>>();
public int Count => _items.Count;
/// <inheritdoc />
public object Get(string cacheKey)
{
Items.TryGetValue(cacheKey, out var result); // else null
_items.TryGetValue(cacheKey, out var result); // else null
return result == null ? null : SafeLazy.GetSafeLazyValue(result); // return exceptions as null
}
/// <inheritdoc />
public object Get(string cacheKey, Func<object> getCacheItem)
{
var result = Items.GetOrAdd(cacheKey, k => SafeLazy.GetSafeLazy(getCacheItem));
var result = _items.GetOrAdd(cacheKey, k => SafeLazy.GetSafeLazy(getCacheItem));
var value = result.Value; // will not throw (safe lazy)
if (!(value is SafeLazy.ExceptionHolder eh))
@@ -37,7 +46,7 @@ namespace Umbraco.Core.Cache
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
// in order NOT to cache exceptions
Items.TryRemove(cacheKey, out result);
_items.TryRemove(cacheKey, out result);
eh.Exception.Throw(); // throw once!
return null; // never reached
}
@@ -45,7 +54,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
return Items
return _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
.Select(kvp => SafeLazy.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
@@ -55,7 +64,7 @@ namespace Umbraco.Core.Cache
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
return Items
return _items
.Where(kvp => compiled.IsMatch(kvp.Key))
.Select(kvp => SafeLazy.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
@@ -64,23 +73,23 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public void Clear()
{
Items.Clear();
_items.Clear();
}
/// <inheritdoc />
public void Clear(string key)
{
Items.TryRemove(key, out _);
_items.TryRemove(key, out _);
}
/// <inheritdoc />
public void ClearOfType(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
var type = _typeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
@@ -92,7 +101,7 @@ namespace Umbraco.Core.Cache
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
@@ -101,7 +110,7 @@ namespace Umbraco.Core.Cache
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
@@ -114,7 +123,7 @@ namespace Umbraco.Core.Cache
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
@@ -123,7 +132,7 @@ namespace Umbraco.Core.Cache
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
@@ -139,24 +148,24 @@ namespace Umbraco.Core.Cache
// run predicate on the 'public key' part only, ie without prefix
&& predicate(x.Key, (T)value);
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearByKey(string keyStartsWith)
{
foreach (var ikvp in Items
foreach (var ikvp in _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
Items.TryRemove(ikvp.Key, out _);
_items.TryRemove(ikvp.Key, out _);
}
/// <inheritdoc />
public void ClearByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
foreach (var ikvp in Items
foreach (var ikvp in _items
.Where(kvp => compiled.IsMatch(kvp.Key)))
Items.TryRemove(ikvp.Key, out _);
_items.TryRemove(ikvp.Key, out _);
}
}
}
@@ -10,8 +10,15 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Provides a base class to fast, dictionary-based <see cref="IAppCache"/> implementations.
/// </summary>
internal abstract class FastDictionaryAppCacheBase : IAppCache
public abstract class FastDictionaryAppCacheBase : IAppCache
{
private readonly ITypeFinder _typeFinder;
protected FastDictionaryAppCacheBase(ITypeFinder typeFinder)
{
_typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder));
}
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
@@ -116,7 +123,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
var type = _typeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
try
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
@@ -15,37 +16,29 @@ namespace Umbraco.Core.Cache
/// or no Items...) then this cache acts as a pass-through and does not cache
/// anything.</para>
/// </remarks>
internal class HttpRequestAppCache : FastDictionaryAppCacheBase
public class HttpRequestAppCache : FastDictionaryAppCacheBase
{
private readonly HttpContextBase _context;
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
/// </summary>
public HttpRequestAppCache(HttpContextBase context)
public HttpRequestAppCache(Func<IDictionary> requestItems, ITypeFinder typeFinder) : base(typeFinder)
{
_context = context;
ContextItems = requestItems;
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class.
/// </summary>
/// <remarks>
/// <para>Will use HttpContext.Current.</para>
/// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current
/// </remarks>
public HttpRequestAppCache()
{ }
private Func<IDictionary> ContextItems { get; }
private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items;
private bool HasContextItems => _context?.Items != null || HttpContext.Current != null;
private bool TryGetContextItems(out IDictionary items)
{
items = ContextItems?.Invoke();
return items != null;
}
/// <inheritdoc />
public override object Get(string key, Func<object> factory)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return factory();
if (!TryGetContextItems(out var items)) return factory();
key = GetCacheKey(key);
@@ -54,7 +47,7 @@ namespace Umbraco.Core.Cache
try
{
EnterWriteLock();
result = ContextItems[key] as Lazy<object>; // null if key not found
result = items[key] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
@@ -63,7 +56,7 @@ namespace Umbraco.Core.Cache
if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = SafeLazy.GetSafeLazy(factory);
ContextItems[key] = result;
items[key] = result;
}
}
finally
@@ -89,22 +82,22 @@ namespace Umbraco.Core.Cache
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
if (!TryGetContextItems(out var items)) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
return items.Cast<DictionaryEntry>()
.Where(x => x.Key is string s && s.StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
if (HasContextItems == false) return;
if (!TryGetContextItems(out var items)) return;
ContextItems.Remove(key);
items.Remove(key);
}
protected override object GetEntry(string key)
{
return HasContextItems ? ContextItems[key] : null;
return !TryGetContextItems(out var items) ? null : items[key];
}
#endregion
@@ -117,26 +110,27 @@ namespace Umbraco.Core.Cache
protected override void EnterWriteLock()
{
if (HasContextItems)
{
// note: cannot keep 'entered' as a class variable here,
// since there is one per request - so storing it within
// ContextItems - which is locked, so this should be safe
if (!TryGetContextItems(out var items)) return;
var entered = false;
Monitor.Enter(ContextItems.SyncRoot, ref entered);
ContextItems[ContextItemsLockKey] = entered;
}
// note: cannot keep 'entered' as a class variable here,
// since there is one per request - so storing it within
// ContextItems - which is locked, so this should be safe
var entered = false;
Monitor.Enter(items.SyncRoot, ref entered);
items[ContextItemsLockKey] = entered;
}
protected override void ExitReadLock() => ExitWriteLock();
protected override void ExitWriteLock()
{
var entered = (bool?) ContextItems[ContextItemsLockKey] ?? false;
if (!TryGetContextItems(out var items)) return;
var entered = (bool?)items[ContextItemsLockKey] ?? false;
if (entered)
Monitor.Exit(ContextItems.SyncRoot);
ContextItems.Remove(ContextItemsLockKey);
Monitor.Exit(items.SyncRoot);
items.Remove(ContextItemsLockKey);
}
#endregion
@@ -13,13 +13,15 @@ namespace Umbraco.Core.Cache
/// </summary>
public class ObjectCacheAppCache : IAppPolicyCache
{
private readonly ITypeFinder _typeFinder;
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <summary>
/// Initializes a new instance of the <see cref="ObjectCacheAppCache"/>.
/// </summary>
public ObjectCacheAppCache()
public ObjectCacheAppCache(ITypeFinder typeFinder)
{
_typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder));
// the MemoryCache is created with name "in-memory". That name is
// used to retrieve configuration options. It does not identify the memory cache, i.e.
// each instance of this class has its own, independent, memory cache.
@@ -29,7 +31,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Gets the internal memory cache, for tests only!
/// </summary>
internal ObjectCache MemoryCache { get; private set; }
public ObjectCache MemoryCache { get; private set; }
/// <inheritdoc />
public object Get(string key)
@@ -178,7 +180,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
var type = _typeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
try
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Used to find objects by implemented types, names and/or attributes
/// </summary>
public interface ITypeFinder
{
Type GetTypeByName(string name);
/// <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);
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
@@ -6,98 +7,30 @@ 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.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
{
/// <summary>
/// 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
/// <inheritdoc cref="ITypeFinder"/>
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, ITypeFinderConfig typeFinderConfig = null)
{
get
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty<string>();
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
if (_assembliesAcceptingLoadExceptions != null)
return _assembliesAcceptingLoadExceptions;
var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions];
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
? Array.Empty<string>()
: s.Split(',').Select(x => x.Trim()).ToArray();
}
}
private static bool AcceptsLoadExceptions(Assembly a)
{
if (AssembliesAcceptingLoadExceptions.Length == 0)
return false;
if (AssembliesAcceptingLoadExceptions.Length == 1 && AssembliesAcceptingLoadExceptions[0] == "*")
return true;
var name = a.GetName().Name; // simple name of the assembly
return AssembliesAcceptingLoadExceptions.Any(pattern =>
{
if (pattern.Length > name.Length) return false; // pattern longer than name
if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical
if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot
return name.StartsWith(pattern); // and name must start with pattern
});
}
/// <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>
internal static HashSet<Assembly> GetAllAssemblies()
{
return AllAssemblies.Value;
}
//Lazy access to the all assemblies list
private static readonly Lazy<HashSet<Assembly>> AllAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
HashSet<Assembly> assemblies = null;
try
{
var isHosted = Current.IOHelper.IsHosted;
HashSet<Assembly> assemblies = null;
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 = Current.IOHelper.GetRootDirectoryBinFolder();
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();
@@ -122,63 +55,101 @@ namespace Umbraco.Core.Composing
}
}
}
}
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
if (assemblies.Any() == false)
{
//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(Current.IOHelper.MapPath(Current.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()))
catch (InvalidOperationException e)
{
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");
}
if (e.InnerException is SecurityException == false)
throw;
}
}
catch (InvalidOperationException e)
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 static readonly ConcurrentDictionary<string, Type> TypeNamesCache= new ConcurrentDictionary<string, Type>();
private string _rootDir = "";
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()
{
if (string.IsNullOrEmpty(_rootDir) == false)
{
if (e.InnerException is SecurityException == false)
throw;
return _rootDir;
}
return assemblies;
});
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;
}
private bool AcceptsLoadExceptions(Assembly a)
{
if (_assembliesAcceptingLoadExceptions.Length == 0)
return false;
if (_assembliesAcceptingLoadExceptions.Length == 1 && _assembliesAcceptingLoadExceptions[0] == "*")
return true;
var name = a.GetName().Name; // simple name of the assembly
return _assembliesAcceptingLoadExceptions.Any(pattern =>
{
if (pattern.Length > name.Length) return false; // pattern longer than name
if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical
if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot
return name.StartsWith(pattern); // and name must start with pattern
});
}
/// <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.
/// 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>
/// <param name="excludeFromResults"></param>
/// <returns></returns>
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
IEnumerable<Assembly> excludeFromResults = null)
/// <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()
{
lock (LocalFilteredAssemblyCacheLocker)
{
if (_localFilteredAssemblyCache != null)
return _localFilteredAssemblyCache;
return _allAssemblies.Value;
}
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
return _localFilteredAssemblyCache;
/// <inheritdoc />
public IEnumerable<Assembly> AssembliesToScan
{
get
{
lock (_localFilteredAssemblyCacheLocker)
{
if (_localFilteredAssemblyCache != null)
return _localFilteredAssemblyCache;
var assemblies = GetFilteredAssemblies(null, KnownAssemblyExclusionFilter);
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
return _localFilteredAssemblyCache;
}
}
}
@@ -188,7 +159,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 +181,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 +232,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 +273,58 @@ 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();
return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses);
}
/// <summary>
/// Finds the classes with attribute.
/// Returns a Type for the string type name
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies">The assemblies.</param>
/// <param name="name"></param>
/// <returns></returns>
public static IEnumerable<Type> FindClassesWithAttribute<T>(IEnumerable<Assembly> assemblies)
where T : Attribute
public virtual Type GetTypeByName(string name)
{
return FindClassesWithAttribute<T>(assemblies, true);
}
// This is exactly what the BuildManager does, if the type is an assembly qualified type
// name it will find it.
if (TypeNameContainsAssembly(name))
{
return Type.GetType(name);
}
/// <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());
// It didn't parse, so try loading from each already loaded assembly and cache it
return TypeNamesCache.GetOrAdd(name, s =>
AppDomain.CurrentDomain.GetAssemblies()
.Select(x => x.GetType(s))
.FirstOrDefault(x => x != null));
}
#region Private methods
private static IEnumerable<Type> GetClassesWithAttribute(
// borrowed from aspnet System.Web.UI.Util
private static bool TypeNameContainsAssembly(string typeName)
{
return CommaIndexInTypeName(typeName) > 0;
}
// borrowed from aspnet System.Web.UI.Util
private static int CommaIndexInTypeName(string typeName)
{
var num1 = typeName.LastIndexOf(',');
if (num1 < 0)
return -1;
var num2 = typeName.LastIndexOf(']');
if (num2 > num1)
return -1;
return typeName.IndexOf(',', num2 + 1);
}
private IEnumerable<Type> GetClassesWithAttribute(
Type attributeType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
@@ -441,7 +355,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 +391,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 +421,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 +447,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 +483,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 +509,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 +522,6 @@ 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);
}
}
}
@@ -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);
}
}
@@ -8,6 +8,7 @@ using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// A utility class for type checking, this provides internal caching so that calls to these methods will be faster
/// than doing a manual type check in c#
@@ -18,9 +19,11 @@ namespace Umbraco.Core.Composing
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
= new ConcurrentDictionary<Type, FieldInfo[]>();
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
/// <summary>
/// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too.
/// If it cannot be done, null is returned.
@@ -5,30 +5,30 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Compilation;
using Umbraco.Core.Cache;
using Umbraco.Core.Collections;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using File = System.IO.File;
namespace Umbraco.Core.Composing
{
/// <summary>
/// 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="ITypeFinder"/> 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>
/// </remarks>
public class TypeLoader
public sealed class TypeLoader
{
private const string CacheKey = "umbraco-types.list";
private readonly IIOHelper _ioHelper;
private readonly IAppPolicyCache _runtimeCache;
private readonly IProfilingLogger _logger;
@@ -42,31 +42,40 @@ namespace Umbraco.Core.Composing
private string _currentAssembliesHash;
private IEnumerable<Assembly> _assemblies;
private bool _reportedChange;
private readonly string _localTempPath;
private readonly DirectoryInfo _localTempPath;
private string _fileBasePath;
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="ioHelper"></param>
/// <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)
/// <param name="assembliesToScan"></param>
public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, IEnumerable<Assembly> assembliesToScan = null)
: this(ioHelper, typeFinder, runtimeCache, localTempPath, logger, true, assembliesToScan)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
/// </summary>
/// <param name="ioHelper"></param>
/// <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)
/// <param name="assembliesToScan"></param>
public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, bool detectChanges, IEnumerable<Assembly> assembliesToScan = null)
{
TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder));
_ioHelper = ioHelper;
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
_localTempPath = localTempPath;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_assemblies = assembliesToScan;
if (detectChanges)
{
@@ -99,11 +108,10 @@ namespace Umbraco.Core.Composing
}
/// <summary>
/// Initializes a new, test/blank, instance of the <see cref="TypeLoader"/> class.
/// Returns the underlying <see cref="ITypeFinder"/>
/// </summary>
/// <remarks>The initialized instance cannot get types.</remarks>
internal TypeLoader()
{ }
// ReSharper disable once MemberCanBePrivate.Global
public ITypeFinder TypeFinder { get; }
/// <summary>
/// Gets or sets the set of assemblies to scan.
@@ -115,25 +123,21 @@ namespace Umbraco.Core.Composing
/// <para>This is for unit tests.</para>
/// </remarks>
// internal for tests
internal IEnumerable<Assembly> AssembliesToScan
{
get => _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions());
set => _assemblies = value;
}
public IEnumerable<Assembly> AssembliesToScan => _assemblies ?? (_assemblies = TypeFinder.AssembliesToScan);
/// <summary>
/// Gets the type lists.
/// </summary>
/// <remarks>For unit tests.</remarks>
// internal for tests
internal IEnumerable<TypeList> TypeLists => _types.Values;
public IEnumerable<TypeList> TypeLists => _types.Values;
/// <summary>
/// Sets a type list.
/// </summary>
/// <remarks>For unit tests.</remarks>
// internal for tests
internal void AddTypeList(TypeList typeList)
public void AddTypeList(TypeList typeList)
{
var tobject = typeof(object); // CompositeTypeTypeKey does not support null values
_types[new CompositeTypeTypeKey(typeList.BaseType ?? tobject, typeList.AttributeType ?? tobject)] = typeList;
@@ -180,12 +184,16 @@ namespace Umbraco.Core.Composing
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
{
// TODO: Would be nicer to abstract this logic out into IAssemblyHash
// TODO: Use constants from SystemDirectories when we can (once it's ported to netstandard lib)
// the bin folder and everything in it
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(Current.IOHelper.MapPath(SystemDirectories.Bin)), false),
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(_ioHelper.MapPath("~/bin")), false),
// the app code folder and everything in it
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(Current.IOHelper.MapPath("~/App_Code")), false),
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(_ioHelper.MapPath("~/App_Code")), false),
// global.asax (the app domain also monitors this, if it changes will do a full restart)
new Tuple<FileSystemInfo, bool>(new FileInfo(Current.IOHelper.MapPath("~/global.asax")), false)
new Tuple<FileSystemInfo, bool>(new FileInfo(_ioHelper.MapPath("~/global.asax")), false)
}, _logger);
return _currentAssembliesHash;
@@ -267,7 +275,7 @@ namespace Umbraco.Core.Composing
/// <param name="logger">A profiling logger.</param>
/// <returns>The hash.</returns>
// internal for tests
internal static string GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, IProfilingLogger logger)
public static string GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, IProfilingLogger logger)
{
using (logger.DebugDuration<TypeLoader>("Determining hash of code files on disk", "Hash determined"))
{
@@ -298,7 +306,7 @@ namespace Umbraco.Core.Composing
private const int FileDeleteTimeout = 4000; // milliseconds
// internal for tests
internal Attempt<IEnumerable<string>> TryGetCached(Type baseType, Type attributeType)
public Attempt<IEnumerable<string>> TryGetCached(Type baseType, Type attributeType)
{
var cache = _runtimeCache.GetCacheItem<Dictionary<Tuple<string, string>, IEnumerable<string>>>(CacheKey, ReadCacheSafe, TimeSpan.FromSeconds(ListFileCacheDuration));
@@ -331,7 +339,7 @@ namespace Umbraco.Core.Composing
}
// internal for tests
internal Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
public Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
{
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
@@ -377,7 +385,7 @@ namespace Umbraco.Core.Composing
}
// internal for tests
internal string GetTypesListFilePath() => GetFileBasePath() + ".list";
public string GetTypesListFilePath() => GetFileBasePath() + ".list";
private string GetTypesHashFilePath() => GetFileBasePath() + ".hash";
@@ -388,7 +396,7 @@ namespace Umbraco.Core.Composing
if (_fileBasePath != null)
return _fileBasePath;
_fileBasePath = Path.Combine(_localTempPath, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName);
_fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName);
// ensure that the folder exists
var directory = Path.GetDirectoryName(_fileBasePath);
@@ -402,7 +410,7 @@ namespace Umbraco.Core.Composing
}
// internal for tests
internal void WriteCache()
public void WriteCache()
{
_logger.Debug<TypeLoader>("Writing cache file.");
var typesListFilePath = GetTypesListFilePath();
@@ -720,18 +728,13 @@ namespace Umbraco.Core.Composing
// successfully retrieved types from the file cache: load
foreach (var type in cacheResult.Result)
{
try
{
// we use the build manager to ensure we get all types loaded, this is slightly slower than
// Type.GetType but if the types in the assembly aren't loaded yet it would fail whereas
// BuildManager will load them - this is how eg MVC loads types, etc - no need to make it
// more complicated
typeList.Add(BuildManager.GetType(type, true));
}
catch (Exception ex)
var resolvedType = TypeFinder.GetTypeByName(type);
if (resolvedType != null)
typeList.Add(resolvedType);
else
{
// in case of any exception, we have to exit, and revert to scanning
_logger.Error<TypeLoader>(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type);
_logger.Warn<TypeLoader>("Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type);
scan = true;
break;
}
@@ -783,7 +786,7 @@ namespace Umbraco.Core.Composing
/// Represents a list of types obtained by looking for types inheriting/implementing a
/// specified type, and/or marked with a specified attribute type.
/// </summary>
internal class TypeList
public sealed class TypeList
{
private readonly HashSet<Type> _types = new HashSet<Type>();
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
public interface ITypeFinderConfig
{
IEnumerable<string> AssembliesAcceptingLoadExceptions { get; }
}
}
@@ -21,6 +21,7 @@ namespace Umbraco.Core
IUmbracoDatabaseFactory databaseFactory,
TypeLoader typeLoader,
IRuntimeState state,
ITypeFinder typeFinder,
IIOHelper ioHelper)
{
composition.RegisterUnique(logger);
@@ -32,6 +33,7 @@ namespace Umbraco.Core
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoDatabaseFactory>().SqlContext);
composition.RegisterUnique(typeLoader);
composition.RegisterUnique(state);
composition.RegisterUnique(typeFinder);
composition.RegisterUnique(ioHelper);
}
}
+2
View File
@@ -13,6 +13,8 @@ namespace Umbraco.Core.IO
{
public class IOHelper : IIOHelper
{
internal static IIOHelper Default { get; } = new IOHelper();
/// <summary>
/// Gets or sets a value forcing Umbraco to consider it is non-hosted.
/// </summary>
+29 -8
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Web;
using System.Web.Hosting;
using Umbraco.Core.Cache;
@@ -42,6 +43,11 @@ namespace Umbraco.Core.Runtime
/// </summary>
protected IProfilingLogger ProfilingLogger { get; private set; }
/// <summary>
/// Gets the <see cref="ITypeFinder"/>
/// </summary>
protected ITypeFinder TypeFinder { get; private set; }
/// <summary>
/// Gets the <see cref="IIOHelper"/>
/// </summary>
@@ -58,13 +64,22 @@ namespace Umbraco.Core.Runtime
// loggers
var logger = Logger = GetLogger();
if (logger == null)
throw new InvalidOperationException($"The object returned from {nameof(GetLogger)} cannot be null");
var profiler = Profiler = GetProfiler();
if (profiler == null)
throw new InvalidOperationException($"The object returned from {nameof(GetProfiler)} cannot be null");
var profilingLogger = ProfilingLogger = new ProfilingLogger(logger, profiler);
IOHelper = GetIOHelper();
if (IOHelper == null)
throw new InvalidOperationException($"The object returned from {nameof(GetIOHelper)} cannot be null");
TypeFinder = GetTypeFinder();
if (TypeFinder == null)
throw new InvalidOperationException($"The object returned from {nameof(GetTypeFinder)} cannot be null");
// the boot loader boots using a container scope, so anything that is PerScope will
// be disposed after the boot loader has booted, and anything else will remain.
// note that this REQUIRES that perWebRequestScope has NOT been enabled yet, else
@@ -120,8 +135,8 @@ namespace Umbraco.Core.Runtime
// configs
var configs = GetConfigs();
// type loader
var typeLoader = new TypeLoader(appCaches.RuntimeCache, configs.Global().LocalTempPath, ProfilingLogger);
// type finder/loader
var typeLoader = new TypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, new DirectoryInfo(configs.Global().LocalTempPath), ProfilingLogger);
// runtime state
// beware! must use '() => _factory.GetInstance<T>()' and NOT '_factory.GetInstance<T>'
@@ -137,9 +152,9 @@ namespace Umbraco.Core.Runtime
// main dom
var mainDom = new MainDom(Logger);
// create the composition
// create the composition
composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs);
composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state, IOHelper);
composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state, TypeFinder, IOHelper);
// run handlers
RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory);
@@ -297,7 +312,6 @@ namespace Umbraco.Core.Runtime
/// </summary>
public virtual void Compose(Composition composition)
{
// nothing
}
#region Getters
@@ -322,12 +336,19 @@ namespace Umbraco.Core.Runtime
protected virtual IProfiler GetProfiler()
=> new LogProfiler(Logger);
/// <summary>
/// Gets a <see cref="ITypeFinder"/>
/// </summary>
/// <returns></returns>
protected virtual ITypeFinder GetTypeFinder()
=> new TypeFinder(Logger);
/// <summary>
/// Gets a <see cref="IIOHelper"/>
/// </summary>
/// <returns></returns>
protected virtual IIOHelper GetIOHelper()
=> new Umbraco.Core.IO.IOHelper();
=> Umbraco.Core.IO.IOHelper.Default;
/// <summary>
/// Gets the application caches.
@@ -339,9 +360,9 @@ namespace Umbraco.Core.Runtime
// is overridden by the web runtime
return new AppCaches(
new DeepCloneAppCache(new ObjectCacheAppCache()),
new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder)),
NoAppCache.Instance,
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder))));
}
// by default, returns null, meaning that Umbraco should auto-detect the application root path.
+8 -6
View File
@@ -17,6 +17,7 @@ namespace Umbraco.Core.Scoping
{
private readonly ScopeProvider _scopeProvider;
private readonly ILogger _logger;
private readonly ITypeFinder _typeFinder;
private readonly IsolationLevel _isolationLevel;
private readonly RepositoryCacheMode _repositoryCacheMode;
@@ -35,7 +36,7 @@ namespace Umbraco.Core.Scoping
// initializes a new scope
private Scope(ScopeProvider scopeProvider,
ILogger logger, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher eventDispatcher = null,
@@ -45,6 +46,7 @@ namespace Umbraco.Core.Scoping
{
_scopeProvider = scopeProvider;
_logger = logger;
_typeFinder = typeFinder;
Context = scopeContext;
@@ -109,26 +111,26 @@ namespace Umbraco.Core.Scoping
// initializes a new scope
public Scope(ScopeProvider scopeProvider,
ILogger logger, FileSystems fileSystems, bool detachable, ScopeContext scopeContext,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, bool detachable, ScopeContext scopeContext,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false)
: this(scopeProvider, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
: this(scopeProvider, logger, typeFinder, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
{ }
// initializes a new scope in a nested scopes chain, with its parent
public Scope(ScopeProvider scopeProvider,
ILogger logger, FileSystems fileSystems, Scope parent,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false)
: this(scopeProvider, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
: this(scopeProvider, logger, typeFinder, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
{ }
public Guid InstanceId { get; } = Guid.NewGuid();
@@ -175,7 +177,7 @@ namespace Umbraco.Core.Scoping
if (ParentScope != null) return ParentScope.IsolatedCaches;
return _isolatedCaches ?? (_isolatedCaches
= new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
= new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache(_typeFinder))));
}
}
+6 -4
View File
@@ -22,13 +22,15 @@ namespace Umbraco.Core.Scoping
internal class ScopeProvider : IScopeProvider, IScopeAccessor
{
private readonly ILogger _logger;
private readonly ITypeFinder _typeFinder;
private readonly FileSystems _fileSystems;
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ILogger logger)
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ILogger logger, ITypeFinder typeFinder)
{
DatabaseFactory = databaseFactory;
_fileSystems = fileSystems;
_logger = logger;
_typeFinder = typeFinder;
// take control of the FileSystems
_fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems;
@@ -323,7 +325,7 @@ namespace Umbraco.Core.Scoping
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null)
{
return new Scope(this, _logger, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
return new Scope(this, _logger, _typeFinder, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
}
/// <inheritdoc />
@@ -379,13 +381,13 @@ namespace Umbraco.Core.Scoping
{
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, _logger, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
var scope = new Scope(this, _logger, _typeFinder, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
}
var nested = new Scope(this, _logger, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
var nested = new Scope(this, _logger, _typeFinder, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
SetAmbient(nested, AmbientContext);
return nested;
}
-8
View File
@@ -127,14 +127,8 @@
</Compile>
-->
<Compile Include="Cache\DefaultRepositoryCachePolicy.cs" />
<Compile Include="Cache\FastDictionaryAppCacheBase.cs" />
<Compile Include="Cache\ObjectCacheAppCache.cs" />
<Compile Include="Compose\AuditEventsComponent.cs" />
<Compile Include="Cache\CacheRefresherCollectionBuilder.cs" />
<Compile Include="Cache\FastDictionaryAppCache.cs" />
<Compile Include="Cache\FullDataSetRepositoryCachePolicy.cs" />
<Compile Include="Cache\HttpRequestAppCache.cs" />
<Compile Include="Cache\WebCachingAppCache.cs" />
<Compile Include="Cache\RepositoryCachePolicyBase.cs" />
<Compile Include="Cache\SingleItemsOnlyRepositoryCachePolicy.cs" />
<Compile Include="Compose\AuditEventsComposer.cs" />
@@ -185,8 +179,6 @@
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />
<Compile Include="Composing\LightInject\MixedLightInjectScopeManagerProvider.cs" />
<Compile Include="Composing\TargetedServiceFactory.cs" />
<Compile Include="Composing\TypeFinder.cs" />
<Compile Include="Composing\TypeLoader.cs" />
<Compile Include="IO\IMediaFileSystem.cs" />
<Compile Include="IO\IMediaPathScheme.cs" />
<Compile Include="IO\IOHelper.cs" />
+8 -1
View File
@@ -17,6 +17,13 @@ namespace Umbraco.Examine
/// </summary>
public abstract class LuceneIndexCreator : IIndexCreator
{
private readonly ITypeFinder _typeFinder;
protected LuceneIndexCreator(ITypeFinder typeFinder)
{
_typeFinder = typeFinder;
}
public abstract IEnumerable<IIndex> Create();
/// <summary>
@@ -38,7 +45,7 @@ namespace Umbraco.Examine
if (!configuredDirectoryFactory.IsNullOrWhiteSpace())
{
//this should be a fully qualified type
var factoryType = TypeFinder.GetTypeByName(configuredDirectoryFactory);
var factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory);
if (factoryType == null) throw new NullReferenceException("No directory type found for value: " + configuredDirectoryFactory);
var directoryFactory = (IDirectoryFactory)Activator.CreateInstance(factoryType);
return directoryFactory.CreateDirectory(dirInfo);
@@ -2,13 +2,17 @@
using System.Diagnostics;
using System.Reflection;
using System.Web;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Collections;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Tests.Collections;
using Umbraco.Web.Cache;
namespace Umbraco.Tests.Cache
{
@@ -22,7 +26,8 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
_provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache));
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
_provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, typeFinder));
}
internal override IAppCache AppCache => _provider;
@@ -9,6 +9,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
using Umbraco.Tests.Components;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Cache.DistributedCache
{
@@ -25,7 +26,7 @@ namespace Umbraco.Tests.Cache.DistributedCache
{
var register = RegisterFactory.Create();
var composition = new Composition(register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.RegisterUnique<IServerRegistrar>(_ => new TestServerRegistrar());
composition.RegisterUnique<IServerMessenger>(_ => new TestServerMessenger());
@@ -1,5 +1,8 @@
using NUnit.Framework;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Cache
@@ -13,8 +16,9 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
_ctx = new FakeHttpContextFactory("http://localhost/test");
_appCache = new HttpRequestAppCache(_ctx.HttpContext);
_appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder);
}
internal override IAppCache AppCache
@@ -1,9 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Tests.Cache
{
@@ -20,7 +23,8 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
_provider = new ObjectCacheAppCache();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
_provider = new ObjectCacheAppCache(typeFinder);
}
internal override IAppCache AppCache
@@ -1,8 +1,12 @@
using System;
using System.Diagnostics;
using System.Web;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Web.Cache;
namespace Umbraco.Tests.Cache
{
@@ -16,7 +20,8 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
_appCache = new WebCachingAppCache(HttpRuntime.Cache);
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
_appCache = new WebCachingAppCache(HttpRuntime.Cache, typeFinder);
}
internal override IAppCache AppCache => _appCache;
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Moq;
using NUnit.Framework;
@@ -31,9 +32,10 @@ namespace Umbraco.Tests.Components
var mock = new Mock<IFactory>();
var logger = Mock.Of<ILogger>();
var typeFinder = new TypeFinder(logger);
var f = new UmbracoDatabaseFactory(logger, new Lazy<IMapperCollection>(() => new MapperCollection(Enumerable.Empty<BaseMapper>())));
var fs = new FileSystems(mock.Object, logger);
var p = new ScopeProvider(f, fs, logger);
var p = new ScopeProvider(f, fs, logger, typeFinder);
mock.Setup(x => x.GetInstance(typeof (ILogger))).Returns(logger);
mock.Setup(x => x.GetInstance(typeof (IProfilingLogger))).Returns(new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
@@ -51,7 +53,8 @@ namespace Umbraco.Tests.Components
private static TypeLoader MockTypeLoader()
{
return new TypeLoader();
var ioHelper = IOHelper.Default;
return new TypeLoader(ioHelper, Mock.Of<ITypeFinder>(), Mock.Of<IAppPolicyCache>(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());
}
public static IRuntimeState MockRuntimeState(RuntimeLevel level)
@@ -361,7 +364,9 @@ namespace Umbraco.Tests.Components
[Test]
public void AllComposers()
{
var typeLoader = new TypeLoader(AppCaches.Disabled.RuntimeCache, Current.IOHelper.MapPath("~/App_Data/TEMP"), Mock.Of<IProfilingLogger>());
var ioHelper = IOHelper.Default;
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
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));
@@ -7,6 +7,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Tests.Components;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Composing
{
@@ -21,7 +22,7 @@ namespace Umbraco.Tests.Composing
Current.Reset();
var register = RegisterFactory.Create();
_composition = new Composition(register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
_composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
}
[TearDown]
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Moq;
using NUnit.Framework;
@@ -22,10 +23,9 @@ namespace Umbraco.Tests.Composing
{
ProfilingLogger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
TypeLoader = new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), ProfilingLogger, detectChanges: false)
{
AssembliesToScan = AssembliesToScan
};
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var ioHelper = IOHelper.Default;
TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan);
}
[TearDown]
@@ -1,4 +1,5 @@
using System;
using System.IO;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -36,7 +37,9 @@ namespace Umbraco.Tests.Composing
.Returns(() => factoryFactory?.Invoke(mockedFactory));
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), Current.IOHelper.MapPath("~/App_Data/TEMP"), logger);
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var ioHelper = IOHelper.Default;
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>());
// create the factory, ensure it is the mocked factory
@@ -9,6 +9,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Tests.Components;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Composing
{
@@ -40,7 +41,7 @@ namespace Umbraco.Tests.Composing
public void LazyCollectionBuilderHandlesTypes()
{
var container = CreateRegister();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<TestCollectionBuilder>()
.Add<TransientObject3>()
@@ -66,7 +67,7 @@ namespace Umbraco.Tests.Composing
public void LazyCollectionBuilderHandlesProducers()
{
var container = CreateRegister();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<TestCollectionBuilder>()
.Add(() => new[] { typeof(TransientObject3), typeof(TransientObject2) })
@@ -91,7 +92,7 @@ namespace Umbraco.Tests.Composing
public void LazyCollectionBuilderHandlesTypesAndProducers()
{
var container = CreateRegister();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<TestCollectionBuilder>()
.Add<TransientObject3>()
@@ -117,7 +118,7 @@ namespace Umbraco.Tests.Composing
public void LazyCollectionBuilderThrowsOnIllegalTypes()
{
var container = CreateRegister();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<TestCollectionBuilder>()
.Add<TransientObject3>()
@@ -139,7 +140,7 @@ namespace Umbraco.Tests.Composing
public void LazyCollectionBuilderCanExcludeTypes()
{
var container = CreateRegister();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<TestCollectionBuilder>()
.Add<TransientObject3>()
@@ -9,6 +9,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.PackageActions;
using Umbraco.Tests.Components;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Composing
{
@@ -20,7 +21,7 @@ namespace Umbraco.Tests.Composing
{
var container = RegisterFactory.Create();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<PackageActionCollectionBuilder>()
.Add(() => TypeLoader.GetPackageActions());
+6 -466
View File
@@ -10,6 +10,7 @@ using System.Text;
using System.Threading;
using System.Web;
using System.Web.Compilation;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
@@ -56,39 +57,19 @@ namespace Umbraco.Tests.Composing
[Test]
public void Find_Class_Of_Type_With_Attribute()
{
var typesFound = TypeFinder.FindClassesOfTypeWithAttribute<TestEditor, MyTestAttribute>(_assemblies);
var typeFinder = new TypeFinder(GetTestProfilingLogger());
var typesFound = typeFinder.FindClassesOfTypeWithAttribute<TestEditor, MyTestAttribute>(_assemblies);
Assert.AreEqual(2, typesFound.Count());
}
//[Test]
//public void Find_Classes_Of_Type()
//{
// var typesFound = TypeFinder.FindClassesOfType<IApplicationEventHandler>(_assemblies);
// var originalTypesFound = TypeFinderOriginal.FindClassesOfType<IApplicationEventHandler>(_assemblies);
// foreach (var type in typesFound)
// Console.WriteLine(type);
// Console.WriteLine();
// foreach (var type in originalTypesFound)
// Console.WriteLine(type);
// // 6 classes in _assemblies implement IApplicationEventHandler
// Assert.AreEqual(6, typesFound.Count());
// // however,
// // Umbraco.Core.Profiling.WebProfiler is internal and is not returned by TypeFinderOriginal,
// // that's a known issue of the legacy type finder, so we have to tweak the count here.
// Assert.AreEqual(5, originalTypesFound.Count());
//}
[Test]
public void Find_Classes_With_Attribute()
{
var typesFound = TypeFinder.FindClassesWithAttribute<TreeAttribute>(_assemblies);
var typeFinder = new TypeFinder(GetTestProfilingLogger());
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 });
typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>(new[] { typeof (UmbracoContext).Assembly });
Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
}
@@ -122,447 +103,6 @@ namespace Umbraco.Tests.Composing
}
//USED FOR THE ABOVE TESTS
// see this issue for details: http://issues.umbraco.org/issue/U4-1187
internal static class TypeFinderOriginal
{
private static readonly ConcurrentBag<Assembly> LocalFilteredAssemblyCache = new ConcurrentBag<Assembly>();
private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim();
private static ReadOnlyCollection<Assembly> _allAssemblies = null;
private static ReadOnlyCollection<Assembly> _binFolderAssemblies = null;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
/// <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>
internal static IEnumerable<Assembly> GetAllAssemblies()
{
if (_allAssemblies == null)
{
using (new WriteLock(Locker))
{
List<Assembly> assemblies = null;
try
{
var isHosted = HttpContext.Current != null;
try
{
if (isHosted)
{
assemblies = new List<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
}
}
catch (InvalidOperationException e)
{
if (!(e.InnerException is SecurityException))
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 = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory;
var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList();
assemblies = new List<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())
{
assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().ToList());
}
//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(Current.IOHelper.MapPath(Current.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())))
{
var appCodeAssembly = Assembly.Load("App_Code");
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
assemblies.Add(appCodeAssembly);
}
//now set the _allAssemblies
_allAssemblies = new ReadOnlyCollection<Assembly>(assemblies);
}
catch (InvalidOperationException e)
{
if (!(e.InnerException is SecurityException))
throw;
_binFolderAssemblies = _allAssemblies;
}
}
}
return _allAssemblies;
}
/// <summary>
/// Returns only assemblies found in the bin folder that have been loaded into the app domain.
/// </summary>
/// <returns></returns>
/// <remarks>
/// This will be used if we implement App_Plugins from Umbraco v5 but currently it is not used.
/// </remarks>
internal static IEnumerable<Assembly> GetBinAssemblies()
{
if (_binFolderAssemblies == null)
{
using (new WriteLock(Locker))
{
var assemblies = GetAssembliesWithKnownExclusions().ToArray();
var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory;
var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList();
var domainAssemblyNames = binAssemblyFiles.Select(AssemblyName.GetAssemblyName);
var safeDomainAssemblies = new List<Assembly>();
var binFolderAssemblies = new List<Assembly>();
foreach (var a in assemblies)
{
try
{
//do a test to see if its queryable in med trust
var assemblyFile = a.GetAssemblyFile();
safeDomainAssemblies.Add(a);
}
catch (SecurityException)
{
//we will just ignore this because this will fail
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
//an assembly that is ok.
}
}
foreach (var assemblyName in domainAssemblyNames)
{
try
{
var foundAssembly = safeDomainAssemblies.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile());
if (foundAssembly != null)
{
binFolderAssemblies.Add(foundAssembly);
}
}
catch (SecurityException)
{
//we will just ignore this because if we are trying to do a call to:
// AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName)))
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
//an assembly that is ok.
}
}
_binFolderAssemblies = new ReadOnlyCollection<Assembly>(binFolderAssemblies);
}
}
return _binFolderAssemblies;
}
/// <summary>
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
/// and exluding the ones passed in and excluding the exclusion list filter, the results of this are
/// cached for perforance reasons.
/// </summary>
/// <param name="excludeFromResults"></param>
/// <returns></returns>
internal static IEnumerable<Assembly> GetAssembliesWithKnownExclusions(
IEnumerable<Assembly> excludeFromResults = null)
{
if (LocalFilteredAssemblyCache.Any()) return LocalFilteredAssemblyCache;
using (new WriteLock(LocalFilteredAssemblyCacheLocker))
{
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
foreach (var assembly in assemblies) LocalFilteredAssemblyCache.Add(assembly);
}
return LocalFilteredAssemblyCache;
}
/// <summary>
/// Return a list of found local Assemblies and exluding the ones passed in and excluding the exclusion list filter
/// </summary>
/// <param name="excludeFromResults"></param>
/// <param name="exclusionFilter"></param>
/// <returns></returns>
private static IEnumerable<Assembly> GetFilteredAssemblies(
IEnumerable<Assembly> excludeFromResults = null,
string[] exclusionFilter = null)
{
if (excludeFromResults == null)
excludeFromResults = new List<Assembly>();
if (exclusionFilter == null)
exclusionFilter = new string[] { };
return GetAllAssemblies()
.Where(x => !excludeFromResults.Contains(x)
&& !x.GlobalAssemblyCache
&& !exclusionFilter.Any(f => x.FullName.StartsWith(f)));
}
/// <summary>
/// this is our assembly filter to filter out known types that def dont contain types we'd like to find or plugins
/// </summary>
/// <remarks>
/// 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
/// </remarks>
internal static readonly string[] KnownAssemblyExclusionFilter = new[]
{
"mscorlib,",
"System.",
"Antlr3.",
"Autofac.",
"Autofac,",
"Castle.",
"ClientDependency.",
"DataAnnotationsExtensions.",
"DataAnnotationsExtensions,",
"Dynamic,",
"HtmlDiff,",
"Iesi.Collections,",
"Microsoft.",
"Newtonsoft.",
"NHibernate.",
"NHibernate,",
"NuGet.",
"RouteDebugger,",
"SqlCE4Umbraco,",
"umbraco.datalayer,",
"umbraco.interfaces,",
"umbraco.providers,",
"Umbraco.Web.UI,",
"Lucene.",
"Examine,",
"Examine.",
"ServiceStack.",
"MySql.",
"HtmlAgilityPack.",
"TidyNet.",
"ICSharpCode.",
"CookComputing.",
/* Mono */
"MonoDevelop.NUnit",
"Serilog."
};
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>()
where TAttribute : Attribute
{
return FindClassesOfTypeWithAttribute<T, TAttribute>(GetAssembliesWithKnownExclusions(), true);
}
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>(IEnumerable<Assembly> assemblies)
where TAttribute : Attribute
{
return FindClassesOfTypeWithAttribute<T, TAttribute>(assemblies, true);
}
public static IEnumerable<Type> FindClassesOfTypeWithAttribute<T, TAttribute>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
where TAttribute : Attribute
{
if (assemblies == null) throw new ArgumentNullException("assemblies");
var l = new List<Type>();
foreach (var a in assemblies)
{
var types = from t in GetTypesWithFormattedException(a)
where !t.IsInterface
&& typeof(T).IsAssignableFrom(t)
&& t.GetCustomAttributes<TAttribute>(false).Any()
&& (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract))
select t;
l.AddRange(types);
}
return l;
}
/// <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("assemblies");
return GetAssignablesFromType<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 the classes with attribute.
/// </summary>
/// <param name="type">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>
public static IEnumerable<Type> FindClassesWithAttribute(Type type, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
{
if (assemblies == null) throw new ArgumentNullException("assemblies");
if (!TypeHelper.IsTypeAssignableFrom<Attribute>(type))
throw new ArgumentException("The type specified: " + type + " is not an Attribute type");
var l = new List<Type>();
foreach (var a in assemblies)
{
var types = from t in GetTypesWithFormattedException(a)
where !t.IsInterface && t.GetCustomAttributes(type, false).Any() && (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract))
select t;
l.AddRange(types);
}
return l;
}
/// <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());
}
#region Private methods
/// <summary>
/// Gets a collection of assignables of type T from a collection of assemblies
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <returns></returns>
private static IEnumerable<Type> GetAssignablesFromType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
{
return GetTypes(typeof(T), assemblies, onlyConcreteClasses);
}
private static IEnumerable<Type> GetTypes(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
{
var l = new List<Type>();
foreach (var a in assemblies)
{
var types = from t in GetTypesWithFormattedException(a)
where !t.IsInterface && assignTypeFrom.IsAssignableFrom(t) && (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract))
select t;
l.AddRange(types);
}
return l;
}
private static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
{
//if the assembly is dynamic, do not try to scan it
if (a.IsDynamic)
return Enumerable.Empty<Type>();
try
{
return a.GetExportedTypes();
}
catch (ReflectionTypeLoadException ex)
{
var sb = new StringBuilder();
sb.AppendLine("Could not load types from assembly " + a.FullName + ", errors:");
foreach (var loaderException in ex.LoaderExceptions.WhereNotNull())
{
sb.AppendLine("Exception: " + loaderException.ToString());
}
throw new ReflectionTypeLoadException(ex.Types, ex.LoaderExceptions, sb.ToString());
}
}
#endregion
}
}
+31 -25
View File
@@ -27,24 +27,30 @@ namespace Umbraco.Tests.Composing
public void Initialize()
{
// this ensures it's reset
_typeLoader = new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), false);
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
_typeLoader = new TypeLoader(IOHelper.Default, typeFinder, NoAppCache.Instance,
new DirectoryInfo(IOHelper.Default.MapPath("~/App_Data/TEMP")),
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), false,
// for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
// TODO: Should probably update this so it only searches this assembly and add custom types to be found
new[]
{
this.GetType().Assembly,
typeof(System.Guid).Assembly,
typeof(NUnit.Framework.Assert).Assembly,
typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly,
typeof(System.Xml.NameTable).Assembly,
typeof(System.Configuration.GenericEnumConverter).Assembly,
typeof(System.Web.SiteMap).Assembly,
//typeof(TabPage).Assembly,
typeof(System.Web.Mvc.ActionResult).Assembly,
typeof(TypeFinder).Assembly,
typeof(UmbracoContext).Assembly
});
// for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
// TODO: Should probably update this so it only searches this assembly and add custom types to be found
_typeLoader.AssembliesToScan = new[]
{
this.GetType().Assembly,
typeof(System.Guid).Assembly,
typeof(NUnit.Framework.Assert).Assembly,
typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly,
typeof(System.Xml.NameTable).Assembly,
typeof(System.Configuration.GenericEnumConverter).Assembly,
typeof(System.Web.SiteMap).Assembly,
//typeof(TabPage).Assembly,
typeof(System.Web.Mvc.ActionResult).Assembly,
typeof(TypeFinder).Assembly,
typeof(UmbracoContext).Assembly
};
}
[TearDown]
@@ -189,15 +195,15 @@ AnotherContentFinder
[Test]
public void Create_Cached_Plugin_File()
{
var types = new[] { typeof (TypeLoader), typeof (TypeLoaderTests), typeof (UmbracoContext) };
var types = new[] { typeof(TypeLoader), typeof(TypeLoaderTests), typeof(UmbracoContext) };
var typeList1 = new TypeLoader.TypeList(typeof (object), null);
var typeList1 = new TypeLoader.TypeList(typeof(object), null);
foreach (var type in types) typeList1.Add(type);
_typeLoader.AddTypeList(typeList1);
_typeLoader.WriteCache();
var plugins = _typeLoader.TryGetCached(typeof (object), null);
var diffType = _typeLoader.TryGetCached(typeof (object), typeof (ObsoleteAttribute));
var plugins = _typeLoader.TryGetCached(typeof(object), null);
var diffType = _typeLoader.TryGetCached(typeof(object), typeof(ObsoleteAttribute));
Assert.IsTrue(plugins.Success);
//this will be false since there is no cache of that type resolution kind
@@ -268,7 +274,7 @@ AnotherContentFinder
public void GetDataEditors()
{
var types = _typeLoader.GetDataEditors();
Assert.AreEqual(38, types.Count());
Assert.AreEqual(37, types.Count());
}
/// <summary>
@@ -280,16 +286,16 @@ AnotherContentFinder
{
var types = new HashSet<TypeLoader.TypeList>();
var propEditors = new TypeLoader.TypeList(typeof (DataEditor), null);
var propEditors = new TypeLoader.TypeList(typeof(DataEditor), null);
propEditors.Add(typeof(LabelPropertyEditor));
types.Add(propEditors);
var found = types.SingleOrDefault(x => x.BaseType == typeof (DataEditor) && x.AttributeType == null);
var found = types.SingleOrDefault(x => x.BaseType == typeof(DataEditor) && x.AttributeType == null);
Assert.IsNotNull(found);
//This should not find a type list of this type
var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof (IDataEditor) && x.AttributeType == null);
var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof(IDataEditor) && x.AttributeType == null);
Assert.IsNull(shouldNotFind);
}
+4 -2
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Moq;
@@ -25,9 +26,10 @@ namespace Umbraco.Tests.CoreThings
{
// FIXME: bad in a unit test - but Udi has a static ctor that wants it?!
var container = new Mock<IFactory>();
var globalSettings = SettingsForTests.GenerateMockGlobalSettings();
var ioHelper = IOHelper.Default;
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns(
new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
Current.Factory = container.Object;
Udi.ResetUdiTypes();
+1 -1
View File
@@ -28,7 +28,7 @@ namespace Umbraco.Tests.IO
{
_register = RegisterFactory.Create();
var composition = new Composition(_register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(_register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.Register(_ => Mock.Of<ILogger>());
composition.Register(_ => Mock.Of<IDataTypeService>());
+6 -3
View File
@@ -1,7 +1,9 @@
using NUnit.Framework;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Macros;
@@ -15,11 +17,12 @@ namespace Umbraco.Tests.Macros
[SetUp]
public void Setup()
{
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
//we DO want cache enabled for these tests
var cacheHelper = new AppCaches(
new ObjectCacheAppCache(),
new ObjectCacheAppCache(typeFinder),
NoAppCache.Instance,
new IsolatedCaches(type => new ObjectCacheAppCache()));
new IsolatedCaches(type => new ObjectCacheAppCache(typeFinder)));
//Current.ApplicationContext = new ApplicationContext(cacheHelper, new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
Current.Reset();
+2 -1
View File
@@ -9,6 +9,7 @@ using Newtonsoft.Json;
using Umbraco.Core;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -269,7 +270,7 @@ namespace Umbraco.Tests.Models
content.UpdateDate = DateTime.Now;
content.WriterId = 23;
var runtimeCache = new ObjectCacheAppCache();
var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of<ILogger>()));
runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content);
var proflog = GetTestProfilingLogger();
@@ -77,9 +77,9 @@ namespace Umbraco.Tests.Persistence.Repositories
public void CacheActiveForIntsAndGuids()
{
var realCache = new AppCaches(
new ObjectCacheAppCache(),
new ObjectCacheAppCache(TypeFinder),
new DictionaryAppCache(),
new IsolatedCaches(t => new ObjectCacheAppCache()));
new IsolatedCaches(t => new ObjectCacheAppCache(TypeFinder)));
var provider = TestObjects.GetScopeProvider(Logger);
using (var scope = provider.CreateScope())
@@ -49,9 +49,9 @@ namespace Umbraco.Tests.Persistence.Repositories
MediaTypeRepository mediaTypeRepository;
var realCache = new AppCaches(
new ObjectCacheAppCache(),
new ObjectCacheAppCache(TypeFinder),
new DictionaryAppCache(),
new IsolatedCaches(t => new ObjectCacheAppCache()));
new IsolatedCaches(t => new ObjectCacheAppCache(TypeFinder)));
var provider = TestObjects.GetScopeProvider(Logger);
using (var scope = provider.CreateScope())
@@ -70,7 +70,7 @@ namespace Umbraco.Tests.PropertyEditors
try
{
var container = RegisterFactory.Create();
var composition = new Composition(container, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>();
@@ -24,7 +24,7 @@ namespace Umbraco.Tests.PropertyEditors
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var register = RegisterFactory.Create();
var composition = new Composition(register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
register.Register<IShortStringHelper>(_
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings())));
@@ -178,7 +178,7 @@ namespace Umbraco.Tests.Published
Current.Reset();
var register = RegisterFactory.Create();
var composition = new Composition(register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
composition.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>()
.Append<SimpleConverter3A>()
@@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -124,8 +125,9 @@ namespace Umbraco.Tests.Published
var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes);
var elementsCache = new FastDictionaryAppCache();
var snapshotCache = new FastDictionaryAppCache();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var elementsCache = new FastDictionaryAppCache(typeFinder);
var snapshotCache = new FastDictionaryAppCache(typeFinder);
var publishedSnapshot = new Mock<IPublishedSnapshot>();
publishedSnapshot.Setup(x => x.SnapshotCache).Returns(snapshotCache);
@@ -144,39 +146,39 @@ namespace Umbraco.Tests.Published
Assert.AreEqual(1, converter.SourceConverts);
Assert.AreEqual(1, converter.InterConverts);
Assert.AreEqual(elementsCount1, elementsCache.Items.Count);
Assert.AreEqual(snapshotCount1, snapshotCache.Items.Count);
Assert.AreEqual(elementsCount1, elementsCache.Count);
Assert.AreEqual(snapshotCount1, snapshotCache.Count);
Assert.AreEqual(1234, set1.Value("prop1"));
Assert.AreEqual(1, converter.SourceConverts);
Assert.AreEqual(interConverts, converter.InterConverts);
Assert.AreEqual(elementsCount2, elementsCache.Items.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Items.Count);
Assert.AreEqual(elementsCount2, elementsCache.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
var oldSnapshotCache = snapshotCache;
snapshotCache.Items.Clear();
snapshotCache.Clear();
Assert.AreEqual(1234, set1.Value("prop1"));
Assert.AreEqual(1, converter.SourceConverts);
Assert.AreEqual(elementsCount2, elementsCache.Items.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Items.Count);
Assert.AreEqual(snapshotCount2, oldSnapshotCache.Items.Count);
Assert.AreEqual(elementsCount2, elementsCache.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
Assert.AreEqual(snapshotCount2, oldSnapshotCache.Count);
Assert.AreEqual((interConverts == 1 ? 1 : 3) + snapshotCache.Items.Count, converter.InterConverts);
Assert.AreEqual((interConverts == 1 ? 1 : 3) + snapshotCache.Count, converter.InterConverts);
var oldElementsCache = elementsCache;
elementsCache.Items.Clear();
elementsCache.Clear();
Assert.AreEqual(1234, set1.Value("prop1"));
Assert.AreEqual(1, converter.SourceConverts);
Assert.AreEqual(elementsCount2, elementsCache.Items.Count);
Assert.AreEqual(elementsCount2, oldElementsCache.Items.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Items.Count);
Assert.AreEqual(elementsCount2, elementsCache.Count);
Assert.AreEqual(elementsCount2, oldElementsCache.Count);
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
Assert.AreEqual((interConverts == 1 ? 1 : 4) + snapshotCache.Items.Count + elementsCache.Items.Count, converter.InterConverts);
Assert.AreEqual((interConverts == 1 ? 1 : 4) + snapshotCache.Count + elementsCache.Count, converter.InterConverts);
}
[Test]
@@ -138,6 +138,8 @@ namespace Umbraco.Tests.PublishedContent
// create a data source for NuCache
_source = new TestDataSource(kits);
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
// at last, create the complete NuCache snapshot service!
var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true };
_snapshotService = new PublishedSnapshotService(options,
@@ -158,7 +160,8 @@ namespace Umbraco.Tests.PublishedContent
globalSettings,
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
typeFinder);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();
@@ -181,6 +181,8 @@ namespace Umbraco.Tests.PublishedContent
// create a variation accessor
_variationAccesor = new TestVariationContextAccessor();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
// at last, create the complete NuCache snapshot service!
var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true };
_snapshotService = new PublishedSnapshotService(options,
@@ -201,7 +203,8 @@ namespace Umbraco.Tests.PublishedContent
globalSettings,
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
typeFinder);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();
@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Routing;
using Moq;
using Umbraco.Core;
@@ -12,6 +14,7 @@ using Umbraco.Core.Composing;
using Current = Umbraco.Core.Composing.Current;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
@@ -41,16 +44,15 @@ namespace Umbraco.Tests.PublishedContent
Composition.RegisterUnique<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
}
protected override TypeLoader CreateTypeLoader(IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
protected override TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
{
var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger);
var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger);
// this is so the model factory looks into the test assembly
pluginManager.AssembliesToScan = pluginManager.AssembliesToScan
.Union(new[] { typeof (PublishedContentMoreTests).Assembly })
.ToList();
return pluginManager;
return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(globalSettings.LocalTempPath), logger, false,
// this is so the model factory looks into the test assembly
baseLoader.AssembliesToScan
.Union(new[] {typeof(PublishedContentMoreTests).Assembly})
.ToList());
}
private UmbracoContext GetUmbracoContext()
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using NUnit.Framework;
@@ -14,6 +15,7 @@ using Moq;
using Newtonsoft.Json;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
@@ -83,16 +85,16 @@ namespace Umbraco.Tests.PublishedContent
ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType;
}
protected override TypeLoader CreateTypeLoader(IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
protected override TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
{
var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger);
var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger);
// this is so the model factory looks into the test assembly
pluginManager.AssembliesToScan = pluginManager.AssembliesToScan
.Union(new[] { typeof(PublishedContentTests).Assembly })
.ToList();
return pluginManager;
return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(globalSettings.LocalTempPath), logger, false,
// this is so the model factory looks into the test assembly
baseLoader.AssembliesToScan
.Union(new[] { typeof(PublishedContentTests).Assembly })
.ToList());
}
private readonly Guid _node1173Guid = Guid.NewGuid();
@@ -61,14 +61,16 @@ 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>()));
var typeLoader = new TypeLoader(appCaches.RuntimeCache, Current.IOHelper.MapPath("~/App_Data/TEMP"), profilingLogger);
var typeFinder = new TypeFinder(logger);
var ioHelper = IOHelper.Default;
var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger);
var mainDom = new SimpleMainDom();
var runtimeState = new RuntimeState(logger, null, null, new Lazy<IMainDom>(() => mainDom), new Lazy<IServerRegistrar>(() => factory.GetInstance<IServerRegistrar>()));
// create the register and the composition
var register = RegisterFactory.Create();
var composition = new Composition(register, typeLoader, profilingLogger, runtimeState);
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, new IOHelper());
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper);
// create the core runtime and have it compose itself
var coreRuntime = new CoreRuntime();
@@ -249,7 +251,9 @@ namespace Umbraco.Tests.Runtimes
var profilingLogger = new ProfilingLogger(logger, profiler);
var appCaches = AppCaches.Disabled;
var databaseFactory = Mock.Of<IUmbracoDatabaseFactory>();
var typeLoader = new TypeLoader(appCaches.RuntimeCache, Current.IOHelper.MapPath("~/App_Data/TEMP"), profilingLogger);
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var ioHelper = IOHelper.Default;
var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger);
var runtimeState = Mock.Of<IRuntimeState>();
Mock.Get(runtimeState).Setup(x => x.Level).Returns(RuntimeLevel.Run);
var mainDom = Mock.Of<IMainDom>();
@@ -258,7 +262,7 @@ namespace Umbraco.Tests.Runtimes
// create the register and the composition
var register = RegisterFactory.Create();
var composition = new Composition(register, typeLoader, profilingLogger, runtimeState);
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, new IOHelper());
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper);
// create the core runtime and have it compose itself
var coreRuntime = new CoreRuntime();
@@ -32,7 +32,7 @@ namespace Umbraco.Tests.Scoping
var register = RegisterFactory.Create();
var composition = new Composition(register, new TypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of<IProfilingLogger>(), ComponentTests.MockRuntimeState(RuntimeLevel.Run));
_testObjects = new TestObjects(register);
@@ -82,6 +82,8 @@ namespace Umbraco.Tests.Scoping
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
return new PublishedSnapshotService(
options,
null,
@@ -99,7 +101,8 @@ namespace Umbraco.Tests.Scoping
Factory.GetInstance<IGlobalSettings>(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
typeFinder);
}
protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null)
@@ -43,9 +43,9 @@ namespace Umbraco.Tests.Scoping
{
// this is what's created core web runtime
return new AppCaches(
new DeepCloneAppCache(new ObjectCacheAppCache()),
new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder)),
NoAppCache.Instance,
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder))));
}
[TearDown]
@@ -8,6 +8,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
@@ -53,6 +54,8 @@ namespace Umbraco.Tests.Services
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
return new PublishedSnapshotService(
options,
null,
@@ -70,7 +73,8 @@ namespace Umbraco.Tests.Services
Factory.GetInstance<IGlobalSettings>(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }),
typeFinder);
}
public class LocalServerMessenger : ServerMessengerBase
@@ -1,4 +1,5 @@
using System;
using System.IO;
using Moq;
using NPoco;
using NUnit.Framework;
@@ -36,9 +37,11 @@ namespace Umbraco.Tests.TestHelpers
var container = RegisterFactory.Create();
var ioHelper = IOHelper.Default;
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(NoAppCache.Instance,
Current.IOHelper.MapPath("~/App_Data/TEMP"),
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance,
new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")),
logger,
false);
@@ -5,6 +5,7 @@ using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Moq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Composing;
@@ -38,7 +39,8 @@ namespace Umbraco.Tests.TestHelpers.Stubs
{
if (_factory != null) return _factory(requestContext);
var types = TypeFinder.FindClassesOfType<ControllerBase>(new[] { Assembly.GetExecutingAssembly() });
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var types = typeFinder.FindClassesOfType<ControllerBase>(new[] { Assembly.GetExecutingAssembly() });
var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase));
var t = controllerTypes.SingleOrDefault();
+8 -2
View File
@@ -7,10 +7,13 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.PropertyEditors;
@@ -24,13 +27,16 @@ namespace Umbraco.Tests.TestHelpers
public static class TestHelper
{
public static TypeLoader GetMockedTypeLoader()
{
return new TypeLoader(IOHelper.Default, Mock.Of<ITypeFinder>(), Mock.Of<IAppPolicyCache>(), new DirectoryInfo(IOHelper.Default.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());
}
/// <summary>
/// Gets the current assembly directory.
/// </summary>
/// <value>The assembly directory.</value>
static public string CurrentAssemblyDirectory
public static string CurrentAssemblyDirectory
{
get
{
+3 -2
View File
@@ -230,7 +230,7 @@ namespace Umbraco.Tests.TestHelpers
return container?.TryGetInstance<T>() ?? Mock.Of<T>();
}
public IScopeProvider GetScopeProvider(ILogger logger, FileSystems fileSystems = null, IUmbracoDatabaseFactory databaseFactory = null)
public IScopeProvider GetScopeProvider(ILogger logger, ITypeFinder typeFinder = null, FileSystems fileSystems = null, IUmbracoDatabaseFactory databaseFactory = null)
{
if (databaseFactory == null)
{
@@ -241,8 +241,9 @@ namespace Umbraco.Tests.TestHelpers
databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy<IMapperCollection>(() => mappers));
}
typeFinder = typeFinder ?? new TypeFinder(logger);
fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger);
var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, logger);
var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, logger, typeFinder);
return scopeProvider;
}
}
+20 -18
View File
@@ -101,6 +101,8 @@ namespace Umbraco.Tests.Testing
protected IIOHelper IOHelper { get; private set; }
protected ITypeFinder TypeFinder { get; private set; }
protected IProfiler Profiler => Factory.GetInstance<IProfiler>();
protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance<IProfilingLogger>();
@@ -131,16 +133,19 @@ namespace Umbraco.Tests.Testing
var (logger, profiler) = GetLoggers(Options.Logger);
var proflogger = new ProfilingLogger(logger, profiler);
IOHelper = new IOHelper();
IOHelper = Umbraco.Core.IO.IOHelper.Default;
TypeFinder = new TypeFinder(logger);
var appCaches = GetAppCaches();
var globalSettings = SettingsForTests.GetDefaultGlobalSettings();
var typeLoader = GetTypeLoader(appCaches.RuntimeCache, globalSettings, proflogger, Options.TypeLoader);
var typeLoader = GetTypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, globalSettings, proflogger, Options.TypeLoader);
var register = RegisterFactory.Create();
Composition = new Composition(register, typeLoader, proflogger, ComponentTests.MockRuntimeState(RuntimeLevel.Run));
Composition.RegisterUnique(IOHelper);
Composition.RegisterUnique(TypeFinder);
Composition.RegisterUnique(typeLoader);
Composition.RegisterUnique(logger);
Composition.RegisterUnique(profiler);
@@ -270,38 +275,35 @@ namespace Umbraco.Tests.Testing
.ComposeWebMappingProfiles();
}
protected virtual TypeLoader GetTypeLoader(IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger, UmbracoTestOptions.TypeLoader option)
protected virtual TypeLoader GetTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger, UmbracoTestOptions.TypeLoader option)
{
switch (option)
{
case UmbracoTestOptions.TypeLoader.Default:
return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(runtimeCache, globalSettings, logger));
return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger));
case UmbracoTestOptions.TypeLoader.PerFixture:
return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(runtimeCache, globalSettings, logger));
return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger));
case UmbracoTestOptions.TypeLoader.PerTest:
return CreateTypeLoader(runtimeCache, globalSettings, logger);
return CreateTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger);
default:
throw new ArgumentOutOfRangeException(nameof(option));
}
}
protected virtual TypeLoader CreateTypeLoader(IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
protected virtual TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
{
return CreateCommonTypeLoader(runtimeCache, globalSettings, logger);
return CreateCommonTypeLoader(ioHelper, typeFinder, runtimeCache, globalSettings, logger);
}
// common to all tests = cannot be overriden
private static TypeLoader CreateCommonTypeLoader(IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
private static TypeLoader CreateCommonTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
{
return new TypeLoader(runtimeCache, globalSettings.LocalTempPath, logger, false)
return new TypeLoader(ioHelper, typeFinder, runtimeCache, new DirectoryInfo(globalSettings.LocalTempPath), logger, false, new[]
{
AssembliesToScan = new[]
{
Assembly.Load("Umbraco.Core"),
Assembly.Load("Umbraco.Web"),
Assembly.Load("Umbraco.Tests")
}
};
Assembly.Load("Umbraco.Core"),
Assembly.Load("Umbraco.Web"),
Assembly.Load("Umbraco.Tests")
});
}
protected virtual void ComposeDatabase(UmbracoTestOptions.Database option)
@@ -362,7 +364,7 @@ namespace Umbraco.Tests.Testing
Composition.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>(); // empty
Composition.RegisterUnique(factory
=> TestObjects.GetScopeProvider(factory.TryGetInstance<ILogger>(), factory.TryGetInstance<FileSystems>(), factory.TryGetInstance<IUmbracoDatabaseFactory>()));
=> TestObjects.GetScopeProvider(factory.TryGetInstance<ILogger>(), factory.TryGetInstance<ITypeFinder>(), factory.TryGetInstance<FileSystems>(), factory.TryGetInstance<IUmbracoDatabaseFactory>()));
Composition.RegisterUnique(factory => (IScopeAccessor) factory.GetInstance<IScopeProvider>());
Composition.ComposeServices();
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Web;
using Moq;
@@ -39,8 +40,10 @@ namespace Umbraco.Tests.Web
// FIXME: bad in a unit test - but Udi has a static ctor that wants it?!
var factory = new Mock<IFactory>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var ioHelper = IOHelper.Default;
factory.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns(
new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
factory.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext);
var settings = SettingsForTests.GetDefaultUmbracoSettings();
+6 -3
View File
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Text;
using Examine.LuceneEngine;
using Lucene.Net.Analysis;
@@ -259,13 +260,15 @@ 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 globalSettings = SettingsForTests.GenerateMockGlobalSettings();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var ioHelper = IOHelper.Default;
container
.Setup(x => x.GetInstance(typeof(TypeLoader)))
.Returns(new TypeLoader(
ioHelper,
typeFinder,
NoAppCache.Instance,
Current.IOHelper.MapPath("~/App_Data/TEMP"),
new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")),
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())
)
);
@@ -4,8 +4,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Caching;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
namespace Umbraco.Web.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="System.Web.Caching.Cache"/>.
@@ -23,7 +26,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="WebCachingAppCache"/> class.
/// </summary>
public WebCachingAppCache(System.Web.Caching.Cache cache)
public WebCachingAppCache(System.Web.Caching.Cache cache, ITypeFinder typeFinder) : base(typeFinder)
{
_cache = cache;
}
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Web.Compilation;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
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, ILogger logger, ITypeFinderConfig typeFinderConfig = null) : base(logger, typeFinderConfig)
{
if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
var isHosted = ioHelper.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 IEnumerable<string> _assembliesAcceptingLoadExceptions;
public IEnumerable<string> AssembliesAcceptingLoadExceptions
{
get
{
if (_assembliesAcceptingLoadExceptions != null)
return _assembliesAcceptingLoadExceptions;
var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions];
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
? Array.Empty<string>()
: s.Split(',').Select(x => x.Trim()).ToArray();
}
}
}
}
}
@@ -486,6 +486,7 @@ namespace Umbraco.Web.Editors
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <param name="dataTypeKey"></param>
/// <returns></returns>
public PagedResult<EntityBasic> GetPagedChildren(
string id,
@@ -8,6 +8,7 @@ using CSharpTest.Net.Collections;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -46,6 +47,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly IPublishedModelFactory _publishedModelFactory;
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly ITypeFinder _typeFinder;
// volatile because we read it with no lock
private volatile bool _isReady;
@@ -77,7 +79,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
IDataSource dataSource, IGlobalSettings globalSettings,
IEntityXmlSerializer entitySerializer,
IPublishedModelFactory publishedModelFactory,
UrlSegmentProviderCollection urlSegmentProviders)
UrlSegmentProviderCollection urlSegmentProviders,
ITypeFinder typeFinder)
: base(publishedSnapshotAccessor, variationContextAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
@@ -94,6 +97,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_defaultCultureAccessor = defaultCultureAccessor;
_globalSettings = globalSettings;
_urlSegmentProviders = urlSegmentProviders;
_typeFinder = typeFinder;
// we need an Xml serializer here so that the member cache can support XPath,
// for members this is done by navigating the serialized-to-xml member
@@ -1162,7 +1166,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_contentGen = contentSnap.Gen;
_mediaGen = mediaSnap.Gen;
_domainGen = domainSnap.Gen;
elementsCache = _elementsCache = new FastDictionaryAppCache();
elementsCache = _elementsCache = new FastDictionaryAppCache(_typeFinder);
}
}
+8 -3
View File
@@ -4,6 +4,8 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Runtime;
using Umbraco.Web.Cache;
using Umbraco.Web.Composing;
using Umbraco.Web.Logging;
namespace Umbraco.Web.Runtime
@@ -16,6 +18,7 @@ namespace Umbraco.Web.Runtime
{
private readonly UmbracoApplicationBase _umbracoApplication;
private IProfiler _webProfiler;
private BuildManagerTypeFinder _typeFinder;
/// <summary>
/// Initializes a new instance of the <see cref="WebRuntime"/> class.
@@ -56,18 +59,20 @@ namespace Umbraco.Web.Runtime
#region Getters
protected override ITypeFinder GetTypeFinder() => _typeFinder ?? (_typeFinder = new BuildManagerTypeFinder(IOHelper, Logger, new BuildManagerTypeFinder.TypeFinderConfig()));
protected override IProfiler GetProfiler() => _webProfiler;
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)
new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache)),
new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, TypeFinder)),
// we need request based cache when running in web-based context
new HttpRequestAppCache(),
new HttpRequestAppCache(() => HttpContext.Current?.Items, TypeFinder),
new IsolatedCaches(type =>
// we need to have the dep clone runtime cache provider to ensure
// all entities are cached properly (cloned in and cloned out)
new DeepCloneAppCache(new ObjectCacheAppCache())));
new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder))));
#endregion
}
@@ -7,6 +7,7 @@ using Lucene.Net.Analysis.Standard;
using Examine.LuceneEngine;
using Examine;
using Umbraco.Core;
using Umbraco.Core.Composing;
namespace Umbraco.Web.Search
{
@@ -17,10 +18,12 @@ namespace Umbraco.Web.Search
{
// TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class?
public UmbracoIndexesCreator(IProfilingLogger profilingLogger,
public UmbracoIndexesCreator(
ITypeFinder typeFinder,
IProfilingLogger profilingLogger,
ILocalizationService languageService,
IPublicAccessService publicAccessService,
IMemberService memberService, IUmbracoIndexConfig umbracoIndexConfig)
IMemberService memberService, IUmbracoIndexConfig umbracoIndexConfig) : base(typeFinder)
{
ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger));
LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService));
+2
View File
@@ -130,9 +130,11 @@
<Compile Include="Cache\IDistributedCacheBinder.cs" />
<Compile Include="Cache\UserGroupCacheRefresher.cs" />
<Compile Include="Cache\UserGroupPermissionsCacheRefresher.cs" />
<Compile Include="Cache\WebCachingAppCache.cs" />
<Compile Include="Compose\BackOfficeUserAuditEventsComposer.cs" />
<Compile Include="Compose\NotificationsComposer.cs" />
<Compile Include="Compose\PublicAccessComposer.cs" />
<Compile Include="Composing\BuildManagerTypeFinder.cs" />
<Compile Include="Composing\CompositionExtensions\Installer.cs" />
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />
<Compile Include="Compose\BackOfficeUserAuditEventsComponent.cs" />