diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index 6b7627e257..2630133a62 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -2,23 +2,18 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Linq.Expressions; using System.Reflection; using System.Security; using System.Text; using System.Threading; using System.Web; using System.Web.Compilation; -using System.Web.Hosting; -using Umbraco.Core.Configuration; using Umbraco.Core.IO; namespace Umbraco.Core { - //TODO: Get the App_Code stuff in here from the old one /// @@ -27,7 +22,6 @@ namespace Umbraco.Core /// public static class TypeFinder { - private static readonly ConcurrentBag LocalFilteredAssemblyCache = new ConcurrentBag(); private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim(); private static ReadOnlyCollection _allAssemblies = null; @@ -86,8 +80,7 @@ namespace Umbraco.Core } //now set the _allAssemblies - _allAssemblies = new ReadOnlyCollection(assemblies); - + _allAssemblies = new ReadOnlyCollection(assemblies); } catch (InvalidOperationException e) { @@ -100,8 +93,8 @@ namespace Umbraco.Core } return _allAssemblies; - } - + } + /// /// Returns only assemblies found in the bin folder that have been loaded into the app domain. /// @@ -111,7 +104,6 @@ namespace Umbraco.Core /// internal static IEnumerable GetBinAssemblies() { - if (_binFolderAssemblies == null) { using (new WriteLock(Locker)) @@ -143,7 +135,8 @@ namespace Umbraco.Core { try { - var foundAssembly = safeDomainAssemblies.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile()); + var foundAssembly = + safeDomainAssemblies.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile()); if (foundAssembly != null) { binFolderAssemblies.Add(foundAssembly); @@ -162,7 +155,7 @@ namespace Umbraco.Core } } return _binFolderAssemblies; - } + } /// /// Return a list of found local Assemblies excluding the known assemblies we don't want to scan @@ -181,7 +174,7 @@ namespace Umbraco.Core assemblies.ForEach(LocalFilteredAssemblyCache.Add); } return LocalFilteredAssemblyCache; - } + } /// /// Return a list of found local Assemblies and exluding the ones passed in and excluding the exclusion list filter @@ -190,18 +183,18 @@ namespace Umbraco.Core /// /// private static IEnumerable GetFilteredAssemblies( - IEnumerable excludeFromResults = null, + IEnumerable excludeFromResults = null, string[] exclusionFilter = null) { if (excludeFromResults == null) excludeFromResults = new List(); if (exclusionFilter == null) - exclusionFilter = new string[] { }; + exclusionFilter = new string[] {}; return GetAllAssemblies() .Where(x => !excludeFromResults.Contains(x) - && !x.GlobalAssemblyCache - && !exclusionFilter.Any(f => x.FullName.StartsWith(f))); + && !x.GlobalAssemblyCache + && !exclusionFilter.Any(f => x.FullName.StartsWith(f))); } /// @@ -211,42 +204,42 @@ namespace Umbraco.Core /// 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,", - "log4net,", - "Microsoft.", - "Newtonsoft.", - "NHibernate.", - "NHibernate,", - "NuGet.", - "RouteDebugger,", - "SqlCE4Umbraco,", - "umbraco.datalayer,", - "umbraco.interfaces,", - "umbraco.providers,", - "Umbraco.Web.UI,", - "umbraco.webservices", - "Lucene.", - "Examine,", - "Examine.", - "ServiceStack.", - "MySql.", - "HtmlAgilityPack.", - "TidyNet.", - "ICSharpCode.", - "CookComputing." - }; + { + "mscorlib,", + "System.", + "Antlr3.", + "Autofac.", + "Autofac,", + "Castle.", + "ClientDependency.", + "DataAnnotationsExtensions.", + "DataAnnotationsExtensions,", + "Dynamic,", + "HtmlDiff,", + "Iesi.Collections,", + "log4net,", + "Microsoft.", + "Newtonsoft.", + "NHibernate.", + "NHibernate,", + "NuGet.", + "RouteDebugger,", + "SqlCE4Umbraco,", + "umbraco.datalayer,", + "umbraco.interfaces,", + "umbraco.providers,", + "Umbraco.Web.UI,", + "umbraco.webservices", + "Lucene.", + "Examine,", + "Examine.", + "ServiceStack.", + "MySql.", + "HtmlAgilityPack.", + "TidyNet.", + "ICSharpCode.", + "CookComputing." + }; public static IEnumerable FindClassesOfTypeWithAttribute() where TAttribute : Attribute @@ -260,13 +253,19 @@ namespace Umbraco.Core return FindClassesOfTypeWithAttribute(assemblies, true); } - public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) + public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies, + bool onlyConcreteClasses) where TAttribute : Attribute { if (assemblies == null) throw new ArgumentNullException("assemblies"); + // a assembly cant contain types that are assignable to a type it doesn't reference + assemblies = RemoveAssembliesThatDontReferenceAssemblyOfType(typeof (T), assemblies); + // a assembly cant contain types with a attribute it doesn't reference + assemblies = RemoveAssembliesThatDontReferenceAssemblyOfType(typeof (TAttribute), assemblies); + var l = new List(); - foreach(var a in assemblies) + foreach (var a in assemblies) { var types = from t in GetTypesWithFormattedException(a) where !t.IsInterface @@ -325,7 +324,7 @@ namespace Umbraco.Core public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) where T : Attribute { - return FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); + return FindClassesWithAttribute(typeof (T), assemblies, onlyConcreteClasses); } /// @@ -335,23 +334,58 @@ namespace Umbraco.Core /// The assemblies. /// if set to true only concrete classes. /// - public static IEnumerable FindClassesWithAttribute(Type type, IEnumerable assemblies, bool onlyConcreteClasses) + 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"); + // a assembly cant contain types with a attribute it doesn't reference + assemblies = RemoveAssembliesThatDontReferenceAssemblyOfType(type, assemblies); 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; + where + !t.IsInterface && t.GetCustomAttributes(type, false).Any() && + (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract)) + select t; l.AddRange(types); } return l; } + /// + /// Removes assemblies that doesn't reference the assembly of the type we are looking for. + /// + /// + /// + /// + private static IEnumerable RemoveAssembliesThatDontReferenceAssemblyOfType(Type type, + IEnumerable assemblies) + { + // Avoid scanning assembly if it doesn't contain a reference to the assembly containing the type we are looking for + // to the assembly containing the attribute we are looking for + var assemblyNameOfType = type.Assembly.GetName().Name; + + return assemblies + .Where(assembly => assembly == type.Assembly + || HasReferenceToAssemblyWithName(assembly, assemblyNameOfType)).ToList(); + } + /// + /// checks if the assembly has a reference with the same name as the expected assembly name. + /// + /// + /// + /// + private static bool HasReferenceToAssemblyWithName(Assembly assembly, string expectedAssemblyName) + { + return assembly + .GetReferencedAssemblies() + .Select(a => a.Name) + .Contains(expectedAssemblyName, StringComparer.Ordinal); + } /// /// Finds the classes with attribute. @@ -376,9 +410,8 @@ namespace Umbraco.Core return FindClassesWithAttribute(GetAssembliesWithKnownExclusions()); } - #region Private methods - + /// /// Gets a collection of assignables of type T from a collection of assemblies /// @@ -388,20 +421,26 @@ namespace Umbraco.Core /// private static IEnumerable GetAssignablesFromType(IEnumerable assemblies, bool onlyConcreteClasses) { - return GetTypes(typeof(T), assemblies, onlyConcreteClasses); + return GetTypes(typeof (T), assemblies, onlyConcreteClasses); } - private static IEnumerable GetTypes(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses) + private static IEnumerable GetTypes(Type assignTypeFrom, IEnumerable assemblies, + bool onlyConcreteClasses) { + // a assembly cant contain types that are assignable to a type it doesn't reference + assemblies = RemoveAssembliesThatDontReferenceAssemblyOfType(assignTypeFrom, assemblies); + 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)) + where + !t.IsInterface && assignTypeFrom.IsAssignableFrom(t) && + (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract)) select t; l.AddRange(types); } - return l; + return l; } private static IEnumerable GetTypesWithFormattedException(Assembly a) @@ -420,15 +459,12 @@ namespace Umbraco.Core sb.AppendLine("Could not load types from assembly " + a.FullName + ", errors:"); foreach (var loaderException in ex.LoaderExceptions.WhereNotNull()) { - sb.AppendLine("Exception: " + loaderException.ToString()); + sb.AppendLine("Exception: " + loaderException); } throw new ReflectionTypeLoadException(ex.Types, ex.LoaderExceptions, sb.ToString()); } } #endregion - - - } -} +} \ No newline at end of file