diff --git a/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs b/src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs rename to src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs diff --git a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs similarity index 80% rename from src/Umbraco.Core/Cache/FastDictionaryAppCache.cs rename to src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs index b38f36a7d8..159f9cd7cb 100644 --- a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs @@ -10,24 +10,33 @@ namespace Umbraco.Core.Cache /// /// Implements a fast on top of a concurrent dictionary. /// - internal class FastDictionaryAppCache : IAppCache + public class FastDictionaryAppCache : IAppCache { + private readonly ITypeFinder _typeFinder; + + public FastDictionaryAppCache(ITypeFinder typeFinder) + { + _typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + } + /// /// Gets the internal items dictionary, for tests only! /// - internal readonly ConcurrentDictionary> Items = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _items = new ConcurrentDictionary>(); + + public int Count => _items.Count; /// 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 } /// public object Get(string cacheKey, Func 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 /// public IEnumerable 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 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 /// public void Clear() { - Items.Clear(); + _items.Clear(); } /// public void Clear(string key) { - Items.TryRemove(key, out _); + _items.TryRemove(key, out _); } /// 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 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 _); } /// @@ -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 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 _); } /// @@ -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 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 _); } /// 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 _); } /// 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 _); } } } diff --git a/src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs similarity index 96% rename from src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs rename to src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs index eb84423f32..57f4e7b9a2 100644 --- a/src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs +++ b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs @@ -10,8 +10,15 @@ namespace Umbraco.Core.Cache /// /// Provides a base class to fast, dictionary-based implementations. /// - 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 /// 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 diff --git a/src/Umbraco.Core/Cache/HttpRequestAppCache.cs b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs similarity index 61% rename from src/Umbraco.Core/Cache/HttpRequestAppCache.cs rename to src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs index 018726538b..41c930ae86 100644 --- a/src/Umbraco.Core/Cache/HttpRequestAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs @@ -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. /// - internal class HttpRequestAppCache : FastDictionaryAppCacheBase + public class HttpRequestAppCache : FastDictionaryAppCacheBase { - private readonly HttpContextBase _context; - /// /// Initializes a new instance of the class with a context, for unit tests! /// - public HttpRequestAppCache(HttpContextBase context) + public HttpRequestAppCache(Func requestItems, ITypeFinder typeFinder) : base(typeFinder) { - _context = context; + ContextItems = requestItems; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Will use HttpContext.Current. - /// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current - /// - public HttpRequestAppCache() - { } + private Func 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; + } /// public override object Get(string key, Func 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; // null if key not found + result = items[key] as Lazy; // 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(); + if (!TryGetContextItems(out var items)) return Enumerable.Empty(); - return ContextItems.Cast() + return items.Cast() .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 diff --git a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs similarity index 97% rename from src/Umbraco.Core/Cache/ObjectCacheAppCache.cs rename to src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs index ad7f3d6bf7..208390276a 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs @@ -13,13 +13,15 @@ namespace Umbraco.Core.Cache /// public class ObjectCacheAppCache : IAppPolicyCache { + private readonly ITypeFinder _typeFinder; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); /// /// Initializes a new instance of the . /// - 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 /// /// Gets the internal memory cache, for tests only! /// - internal ObjectCache MemoryCache { get; private set; } + public ObjectCache MemoryCache { get; private set; } /// public object Get(string key) @@ -178,7 +180,7 @@ namespace Umbraco.Core.Cache /// 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 diff --git a/src/Umbraco.Abstractions/Composing/ITypeFinder.cs b/src/Umbraco.Abstractions/Composing/ITypeFinder.cs new file mode 100644 index 0000000000..f302976dd6 --- /dev/null +++ b/src/Umbraco.Abstractions/Composing/ITypeFinder.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Used to find objects by implemented types, names and/or attributes + /// + public interface ITypeFinder + { + Type GetTypeByName(string name); + + /// + /// Return a list of found local Assemblies that Umbraco should scan for type finding + /// + /// + IEnumerable AssembliesToScan { get; } + + /// + /// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute + /// + /// + /// + /// + /// + /// + IEnumerable FindClassesOfTypeWithAttribute( + Type assignTypeFrom, + Type attributeType, + IEnumerable assemblies = null, + bool onlyConcreteClasses = true); + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + /// + IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies = null, bool onlyConcreteClasses = true); + + /// + /// Finds any classes with the attribute. + /// + /// The attribute type + /// The assemblies. + /// if set to true only concrete classes. + /// + IEnumerable FindClassesWithAttribute( + Type attributeType, + IEnumerable assemblies, + bool onlyConcreteClasses); + } +} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Abstractions/Composing/TypeFinder.cs similarity index 58% rename from src/Umbraco.Core/Composing/TypeFinder.cs rename to src/Umbraco.Abstractions/Composing/TypeFinder.cs index c07f3ae0f4..9d88153b0a 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Abstractions/Composing/TypeFinder.cs @@ -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 { - /// - /// A utility class to find all classes of a certain type by reflection in the current bin folder - /// of the web application. - /// - public static class TypeFinder + /// + public class TypeFinder : ITypeFinder { - private static volatile HashSet _localFilteredAssemblyCache; - private static readonly object LocalFilteredAssemblyCacheLocker = new object(); - private static readonly List NotifiedLoadExceptionAssemblies = new List(); - 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(); + _allAssemblies = new Lazy>(() => { - if (_assembliesAcceptingLoadExceptions != null) - return _assembliesAcceptingLoadExceptions; - - var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions]; - return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) - ? Array.Empty() - : 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 - }); - } - - /// - /// 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 - /// - /// - /// 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 - /// - internal static HashSet GetAllAssemblies() - { - return AllAssemblies.Value; - } - - //Lazy access to the all assemblies list - private static readonly Lazy> AllAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - var isHosted = Current.IOHelper.IsHosted; - + HashSet assemblies = null; try - { - if (isHosted) - { - assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - } - } - 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> _allAssemblies; + private volatile HashSet _localFilteredAssemblyCache; + private readonly object _localFilteredAssemblyCacheLocker = new object(); + private readonly List _notifiedLoadExceptionAssemblies = new List(); + private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); + 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 + }); + } /// - /// 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 /// - /// - /// - internal static HashSet GetAssembliesWithKnownExclusions( - IEnumerable excludeFromResults = null) + /// + /// 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 + /// + private IEnumerable GetAllAssemblies() { - lock (LocalFilteredAssemblyCacheLocker) - { - if (_localFilteredAssemblyCache != null) - return _localFilteredAssemblyCache; + return _allAssemblies.Value; + } - var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); - _localFilteredAssemblyCache = new HashSet(assemblies); - return _localFilteredAssemblyCache; + /// + public IEnumerable AssembliesToScan + { + get + { + lock (_localFilteredAssemblyCacheLocker) + { + if (_localFilteredAssemblyCache != null) + return _localFilteredAssemblyCache; + + var assemblies = GetFilteredAssemblies(null, KnownAssemblyExclusionFilter); + _localFilteredAssemblyCache = new HashSet(assemblies); + return _localFilteredAssemblyCache; + } } } @@ -188,7 +159,7 @@ namespace Umbraco.Core.Composing /// /// /// - private static IEnumerable GetFilteredAssemblies( + private IEnumerable GetFilteredAssemblies( IEnumerable 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" /// - internal static readonly string[] KnownAssemblyExclusionFilter = { + private static readonly string[] KnownAssemblyExclusionFilter = { "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -261,115 +232,40 @@ namespace Umbraco.Core.Composing }; /// - /// 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 /// - /// - /// - /// - public static IEnumerable FindClassesOfTypeWithAttribute() - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(GetAssembliesWithKnownExclusions(), true); - } - - /// - /// Finds any classes derived from the type T that contain the attribute TAttribute - /// - /// - /// - /// - /// - public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies) - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(assemblies, true); - } - - /// - /// Finds any classes derived from the type T that contain the attribute TAttribute - /// - /// - /// + /// + /// /// /// /// - public static IEnumerable FindClassesOfTypeWithAttribute( - IEnumerable assemblies, - bool onlyConcreteClasses) - where TAttribute : Attribute + public IEnumerable FindClassesOfTypeWithAttribute( + Type assignTypeFrom, + Type attributeType, + IEnumerable assemblies = null, + bool onlyConcreteClasses = true) { - return FindClassesOfTypeWithAttribute(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()); } /// - /// 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 /// - /// /// /// /// /// - public static IEnumerable FindClassesOfTypeWithAttribute( - Type assignTypeFrom, - IEnumerable assemblies, - bool onlyConcreteClasses) - where TAttribute : Attribute + public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable 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(false).Any()); + return GetClassesWithBaseType(assignTypeFrom, assemblyList, onlyConcreteClasses); } - - /// - /// Searches all filtered local assemblies specified for classes of the type passed in. - /// - /// - /// - public static IEnumerable FindClassesOfType() - { - return FindClassesOfType(GetAssembliesWithKnownExclusions(), true); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses) - { - if (assemblies == null) throw new ArgumentNullException(nameof(assemblies)); - - return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies) - { - return FindClassesOfType(assemblies, true); - } - - /// - /// Finds the classes with attribute. - /// - /// - /// The assemblies. - /// if set to true only concrete classes. - /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) - where T : Attribute - { - return FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); - } - + /// /// Finds any classes with the attribute. /// @@ -377,40 +273,58 @@ namespace Umbraco.Core.Composing /// The assemblies. /// if set to true only concrete classes. /// - public static IEnumerable FindClassesWithAttribute( + public IEnumerable FindClassesWithAttribute( Type attributeType, - IEnumerable assemblies, - bool onlyConcreteClasses) + IEnumerable assemblies = null, + bool onlyConcreteClasses = true) { - return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses); + var assemblyList = (assemblies ?? AssembliesToScan).ToList(); + + return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses); } /// - /// Finds the classes with attribute. + /// Returns a Type for the string type name /// - /// - /// The assemblies. + /// /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies) - where T : Attribute + public virtual Type GetTypeByName(string name) { - return FindClassesWithAttribute(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); + } - /// - /// Finds the classes with attribute in filtered local assemblies - /// - /// - /// - public static IEnumerable FindClassesWithAttribute() - where T : Attribute - { - return FindClassesWithAttribute(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 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 GetClassesWithAttribute( Type attributeType, IEnumerable 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 /// /// An additional filter to apply for what types will actually be included in the return value /// - private static IEnumerable GetClassesWithBaseType( + private IEnumerable GetClassesWithBaseType( Type baseType, IEnumerable 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 GetTypesWithFormattedException(Assembly a) + private IEnumerable 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); - } } } diff --git a/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs b/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs new file mode 100644 index 0000000000..e364790556 --- /dev/null +++ b/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + public static class TypeFinderExtensions + { + /// + /// Finds any classes derived from the type T that contain the attribute TAttribute + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable FindClassesOfTypeWithAttribute(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + where TAttribute : Attribute + => typeFinder.FindClassesOfTypeWithAttribute(typeof(T), typeof(TAttribute), assemblies, onlyConcreteClasses); + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + /// + /// + public static IEnumerable FindClassesOfType(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + => typeFinder.FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses); + + /// + /// Finds the classes with attribute. + /// + /// + /// + /// The assemblies. + /// if set to true only concrete classes. + /// + public static IEnumerable FindClassesWithAttribute(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + where T : Attribute + => typeFinder.FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); + } +} diff --git a/src/Umbraco.Abstractions/Composing/TypeHelper.cs b/src/Umbraco.Abstractions/Composing/TypeHelper.cs index 40a1adc118..28eab6a5ec 100644 --- a/src/Umbraco.Abstractions/Composing/TypeHelper.cs +++ b/src/Umbraco.Abstractions/Composing/TypeHelper.cs @@ -8,6 +8,7 @@ using System.Reflection; namespace Umbraco.Core.Composing { + /// /// 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, PropertyInfo[]>(); private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); - + private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + + /// /// 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. diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Abstractions/Composing/TypeLoader.cs similarity index 91% rename from src/Umbraco.Core/Composing/TypeLoader.cs rename to src/Umbraco.Abstractions/Composing/TypeLoader.cs index d2dffb8ffc..d2f203e112 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Abstractions/Composing/TypeLoader.cs @@ -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 { + + /// /// Provides methods to find and instantiate types. /// /// - /// This class should be used to get all types, the class should never be used directly. + /// This class should be used to get all types, the class should never be used directly. /// In most cases this class is not used directly but through extension methods that retrieve specific types. /// 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. /// - 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 _assemblies; private bool _reportedChange; - private readonly string _localTempPath; + private readonly DirectoryInfo _localTempPath; private string _fileBasePath; /// /// Initializes a new instance of the class. /// + /// + /// /// The application runtime cache. /// Files storage location. /// A profiling logger. - public TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger) - : this(runtimeCache, localTempPath, logger, true) + /// + public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, IEnumerable assembliesToScan = null) + : this(ioHelper, typeFinder, runtimeCache, localTempPath, logger, true, assembliesToScan) { } /// /// Initializes a new instance of the class. /// + /// + /// /// The application runtime cache. /// Files storage location. /// A profiling logger. /// Whether to detect changes using hashes. - internal TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger, bool detectChanges) + /// + public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, bool detectChanges, IEnumerable 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 } /// - /// Initializes a new, test/blank, instance of the class. + /// Returns the underlying /// - /// The initialized instance cannot get types. - internal TypeLoader() - { } + // ReSharper disable once MemberCanBePrivate.Global + public ITypeFinder TypeFinder { get; } /// /// Gets or sets the set of assemblies to scan. @@ -115,25 +123,21 @@ namespace Umbraco.Core.Composing /// This is for unit tests. /// // internal for tests - internal IEnumerable AssembliesToScan - { - get => _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); - set => _assemblies = value; - } + public IEnumerable AssembliesToScan => _assemblies ?? (_assemblies = TypeFinder.AssembliesToScan); /// /// Gets the type lists. /// /// For unit tests. // internal for tests - internal IEnumerable TypeLists => _types.Values; + public IEnumerable TypeLists => _types.Values; /// /// Sets a type list. /// /// For unit tests. // 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> { + // 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(new DirectoryInfo(Current.IOHelper.MapPath(SystemDirectories.Bin)), false), + new Tuple(new DirectoryInfo(_ioHelper.MapPath("~/bin")), false), // the app code folder and everything in it - new Tuple(new DirectoryInfo(Current.IOHelper.MapPath("~/App_Code")), false), + new Tuple(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(new FileInfo(Current.IOHelper.MapPath("~/global.asax")), false) + new Tuple(new FileInfo(_ioHelper.MapPath("~/global.asax")), false) }, _logger); return _currentAssembliesHash; @@ -267,7 +275,7 @@ namespace Umbraco.Core.Composing /// A profiling logger. /// The hash. // internal for tests - internal static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) + public static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) { using (logger.DebugDuration("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> TryGetCached(Type baseType, Type attributeType) + public Attempt> TryGetCached(Type baseType, Type attributeType) { var cache = _runtimeCache.GetCacheItem, IEnumerable>>(CacheKey, ReadCacheSafe, TimeSpan.FromSeconds(ListFileCacheDuration)); @@ -331,7 +339,7 @@ namespace Umbraco.Core.Composing } // internal for tests - internal Dictionary, IEnumerable> ReadCache() + public Dictionary, IEnumerable> ReadCache() { var cache = new Dictionary, IEnumerable>(); @@ -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("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(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); + _logger.Warn("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. /// - internal class TypeList + public sealed class TypeList { private readonly HashSet _types = new HashSet(); diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs new file mode 100644 index 0000000000..fd5b18ed39 --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ITypeFinderConfig + { + IEnumerable AssembliesAcceptingLoadExceptions { get; } + } +} diff --git a/src/Umbraco.Core/CompositionExtensions_Essentials.cs b/src/Umbraco.Core/CompositionExtensions_Essentials.cs index 4755e55adf..20c1646629 100644 --- a/src/Umbraco.Core/CompositionExtensions_Essentials.cs +++ b/src/Umbraco.Core/CompositionExtensions_Essentials.cs @@ -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().SqlContext); composition.RegisterUnique(typeLoader); composition.RegisterUnique(state); + composition.RegisterUnique(typeFinder); composition.RegisterUnique(ioHelper); } } diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index a81c0ab559..36215b267c 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -13,6 +13,8 @@ namespace Umbraco.Core.IO { public class IOHelper : IIOHelper { + internal static IIOHelper Default { get; } = new IOHelper(); + /// /// Gets or sets a value forcing Umbraco to consider it is non-hosted. /// diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 87cbf8faf0..90dbbe666c 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -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 /// protected IProfilingLogger ProfilingLogger { get; private set; } + /// + /// Gets the + /// + protected ITypeFinder TypeFinder { get; private set; } + /// /// Gets the /// @@ -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()' and NOT '_factory.GetInstance' @@ -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 /// public virtual void Compose(Composition composition) { - // nothing } #region Getters @@ -322,12 +336,19 @@ namespace Umbraco.Core.Runtime protected virtual IProfiler GetProfiler() => new LogProfiler(Logger); + /// + /// Gets a + /// + /// + protected virtual ITypeFinder GetTypeFinder() + => new TypeFinder(Logger); + /// /// Gets a /// /// protected virtual IIOHelper GetIOHelper() - => new Umbraco.Core.IO.IOHelper(); + => Umbraco.Core.IO.IOHelper.Default; /// /// 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. diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 84273e23da..3eabfbca9b 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -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)))); } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index 3c0fa94327..60cb83fcd5 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -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); } /// @@ -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; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4532483471..a356da0902 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -127,14 +127,8 @@ --> - - - - - - @@ -185,8 +179,6 @@ - - diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine/LuceneIndexCreator.cs index ca5aacfabc..3834c1476e 100644 --- a/src/Umbraco.Examine/LuceneIndexCreator.cs +++ b/src/Umbraco.Examine/LuceneIndexCreator.cs @@ -17,6 +17,13 @@ namespace Umbraco.Examine /// public abstract class LuceneIndexCreator : IIndexCreator { + private readonly ITypeFinder _typeFinder; + + protected LuceneIndexCreator(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + public abstract IEnumerable Create(); /// @@ -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); diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 13fabe5ca2..77200be86e 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -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()); + _provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, typeFinder)); } internal override IAppCache AppCache => _provider; diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index 650cc64720..4ef08a15f5 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.RegisterUnique(_ => new TestServerRegistrar()); composition.RegisterUnique(_ => new TestServerMessenger()); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index 042830e059..b9c948c1de 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -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()); _ctx = new FakeHttpContextFactory("http://localhost/test"); - _appCache = new HttpRequestAppCache(_ctx.HttpContext); + _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } internal override IAppCache AppCache diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index b9c729f891..0fb8e574a8 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -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()); + _provider = new ObjectCacheAppCache(typeFinder); } internal override IAppCache AppCache diff --git a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs b/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs index e732ae5766..02986e2f78 100644 --- a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs @@ -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()); + _appCache = new WebCachingAppCache(HttpRuntime.Cache, typeFinder); } internal override IAppCache AppCache => _appCache; diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 0e72d6d0fe..5a437e402f 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -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(); var logger = Mock.Of(); + var typeFinder = new TypeFinder(logger); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty()))); 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(), Mock.Of())); @@ -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(), Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); } 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()); + var ioHelper = IOHelper.Default; + var typeFinder = new TypeFinder(Mock.Of()); + var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index ec757e09f0..339bc7a3aa 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + _composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); } [TearDown] diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index f7d2c869b4..8a0dc46e7f 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -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(), Mock.Of()); - TypeLoader = new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), ProfilingLogger, detectChanges: false) - { - AssembliesToScan = AssembliesToScan - }; + var typeFinder = new TypeFinder(Mock.Of()); + var ioHelper = IOHelper.Default; + TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } [TearDown] diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index 77453e86b4..5516d6cab8 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -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(), Mock.Of()); - var typeLoader = new TypeLoader(Mock.Of(), Current.IOHelper.MapPath("~/App_Data/TEMP"), logger); + var typeFinder = new TypeFinder(Mock.Of()); + var ioHelper = IOHelper.Default; + var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of()); // create the factory, ensure it is the mocked factory diff --git a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs index 3996eba89f..ed5a6e3ee2 100644 --- a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs +++ b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs @@ -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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Add() @@ -66,7 +67,7 @@ namespace Umbraco.Tests.Composing public void LazyCollectionBuilderHandlesProducers() { var container = CreateRegister(); - var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Add() @@ -117,7 +118,7 @@ namespace Umbraco.Tests.Composing public void LazyCollectionBuilderThrowsOnIllegalTypes() { var container = CreateRegister(); - var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Add() @@ -139,7 +140,7 @@ namespace Umbraco.Tests.Composing public void LazyCollectionBuilderCanExcludeTypes() { var container = CreateRegister(); - var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Add() diff --git a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs index 07db4be3d3..5586f6baa0 100644 --- a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs +++ b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs @@ -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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Add(() => TypeLoader.GetPackageActions()); diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 579ee081ed..fd070dee2a 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -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(_assemblies); + var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } - //[Test] - //public void Find_Classes_Of_Type() - //{ - // var typesFound = TypeFinder.FindClassesOfType(_assemblies); - // var originalTypesFound = TypeFinderOriginal.FindClassesOfType(_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(_assemblies); + var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] - typesFound = TypeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); + typesFound = typeFinder.FindClassesWithAttribute(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 LocalFilteredAssemblyCache = new ConcurrentBag(); - private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim(); - private static ReadOnlyCollection _allAssemblies = null; - private static ReadOnlyCollection _binFolderAssemblies = null; - private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); - - /// - /// 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 - /// - /// - /// 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 - /// - internal static IEnumerable GetAllAssemblies() - { - if (_allAssemblies == null) - { - using (new WriteLock(Locker)) - { - List assemblies = null; - try - { - var isHosted = HttpContext.Current != null; - - try - { - if (isHosted) - { - assemblies = new List(BuildManager.GetReferencedAssemblies().Cast()); - } - } - 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(); - 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(assemblies); - - } - catch (InvalidOperationException e) - { - if (!(e.InnerException is SecurityException)) - throw; - - _binFolderAssemblies = _allAssemblies; - } - } - } - - return _allAssemblies; - } - - /// - /// Returns only assemblies found in the bin folder that have been loaded into the app domain. - /// - /// - /// - /// This will be used if we implement App_Plugins from Umbraco v5 but currently it is not used. - /// - internal static IEnumerable 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(); - var binFolderAssemblies = new List(); - - 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(binFolderAssemblies); - } - } - return _binFolderAssemblies; - } - - /// - /// 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. - /// - /// - /// - internal static IEnumerable GetAssembliesWithKnownExclusions( - IEnumerable 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; - } - - /// - /// Return a list of found local Assemblies and exluding the ones passed in and excluding the exclusion list filter - /// - /// - /// - /// - private static IEnumerable GetFilteredAssemblies( - IEnumerable excludeFromResults = null, - string[] exclusionFilter = null) - { - if (excludeFromResults == null) - excludeFromResults = new List(); - if (exclusionFilter == null) - exclusionFilter = new string[] { }; - - return GetAllAssemblies() - .Where(x => !excludeFromResults.Contains(x) - && !x.GlobalAssemblyCache - && !exclusionFilter.Any(f => x.FullName.StartsWith(f))); - } - - /// - /// this is our assembly filter to filter out known types that def dont contain types we'd like to find or plugins - /// - /// - /// 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 - /// - 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 FindClassesOfTypeWithAttribute() - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(GetAssembliesWithKnownExclusions(), true); - } - - public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies) - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(assemblies, true); - } - - public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) - where TAttribute : Attribute - { - if (assemblies == null) throw new ArgumentNullException("assemblies"); - - var l = new List(); - foreach (var a in assemblies) - { - var types = from t in GetTypesWithFormattedException(a) - where !t.IsInterface - && typeof(T).IsAssignableFrom(t) - && t.GetCustomAttributes(false).Any() - && (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract)) - select t; - l.AddRange(types); - } - - return l; - } - - /// - /// Searches all filtered local assemblies specified for classes of the type passed in. - /// - /// - /// - public static IEnumerable FindClassesOfType() - { - return FindClassesOfType(GetAssembliesWithKnownExclusions(), true); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses) - { - if (assemblies == null) throw new ArgumentNullException("assemblies"); - - return GetAssignablesFromType(assemblies, onlyConcreteClasses); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies) - { - return FindClassesOfType(assemblies, true); - } - - /// - /// Finds the classes with attribute. - /// - /// - /// The assemblies. - /// if set to true only concrete classes. - /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) - where T : Attribute - { - return FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); - } - - /// - /// Finds the classes with attribute. - /// - /// The attribute type - /// The assemblies. - /// if set to true only concrete classes. - /// - public static IEnumerable FindClassesWithAttribute(Type type, IEnumerable assemblies, bool onlyConcreteClasses) - { - if (assemblies == null) throw new ArgumentNullException("assemblies"); - if (!TypeHelper.IsTypeAssignableFrom(type)) - throw new ArgumentException("The type specified: " + type + " is not an Attribute type"); - - var l = new List(); - 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; - } - - /// - /// Finds the classes with attribute. - /// - /// - /// The assemblies. - /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies) - where T : Attribute - { - return FindClassesWithAttribute(assemblies, true); - } - - /// - /// Finds the classes with attribute in filtered local assemblies - /// - /// - /// - public static IEnumerable FindClassesWithAttribute() - where T : Attribute - { - return FindClassesWithAttribute(GetAssembliesWithKnownExclusions()); - } - - - #region Private methods - - /// - /// Gets a collection of assignables of type T from a collection of assemblies - /// - /// - /// - /// - /// - private static IEnumerable GetAssignablesFromType(IEnumerable assemblies, bool onlyConcreteClasses) - { - return GetTypes(typeof(T), assemblies, onlyConcreteClasses); - } - - private static IEnumerable GetTypes(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses) - { - var l = new List(); - 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 GetTypesWithFormattedException(Assembly a) - { - //if the assembly is dynamic, do not try to scan it - if (a.IsDynamic) - return Enumerable.Empty(); - - 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 - - - - } } diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index d22b534d2b..5163e6a4e8 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -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(), Mock.Of()), false); + var typeFinder = new TypeFinder(Mock.Of()); + _typeLoader = new TypeLoader(IOHelper.Default, typeFinder, NoAppCache.Instance, + new DirectoryInfo(IOHelper.Default.MapPath("~/App_Data/TEMP")), + new ProfilingLogger(Mock.Of(), Mock.Of()), 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()); } /// @@ -280,16 +286,16 @@ AnotherContentFinder { var types = new HashSet(); - 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); } diff --git a/src/Umbraco.Tests/CoreThings/UdiTests.cs b/src/Umbraco.Tests/CoreThings/UdiTests.cs index 8c790c0784..dc2265eac2 100644 --- a/src/Umbraco.Tests/CoreThings/UdiTests.cs +++ b/src/Umbraco.Tests/CoreThings/UdiTests.cs @@ -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(); - var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); + var ioHelper = IOHelper.Default; + var typeFinder = new TypeFinder(Mock.Of()); container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns( - new TypeLoader(NoAppCache.Instance, Current.IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of(), Mock.Of()))); + new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()))); Current.Factory = container.Object; Udi.ResetUdiTypes(); diff --git a/src/Umbraco.Tests/IO/FileSystemsTests.cs b/src/Umbraco.Tests/IO/FileSystemsTests.cs index 872d7c2526..254371ae32 100644 --- a/src/Umbraco.Tests/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests/IO/FileSystemsTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.IO { _register = RegisterFactory.Create(); - var composition = new Composition(_register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(_register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.Register(_ => Mock.Of()); composition.Register(_ => Mock.Of()); diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index 9cc1d14954..0c57c70742 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -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()); //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(), Mock.Of())); Current.Reset(); diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 524d6fe5b1..ffbf462b8e 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -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())); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 4d62ec8301..af27d47f97 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -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()) diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index d04382e32e..fa916b554d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -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()) diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 8d2ab84d35..5bea491973 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -70,7 +70,7 @@ namespace Umbraco.Tests.PropertyEditors try { var container = RegisterFactory.Create(); - var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(container, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder(); diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs index c7e889c84b..06944c14a7 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -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(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); register.Register(_ => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()))); diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 671129848c..360df5daeb 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -178,7 +178,7 @@ namespace Umbraco.Tests.Published Current.Reset(); var register = RegisterFactory.Create(); - var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); composition.WithCollectionBuilder() .Append() diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 9db539d142..fe2b080310 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -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()); + var elementsCache = new FastDictionaryAppCache(typeFinder); + var snapshotCache = new FastDictionaryAppCache(typeFinder); var publishedSnapshot = new Mock(); 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] diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 5d32606ee7..564e129650 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -138,6 +138,8 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); + var typeFinder = new TypeFinder(Mock.Of()); + // 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(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + typeFinder); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 0e05e6baad..d3a4a0d082 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -181,6 +181,8 @@ namespace Umbraco.Tests.PublishedContent // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); + var typeFinder = new TypeFinder(Mock.Of()); + // 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(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + typeFinder); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index 7e6ae75356..d0e780a92b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -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(f => new PublishedModelFactory(f.GetInstance().GetTypes())); } - 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() diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 6ef632bf90..b42dc32b24 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -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(); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index d978c70476..7ff2e6e7b8 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -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(() => factory.GetInstance())); - 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(() => mainDom), new Lazy(() => factory.GetInstance())); // 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(); - var typeLoader = new TypeLoader(appCaches.RuntimeCache, Current.IOHelper.MapPath("~/App_Data/TEMP"), profilingLogger); + var typeFinder = new TypeFinder(Mock.Of()); + var ioHelper = IOHelper.Default; + var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); Mock.Get(runtimeState).Setup(x => x.Level).Returns(RuntimeLevel.Run); var mainDom = Mock.Of(); @@ -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(); diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 001553a8ae..74e79c4ce3 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Scoping var register = RegisterFactory.Create(); - var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); _testObjects = new TestObjects(register); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index c7c403b260..0d2243c6dc 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -82,6 +82,8 @@ namespace Umbraco.Tests.Scoping var mediaRepository = Mock.Of(); var memberRepository = Mock.Of(); + var typeFinder = new TypeFinder(Mock.Of()); + return new PublishedSnapshotService( options, null, @@ -99,7 +101,8 @@ namespace Umbraco.Tests.Scoping Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), - 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 urlProviders = null) diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index c7e4ddcb19..ae4544bf87 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -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] diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 0fe05f385f..bc515cfea3 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -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(); var memberRepository = Mock.Of(); + var typeFinder = new TypeFinder(Mock.Of()); + return new PublishedSnapshotService( options, null, @@ -70,7 +73,8 @@ namespace Umbraco.Tests.Services Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + typeFinder); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index eb3eb8c483..e1e987c4bf 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -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(), Mock.Of()); - var typeLoader = new TypeLoader(NoAppCache.Instance, - Current.IOHelper.MapPath("~/App_Data/TEMP"), + var typeFinder = new TypeFinder(Mock.Of()); + var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, + new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, false); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index e27fe17bbe..273f7a996e 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -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(new[] { Assembly.GetExecutingAssembly() }); + var typeFinder = new TypeFinder(Mock.Of()); + var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); var t = controllerTypes.SingleOrDefault(); diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 647797a500..e07dbe1e5a 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -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(), Mock.Of(), new DirectoryInfo(IOHelper.Default.MapPath("~/App_Data/TEMP")), Mock.Of()); + } /// /// Gets the current assembly directory. /// /// The assembly directory. - static public string CurrentAssemblyDirectory + public static string CurrentAssemblyDirectory { get { diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 2db17a5312..280632fdae 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -230,7 +230,7 @@ namespace Umbraco.Tests.TestHelpers return container?.TryGetInstance() ?? Mock.Of(); } - 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(() => 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; } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 4f14a48c92..8b2977dab4 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -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(); protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance(); @@ -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(); // empty Composition.RegisterUnique(factory - => TestObjects.GetScopeProvider(factory.TryGetInstance(), factory.TryGetInstance(), factory.TryGetInstance())); + => TestObjects.GetScopeProvider(factory.TryGetInstance(), factory.TryGetInstance(), factory.TryGetInstance(), factory.TryGetInstance())); Composition.RegisterUnique(factory => (IScopeAccessor) factory.GetInstance()); Composition.ComposeServices(); diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index b1c49b511d..e3678a1db5 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -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(); + var typeFinder = new TypeFinder(Mock.Of()); + 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(), Mock.Of()))); + new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()))); factory.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext); var settings = SettingsForTests.GetDefaultUmbracoSettings(); diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index ead2f850fd..cdf17e12e9 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -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(); - var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - + var typeFinder = new TypeFinder(Mock.Of()); + 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(), Mock.Of()) ) ); diff --git a/src/Umbraco.Core/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs similarity index 98% rename from src/Umbraco.Core/Cache/WebCachingAppCache.cs rename to src/Umbraco.Web/Cache/WebCachingAppCache.cs index 362df8f603..1879e8b69b 100644 --- a/src/Umbraco.Core/Cache/WebCachingAppCache.cs +++ b/src/Umbraco.Web/Cache/WebCachingAppCache.cs @@ -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 { /// /// Implements on top of a . @@ -23,7 +26,7 @@ namespace Umbraco.Core.Cache /// /// Initializes a new instance of the class. /// - public WebCachingAppCache(System.Web.Caching.Cache cache) + public WebCachingAppCache(System.Web.Caching.Cache cache, ITypeFinder typeFinder) : base(typeFinder) { _cache = cache; } diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs new file mode 100644 index 0000000000..15c8d6d8d6 --- /dev/null +++ b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs @@ -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 +{ + /// + /// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites + /// + /// + /// This finder will also try to resolve dynamic assemblies created from App_Code + /// + 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>(() => + { + var isHosted = ioHelper.IsHosted; + try + { + if (isHosted) + { + var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); + + //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(base.AssembliesToScan); + }); + } + + private readonly Lazy> _allAssemblies; + + /// + /// Explicitly implement and return result from BuildManager + /// + /// + /// + Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false); + + /// + /// Explicitly implement and return result from BuildManager + /// + IEnumerable ITypeFinder.AssembliesToScan => _allAssemblies.Value; + + /// + /// TypeFinder config via appSettings + /// + internal class TypeFinderConfig : ITypeFinderConfig + { + private IEnumerable _assembliesAcceptingLoadExceptions; + public IEnumerable AssembliesAcceptingLoadExceptions + { + get + { + if (_assembliesAcceptingLoadExceptions != null) + return _assembliesAcceptingLoadExceptions; + + var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions]; + return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) + ? Array.Empty() + : s.Split(',').Select(x => x.Trim()).ToArray(); + } + } + } + } +} diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 7113abd6eb..aa8f735cf2 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -486,6 +486,7 @@ namespace Umbraco.Web.Editors /// /// /// + /// /// public PagedResult GetPagedChildren( string id, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index eae2f33979..554e76c665 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -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); } } diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 13cd717fd1..2f1ef43e4c 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -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; /// /// Initializes a new instance of the 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 } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index ec536b9d75..b9dbc7d996 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -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)); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index bede1a1a6d..589880f70c 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -130,9 +130,11 @@ + +