From c12f9d027205aa5d0dfadf1e990138613ab0e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Skyldahl=20S=C3=B8rensen?= Date: Wed, 17 Apr 2013 14:03:54 -0200 Subject: [PATCH] Optimization for issue: U4-1187 Url: http://issues.umbraco.org/issue/U4-1187 Added assembly filtering, to stop scanning of third party assemblies that doesn't reference the types we are looking for. this limits the amount of possible missing references we encounter when checking against the types --- src/Umbraco.Core/TypeFinder.cs | 178 ++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 71 deletions(-) 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