From 58fbe9f4ab92ab0fbbae9be5f542a75f9537ec57 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 06:24:57 +0600 Subject: [PATCH 1/8] Added new TypeFinder methods for resolving types with attributes. Updated the PluginResolver to resolve IApplication --- src/Umbraco.Core/PluginTypeResolver.cs | 31 +++++-- src/Umbraco.Core/TypeFinder2.cs | 89 ++++++------------- src/Umbraco.Tests/TypeFinderBenchmarkTests.cs | 34 ++++++- .../ApplicationRegistrar.cs | 6 +- .../PluginTypeResolverExtensions.cs | 10 +++ 5 files changed, 95 insertions(+), 75 deletions(-) diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index 426029de61..d2e373fdd5 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -73,14 +73,9 @@ namespace Umbraco.Core } } return instances; - } + } - /// - /// Generic method to find the specified type and cache the result - /// - /// - /// - internal IEnumerable ResolveTypes() + private IEnumerable ResolveTypes(Func> finder) { using (var readLock = new UpgradeableReadLock(_lock)) { @@ -93,7 +88,7 @@ namespace Umbraco.Core typeList = new TypeList(); - foreach (var t in TypeFinder.FindClassesOfType()) + foreach (var t in finder()) { typeList.AddType(t); } @@ -103,8 +98,28 @@ namespace Umbraco.Core } return typeList.GetTypes(); } + } + /// + /// Generic method to find the specified type and cache the result + /// + /// + /// + internal IEnumerable ResolveTypes() + { + return ResolveTypes(() => TypeFinder.FindClassesOfType()); + } + /// + /// Generic method to find the specified type that has an attribute and cache the result + /// + /// + /// + /// + internal IEnumerable ResolveTypesWithAttribute() + where TAttribute : Attribute + { + return ResolveTypes(() => TypeFinder.FindClassesOfTypeWithAttribute()); } /// diff --git a/src/Umbraco.Core/TypeFinder2.cs b/src/Umbraco.Core/TypeFinder2.cs index 8f9f67b4bc..d5641be4a5 100644 --- a/src/Umbraco.Core/TypeFinder2.cs +++ b/src/Umbraco.Core/TypeFinder2.cs @@ -16,6 +16,9 @@ using System.Web.Hosting; namespace Umbraco.Core { + + //TODO: Get the App_Code stuff in here from the old one + /// /// A utility class to find all classes of a certain type by reflection in the current bin folder /// of the web application. @@ -252,6 +255,32 @@ namespace Umbraco.Core return FindClassesOfType(new[] { assembly }); } + public IEnumerable FindClassesOfTypeWithAttribute() + where TAttribute : Attribute + { + return FindClassesOfTypeWithAttribute(GetAssembliesWithKnownExclusions(), true); + } + + public IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies) + where TAttribute : Attribute + { + return FindClassesOfTypeWithAttribute(assemblies, true); + } + + public IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) + where TAttribute : Attribute + { + if (assemblies == null) throw new ArgumentNullException("assemblies"); + + return (from a in assemblies + from t in GetTypesWithFormattedException(a) + where !t.IsInterface + && typeof(T).IsAssignableFrom(t) + && t.GetCustomAttributes(false).Any() + && (!onlyConcreteClasses || (t.IsClass && !t.IsAbstract)) + select t).ToList(); + } + /// /// Searches all filtered local assemblies specified for classes of the type passed in. /// @@ -262,32 +291,6 @@ namespace Umbraco.Core return FindClassesOfType(GetAssembliesWithKnownExclusions(), true); } - /// - /// Searches all assemblies specified for classes of the type passed in. - /// - /// - /// - /// - public IEnumerable FindClassesOfType(IEnumerable assemblyFiles) - { - return FindClassesOfType(assemblyFiles, true); - } - - /// - /// Searches all assemblies specified for classes of the type passed in. - /// - /// - /// - /// Only resolve concrete classes that can be instantiated - /// - public IEnumerable FindClassesOfType(IEnumerable assemblyFiles, bool onlyConcreteClasses) - { - if (assemblyFiles == null) throw new ArgumentNullException("assemblyFiles"); - - var typeAndAssembly = GetAssignablesFromType(assemblyFiles, onlyConcreteClasses); - return GetTypesFromResult(typeAndAssembly); - } - /// /// Returns all types found of in the assemblies specified of type T /// @@ -354,40 +357,6 @@ namespace Umbraco.Core return FindClassesWithAttribute(GetAssembliesWithKnownExclusions()); } - #region Internal Methods - For testing - /// - /// Used to find all types in all assemblies in the specified folder - /// - /// - /// - /// - /// - /// - /// This has potential cost alot in performance so it is marked internal and should ONLY be used - /// where absolutely necessary (i.e. Tests) - /// - internal IEnumerable FindClassesOfType(DirectoryInfo folder, bool onlyConcreteClasses) - { - var types = new List(); - types.AddRange(FindClassesOfType(folder.GetFiles("*.dll").Select(x => x.FullName))); - return types; - } - - /// - /// Used to find all types in all assemblies in the specified folder - /// - /// - /// - /// - /// - /// This has potential cost alot in performance so it is marked internal and should ONLY be used - /// where absolutely necessary (i.e. Tests) - /// - internal IEnumerable FindClassesOfType(DirectoryInfo folder) - { - return FindClassesOfType(folder, true); - } - #endregion #region Internal Attributed Assembly class diff --git a/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs b/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs index 7ad7277ca6..97954bcb85 100644 --- a/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs +++ b/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs @@ -16,16 +16,15 @@ using umbraco.businesslogic; using umbraco.cms.businesslogic; using umbraco.uicontrols; -[assembly: TypeFinderBenchmarkTests.AssemblyContainsPluginsAttribute] +[assembly: TypeFinderTests.AssemblyContainsPluginsAttribute] namespace Umbraco.Tests { /// /// Full Trust benchmark tests for typefinder and the old typefinder /// - [TestFixture] - [Ignore("This is a benchark test")] - public class TypeFinderBenchmarkTests + [TestFixture] + public class TypeFinderTests { /// /// List of assemblies to scan @@ -73,18 +72,43 @@ namespace Umbraco.Tests { } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class MyTestAttribute : Attribute + { + + } + public abstract class TestEditor { } + [MyTestAttribute] public class BenchmarkTestEditor : TestEditor { } + [MyTestAttribute] + public class MyOtherTestEditor : TestEditor + { + + } + + [Test] + public void Get_Type_With_Attribute() + { + + var finder2 = new Umbraco.Core.TypeFinder2(); + + var typesFound = finder2.FindClassesOfTypeWithAttribute(_assemblies); + + Assert.AreEqual(2, typesFound.Count()); + + } [Test] + [Ignore("This is a benchark test")] public void Benchmark_Old_TypeFinder_vs_New_TypeFinder_FindClassesWithAttribute() { var timer = new Stopwatch(); @@ -107,6 +131,7 @@ namespace Umbraco.Tests } [Test] + [Ignore("This is a benchark test")] public void Benchmark_Old_TypeFinder_vs_New_TypeFinder_FindClassesOfType() { var timer = new Stopwatch(); @@ -133,6 +158,7 @@ namespace Umbraco.Tests /// cache files created instead since this is clearly a TON faster. /// [Test] + [Ignore("This is a benchark test")] public void Benchmark_Finding_First_Type_In_Assemblies() { var timer = new Stopwatch(); diff --git a/src/umbraco.businesslogic/ApplicationRegistrar.cs b/src/umbraco.businesslogic/ApplicationRegistrar.cs index d8ee358a93..47139638f3 100644 --- a/src/umbraco.businesslogic/ApplicationRegistrar.cs +++ b/src/umbraco.businesslogic/ApplicationRegistrar.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Xml.Linq; +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.DataLayer; using umbraco.businesslogic; @@ -29,10 +30,9 @@ namespace umbraco.BusinessLogic public ApplicationRegistrar() { // Load all Applications by attribute and add them to the XML config - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType() - .Where(x => x.GetCustomAttributes(typeof(ApplicationAttribute), false).Any()); + var types = PluginTypeResolver.Current.ResolveApplications(); + //we can have multiple attributes so we'll query for them var attrs = types.Select(x => (ApplicationAttribute)x.GetCustomAttributes(typeof(ApplicationAttribute), false).Single()) .Where(x => Application.getByAlias(x.Alias) == null); diff --git a/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs b/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs index 924403820c..f3acbfd55e 100644 --- a/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs +++ b/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs @@ -20,5 +20,15 @@ namespace umbraco.businesslogic return resolver.ResolveTypes(); } + /// + /// Returns all available IApplication object + /// + /// + /// + internal static IEnumerable ResolveApplications(this PluginTypeResolver resolver) + { + return resolver.ResolveTypesWithAttribute(); + } + } } \ No newline at end of file From 09a68f93763ff9f86f00e49730629a6549183bc6 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 10:37:12 +0600 Subject: [PATCH 2/8] Updated ApplicationTreeRegistrar to use PluginTypeResolver, created unit tests for resolving trees/applications --- src/Umbraco.Core/PluginTypeResolver.cs | 16 +++++- src/Umbraco.Tests/PluginTypeResolverTests.cs | 49 +++++++++++++++++++ src/Umbraco.Tests/TypeFinderBenchmarkTests.cs | 2 - .../ApplicationRegistrar.cs | 6 ++- .../ApplicationTreeRegistrar.cs | 14 +++--- .../PluginTypeResolverExtensions.cs | 12 ++++- 6 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index d2e373fdd5..be16fc6ff3 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading; @@ -49,6 +50,17 @@ namespace Umbraco.Core internal readonly TypeFinder2 TypeFinder = new TypeFinder2(); private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private readonly HashSet _types = new HashSet(); + private IEnumerable _assemblies; + + /// + /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set + /// this will search all assemblies known to have plugins and exclude ones known to not have them. + /// + internal IEnumerable AssembliesToScan + { + get { return _assemblies ?? (_assemblies = TypeFinder2.GetAssembliesWithKnownExclusions()); } + set { _assemblies = value; } + } /// /// Used to create instances of the specified type based on the resolved/cached plugin types @@ -107,7 +119,7 @@ namespace Umbraco.Core /// internal IEnumerable ResolveTypes() { - return ResolveTypes(() => TypeFinder.FindClassesOfType()); + return ResolveTypes(() => TypeFinder.FindClassesOfType(AssembliesToScan)); } /// @@ -119,7 +131,7 @@ namespace Umbraco.Core internal IEnumerable ResolveTypesWithAttribute() where TAttribute : Attribute { - return ResolveTypes(() => TypeFinder.FindClassesOfTypeWithAttribute()); + return ResolveTypes(() => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan)); } /// diff --git a/src/Umbraco.Tests/PluginTypeResolverTests.cs b/src/Umbraco.Tests/PluginTypeResolverTests.cs index b9538191b7..b5428e42e7 100644 --- a/src/Umbraco.Tests/PluginTypeResolverTests.cs +++ b/src/Umbraco.Tests/PluginTypeResolverTests.cs @@ -1,12 +1,47 @@ using System.Linq; using NUnit.Framework; +using SqlCE4Umbraco; using Umbraco.Core; +using Umbraco.Web; +using umbraco.DataLayer; +using umbraco.MacroEngines; +using umbraco.MacroEngines.Iron; +using umbraco.businesslogic; +using umbraco.cms.businesslogic; +using umbraco.uicontrols; namespace Umbraco.Tests { public class PluginTypeResolverTests { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly, + typeof(ApplicationStartupHandler).Assembly, + typeof(SqlCEHelper).Assembly, + typeof(CMSNode).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(TypeFinder2).Assembly, + typeof(ISqlHelper).Assembly, + typeof(DLRScriptingEngine).Assembly, + typeof(ICultureDictionary).Assembly, + typeof(UmbracoContext).Assembly + + }; + } + [Test] public void Ensure_Only_One_Type_List_Created() { @@ -22,6 +57,20 @@ namespace Umbraco.Tests Assert.AreEqual(2, foundTypes1.Count()); } + [Test] + public void Resolves_Trees() + { + var trees = PluginTypeResolver.Current.ResolveTrees(); + Assert.AreEqual(26, trees.Count()); + } + + [Test] + public void Resolves_Applications() + { + var apps = PluginTypeResolver.Current.ResolveApplications(); + Assert.AreEqual(7, apps.Count()); + } + public interface IFindMe { diff --git a/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs b/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs index 97954bcb85..2c61502a78 100644 --- a/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs +++ b/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs @@ -36,9 +36,7 @@ namespace Umbraco.Tests { _assemblies = new[] { - //both contain the type this.GetType().Assembly, - //these dont contain the type typeof(ApplicationStartupHandler).Assembly, typeof(SqlCEHelper).Assembly, typeof(CMSNode).Assembly, diff --git a/src/umbraco.businesslogic/ApplicationRegistrar.cs b/src/umbraco.businesslogic/ApplicationRegistrar.cs index 47139638f3..29cd59ba37 100644 --- a/src/umbraco.businesslogic/ApplicationRegistrar.cs +++ b/src/umbraco.businesslogic/ApplicationRegistrar.cs @@ -32,8 +32,10 @@ namespace umbraco.BusinessLogic // Load all Applications by attribute and add them to the XML config var types = PluginTypeResolver.Current.ResolveApplications(); - //we can have multiple attributes so we'll query for them - var attrs = types.Select(x => (ApplicationAttribute)x.GetCustomAttributes(typeof(ApplicationAttribute), false).Single()) + //since applications don't populate their metadata from the attribute and because it is an interface, + //we need to interrogate the attributes for the data. Would be better to have a base class that contains + //metadata populated by the attribute. Oh well i guess. + var attrs = types.Select(x => x.GetCustomAttributes(false).Single()) .Where(x => Application.getByAlias(x.Alias) == null); var allAliases = Application.getAll().Select(x => x.alias).Concat(attrs.Select(x => x.Alias)); diff --git a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs index 2a9cd706d0..5ba139a3ff 100644 --- a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs +++ b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Xml.Linq; +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.DataLayer; using umbraco.businesslogic; @@ -29,14 +30,13 @@ namespace umbraco.BusinessLogic public ApplicationTreeRegistrar() { - // Load all Applications by attribute and add them to the XML config - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType() - .Where(x => x.GetCustomAttributes(typeof(TreeAttribute), false).Any()); + // Load all Trees by attribute and add them to the XML config + var types = PluginTypeResolver.Current.ResolveTrees(); - var items = types.Select(x => new Tuple(x, - (TreeAttribute)x.GetCustomAttributes(typeof(TreeAttribute), false).Single())) - .Where(x => ApplicationTree.getByAlias(x.Item2.Alias) == null); + var items = types + .Select(x => + new Tuple(x, x.GetCustomAttributes(false).Single())) + .Where(x => ApplicationTree.getByAlias(x.Item2.Alias) == null); var allAliases = ApplicationTree.getAll().Select(x => x.Alias).Concat(items.Select(x => x.Item2.Alias)); var inString = "'" + string.Join("','", allAliases) + "'"; diff --git a/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs b/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs index f3acbfd55e..724ad770ba 100644 --- a/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs +++ b/src/umbraco.businesslogic/PluginTypeResolverExtensions.cs @@ -21,7 +21,7 @@ namespace umbraco.businesslogic } /// - /// Returns all available IApplication object + /// Returns all available IApplication in application /// /// /// @@ -30,5 +30,15 @@ namespace umbraco.businesslogic return resolver.ResolveTypesWithAttribute(); } + /// + /// Returns all available ITrees in application + /// + /// + /// + internal static IEnumerable ResolveTrees(this PluginTypeResolver resolver) + { + return resolver.ResolveTypesWithAttribute(); + } + } } \ No newline at end of file From 1823cce4f69a774d112ac0c9ab17c2207605da0d Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 10:53:40 +0600 Subject: [PATCH 3/8] Changed over Action to use PluginTypeResolver to find and instantiate IActionHandlers and included a unit test. --- src/Umbraco.Core/PluginTypeResolver.cs | 27 ++++++++++++++++--- src/Umbraco.Core/Properties/AssemblyInfo.cs | 1 + src/Umbraco.Tests/PluginTypeResolverTests.cs | 10 +++++++ .../Routing/DocumentLookupsResolver.cs | 2 +- src/umbraco.cms/Actions/Action.cs | 16 +++-------- .../PluginTypeResolverExtensions.cs | 27 +++++++++++++++++++ src/umbraco.cms/Properties/AssemblyInfo.cs | 3 ++- src/umbraco.cms/umbraco.cms.csproj | 1 + 8 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 src/umbraco.cms/PluginTypeResolverExtensions.cs diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index be16fc6ff3..50df96c5c2 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core { /// - /// Used to resolve all plugin types + /// Used to resolve all plugin types and cache them /// /// /// @@ -62,16 +62,29 @@ namespace Umbraco.Core set { _assemblies = value; } } + /// + /// Used to resolve and create instances of the specified type based on the resolved/cached plugin types + /// + /// + /// set to true if an exception is to be thrown if there is an error during instantiation + /// + internal IEnumerable FindAndCreateInstances(bool throwException = false) + { + var types = ResolveTypes(); + return CreateInstances(types, throwException); + } + /// /// Used to create instances of the specified type based on the resolved/cached plugin types /// /// + /// + /// set to true if an exception is to be thrown if there is an error during instantiation /// - internal IEnumerable CreateInstances() + internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false) { - var types = ResolveTypes(); var instances = new List(); - foreach(var t in types) + foreach (var t in types) { try { @@ -82,6 +95,10 @@ namespace Umbraco.Core { //TODO: Need to fix logging so this doesn't bork if no SQL connection //Log.Add(LogTypes.Error, -1, "Error loading ILookup: " + ex.ToString()); + if (throwException) + { + throw ex; + } } } return instances; @@ -143,6 +160,8 @@ namespace Umbraco.Core return _types; } + + #region Private classes internal abstract class TypeList { diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 49c9f88d05..222f4e76b8 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -21,4 +21,5 @@ using System.Runtime.InteropServices; [assembly: InternalsVisibleTo("umbraco")] [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("businesslogic")] +[assembly: InternalsVisibleTo("cms")] diff --git a/src/Umbraco.Tests/PluginTypeResolverTests.cs b/src/Umbraco.Tests/PluginTypeResolverTests.cs index b5428e42e7..96e0ede87a 100644 --- a/src/Umbraco.Tests/PluginTypeResolverTests.cs +++ b/src/Umbraco.Tests/PluginTypeResolverTests.cs @@ -9,9 +9,12 @@ using umbraco.MacroEngines.Iron; using umbraco.businesslogic; using umbraco.cms.businesslogic; using umbraco.uicontrols; +using umbraco.cms; namespace Umbraco.Tests { + + [TestFixture] public class PluginTypeResolverTests { @@ -71,6 +74,13 @@ namespace Umbraco.Tests Assert.AreEqual(7, apps.Count()); } + [Test] + public void Resolves_Actions() + { + var types = PluginTypeResolver.Current.ResolveActions(); + Assert.AreEqual(1, types.Count()); + } + public interface IFindMe { diff --git a/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs b/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs index 0e6cae28d2..642c79f9fc 100644 --- a/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs +++ b/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Routing internal DocumentLookupsResolver(IEnumerable resolvers, IRequestDocumentLastChanceResolver lastChanceResolver) { //TODO: I've changed this to resolve types but the intances are not created yet! - // I've created a method on the PluginTypeResolver to create types: PluginTypesResolver.Current.CreateInstances() + // I've created a method on the PluginTypeResolver to create types: PluginTypesResolver.Current.FindAndCreateInstances() _resolverTypes.AddRange(resolvers); diff --git a/src/umbraco.cms/Actions/Action.cs b/src/umbraco.cms/Actions/Action.cs index b7cae428d8..0622709b3b 100644 --- a/src/umbraco.cms/Actions/Action.cs +++ b/src/umbraco.cms/Actions/Action.cs @@ -6,6 +6,7 @@ using System.Reflection; using Umbraco.Core; using umbraco.BasePages; using umbraco.BusinessLogic.Utils; +using umbraco.cms; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.workflow; using umbraco.interfaces; @@ -35,9 +36,6 @@ namespace umbraco.BusinessLogic.Actions private static readonly Dictionary ActionJs = new Dictionary(); private static readonly object Lock = new object(); - private static readonly object LockerReRegister = new object(); - - private static readonly Umbraco.Core.TypeFinder2 TypeFinder = new TypeFinder2(); static Action() { @@ -64,17 +62,11 @@ namespace umbraco.BusinessLogic.Actions /// private static void RegisterIActionHandlers() { - if (ActionHandlers.Count == 0) { - - var foundIActionHandlers = TypeFinder.FindClassesOfType(); - foreach (var type in foundIActionHandlers) - { - var typeInstance = Activator.CreateInstance(type) as IActionHandler; - if (typeInstance != null) - ActionHandlers.Add(typeInstance); - } + ActionHandlers.AddRange( + PluginTypeResolver.Current.CreateInstances( + PluginTypeResolver.Current.ResolveActions())); } } diff --git a/src/umbraco.cms/PluginTypeResolverExtensions.cs b/src/umbraco.cms/PluginTypeResolverExtensions.cs new file mode 100644 index 0000000000..1ccda0c54d --- /dev/null +++ b/src/umbraco.cms/PluginTypeResolverExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using umbraco.BusinessLogic.Actions; +using umbraco.businesslogic; +using umbraco.interfaces; + +namespace umbraco.cms +{ + /// + /// Extension methods for the PluginTypeResolver + /// + public static class PluginTypeResolverExtensions + { + + /// + /// Returns all available IActionHandler in application + /// + /// + /// + internal static IEnumerable ResolveActions(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + + } +} \ No newline at end of file diff --git a/src/umbraco.cms/Properties/AssemblyInfo.cs b/src/umbraco.cms/Properties/AssemblyInfo.cs index c38a6bd99c..9cbdba2843 100644 --- a/src/umbraco.cms/Properties/AssemblyInfo.cs +++ b/src/umbraco.cms/Properties/AssemblyInfo.cs @@ -13,4 +13,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("umbraco", AllInternalsVisible=true)] -[assembly: InternalsVisibleTo("Umbraco.LegacyTests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Umbraco.LegacyTests")] +[assembly: InternalsVisibleTo("Umbraco.Tests")] \ No newline at end of file diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index 068836f738..bdd3b9639d 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -267,6 +267,7 @@ + Code From 5576f65ef043d2f0addebb071f0739a033378afa Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 11:54:29 +0600 Subject: [PATCH 4/8] Updated DataType factory to use PluginTypeResolver and made the dictionary thread safe. Updated MacroEngineFactory to use PluginTypeResolver and made it thread safe. Have also updated it to only create one instance of each IMacroEngine per application which should increase performance a teeny bit, plus that's how it should be. --- src/Umbraco.Core/PluginTypeResolver.cs | 13 +++ .../Resolving/MultipleResolverBase.cs | 3 + src/Umbraco.Tests/DataTypeFactoryTests.cs | 63 +++++++++++++ .../EnumerableExtensionsTests.cs | 1 - src/Umbraco.Tests/PluginTypeResolverTests.cs | 12 ++- src/Umbraco.Tests/Umbraco.Tests.csproj | 5 + .../PluginTypeResolverExtensions.cs | 21 +++++ .../businesslogic/datatype/factory.cs | 56 ++++------- .../businesslogic/macro/MacroEngineFactory.cs | 92 +++++++------------ 9 files changed, 169 insertions(+), 97 deletions(-) create mode 100644 src/Umbraco.Tests/DataTypeFactoryTests.cs diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index 50df96c5c2..8440aadb02 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -104,6 +104,19 @@ namespace Umbraco.Core return instances; } + /// + /// Used to create an instance of the specified type based on the resolved/cached plugin types + /// + /// + /// + /// + /// + internal T CreateInstance(Type type, bool throwException = false) + { + var instances = CreateInstances(new[] {type}, throwException); + return instances.FirstOrDefault(); + } + private IEnumerable ResolveTypes(Func> finder) { using (var readLock = new UpgradeableReadLock(_lock)) diff --git a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs b/src/Umbraco.Core/Resolving/MultipleResolverBase.cs index 8fba742f58..85104696f0 100644 --- a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs +++ b/src/Umbraco.Core/Resolving/MultipleResolverBase.cs @@ -3,6 +3,9 @@ using System.Threading; namespace Umbraco.Core.Resolving { + //NOTE: This class should also support creating instances of the object and managing their lifespan, + // for example, some resolvers would want to return new object each time, per request or per application lifetime. + /// /// A Resolver to return and set a Multiply registered object. /// diff --git a/src/Umbraco.Tests/DataTypeFactoryTests.cs b/src/Umbraco.Tests/DataTypeFactoryTests.cs new file mode 100644 index 0000000000..46f0cedd7a --- /dev/null +++ b/src/Umbraco.Tests/DataTypeFactoryTests.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using SqlCE4Umbraco; +using Umbraco.Core; +using Umbraco.Web; +using umbraco.DataLayer; +using umbraco.MacroEngines; +using umbraco.MacroEngines.Iron; +using umbraco.businesslogic; +using umbraco.cms.businesslogic; +using umbraco.editorControls; +using umbraco.uicontrols; +using System.Linq; + +namespace Umbraco.Tests +{ + [TestFixture] + public class DataTypeFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly, + typeof(ApplicationStartupHandler).Assembly, + typeof(SqlCEHelper).Assembly, + typeof(CMSNode).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(TypeFinder2).Assembly, + typeof(ISqlHelper).Assembly, + typeof(DLRScriptingEngine).Assembly, + typeof(ICultureDictionary).Assembly, + typeof(UmbracoContext).Assembly, + typeof(BaseDataType).Assembly, + + }; + } + + [Test] + public void Find_All_DataTypes() + { + umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); + Assert.AreEqual(33, umbraco.cms.businesslogic.datatype.controls.Factory._controls.Count); + } + + [Test] + public void Get_All_Instances() + { + umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); + var factory = new umbraco.cms.businesslogic.datatype.controls.Factory(); + Assert.AreEqual(33, factory.GetAll().Count()); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/EnumerableExtensionsTests.cs b/src/Umbraco.Tests/EnumerableExtensionsTests.cs index 9bacfeb39a..c13cee8ea9 100644 --- a/src/Umbraco.Tests/EnumerableExtensionsTests.cs +++ b/src/Umbraco.Tests/EnumerableExtensionsTests.cs @@ -8,7 +8,6 @@ using Umbraco.Core.Resolving; namespace Umbraco.Tests { - [TestFixture] public class EnumerableExtensionsTests { diff --git a/src/Umbraco.Tests/PluginTypeResolverTests.cs b/src/Umbraco.Tests/PluginTypeResolverTests.cs index 96e0ede87a..058c71cb31 100644 --- a/src/Umbraco.Tests/PluginTypeResolverTests.cs +++ b/src/Umbraco.Tests/PluginTypeResolverTests.cs @@ -8,6 +8,7 @@ using umbraco.MacroEngines; using umbraco.MacroEngines.Iron; using umbraco.businesslogic; using umbraco.cms.businesslogic; +using umbraco.editorControls; using umbraco.uicontrols; using umbraco.cms; @@ -40,8 +41,8 @@ namespace Umbraco.Tests typeof(ISqlHelper).Assembly, typeof(DLRScriptingEngine).Assembly, typeof(ICultureDictionary).Assembly, - typeof(UmbracoContext).Assembly - + typeof(UmbracoContext).Assembly, + typeof(BaseDataType).Assembly }; } @@ -81,6 +82,13 @@ namespace Umbraco.Tests Assert.AreEqual(1, types.Count()); } + [Test] + public void Resolves_DataTypes() + { + var types = PluginTypeResolver.Current.ResolveDataTypes(); + Assert.AreEqual(33, types.Count()); + } + public interface IFindMe { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ae089775fd..d00037beb8 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -49,6 +49,7 @@ + @@ -92,6 +93,10 @@ {C7CB79F0-1C97-4B33-BFA7-00731B579AE2} umbraco.datalayer + + {255F5DF1-4E43-4758-AC05-7A0B68EB021B} + umbraco.editorControls + {511F6D8D-7717-440A-9A57-A507E9A8B27F} umbraco.interfaces diff --git a/src/umbraco.cms/PluginTypeResolverExtensions.cs b/src/umbraco.cms/PluginTypeResolverExtensions.cs index 1ccda0c54d..6b5c56f1f5 100644 --- a/src/umbraco.cms/PluginTypeResolverExtensions.cs +++ b/src/umbraco.cms/PluginTypeResolverExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Umbraco.Core; using umbraco.BusinessLogic.Actions; using umbraco.businesslogic; +using umbraco.cms.businesslogic.macro; using umbraco.interfaces; namespace umbraco.cms @@ -23,5 +24,25 @@ namespace umbraco.cms return resolver.ResolveTypes(); } + /// + /// Returns all available IDataType in application + /// + /// + /// + internal static IEnumerable ResolveDataTypes(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + + /// + /// Returns all available IDataType in application + /// + /// + /// + internal static IEnumerable ResolveMacroEngines(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/datatype/factory.cs b/src/umbraco.cms/businesslogic/datatype/factory.cs index 617c6bf093..240742cddd 100644 --- a/src/umbraco.cms/businesslogic/datatype/factory.cs +++ b/src/umbraco.cms/businesslogic/datatype/factory.cs @@ -1,7 +1,9 @@ using System; using System.Collections; +using System.Collections.Concurrent; +using System.Linq; using System.Web; - +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.interfaces; using System.Collections.Generic; @@ -17,7 +19,7 @@ namespace umbraco.cms.businesslogic.datatype.controls { #region Declarations - private static readonly Dictionary _controls = new Dictionary(); + internal static readonly ConcurrentDictionary _controls = new ConcurrentDictionary(); #endregion @@ -53,7 +55,7 @@ namespace umbraco.cms.businesslogic.datatype.controls } if (_controls.ContainsKey(DataEditorId)) { - IDataType newObject = Activator.CreateInstance(_controls[DataEditorId]) as IDataType; + var newObject = PluginTypeResolver.Current.CreateInstance(_controls[DataEditorId]); return newObject; } else @@ -68,10 +70,10 @@ namespace umbraco.cms.businesslogic.datatype.controls /// A list of IDataType's public IDataType[] GetAll() { - IDataType[] retVal = new IDataType[_controls.Count]; - int c = 0; + var retVal = new IDataType[_controls.Count]; + var c = 0; - foreach (Guid id in _controls.Keys) + foreach (var id in _controls.Keys) { retVal[c] = GetNewObject(id); c++; @@ -80,39 +82,19 @@ namespace umbraco.cms.businesslogic.datatype.controls return retVal; } - private static void Initialize() + internal static void Initialize() { // Get all datatypes from interface - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - GetDataTypes(types); - } - - private static void GetDataTypes(IEnumerable types) - { - foreach (var t in types) - { - IDataType typeInstance = null; - try - { - if (t.IsVisible) - { - typeInstance = Activator.CreateInstance(t) as IDataType; - } - } - catch { } - if (typeInstance != null) - { - try - { - _controls.Add(typeInstance.Id, t); - } - catch (Exception ee) - { - BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, -1, "Can't import datatype '" + t.FullName + "': " + ee.ToString()); - } - } - } + var types = PluginTypeResolver.Current.ResolveDataTypes().ToArray(); + foreach (var t in types) + { + var instance = PluginTypeResolver.Current.CreateInstance(t); + if (instance != null) + { + _controls.TryAdd(instance.Id, t); + } + } } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs b/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs index 506c1a2d51..fe5406c76c 100644 --- a/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs +++ b/src/umbraco.cms/businesslogic/macro/MacroEngineFactory.cs @@ -1,59 +1,50 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.interfaces; namespace umbraco.cms.businesslogic.macro { + + //TODO: This class needs to be changed to use the new MultipleResolverBase + public class MacroEngineFactory { - private static readonly Dictionary m_engines = new Dictionary(); - private static readonly List m_allEngines = new List(); - private static object locker = new object(); + private static readonly List AllEngines = new List(); + private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + private static volatile bool _isInitialized = false; public MacroEngineFactory() { - Initialize(); + EnsureInitialize(); } + internal static void EnsureInitialize() + { + if (_isInitialized) + return; + + using (new WriteLock(Lock)) + { + AllEngines.Clear(); + + AllEngines.AddRange( + PluginTypeResolver.Current.CreateInstances( + PluginTypeResolver.Current.ResolveMacroEngines())); + + _isInitialized = true; + } + } + + [Obsolete("Use EnsureInitialize method instead")] protected static void Initialize() { - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - GetEngines(types); - } - - private static void GetEngines(IEnumerable types) - { - foreach (Type t in types) - { - IMacroEngine typeInstance = null; - try - { - if (t.IsVisible) - { - typeInstance = Activator.CreateInstance(t) as IMacroEngine; - } - } - catch { } - if (typeInstance != null) - { - try - { - lock (locker) - { - if (!m_engines.ContainsKey(typeInstance.Name)) - m_engines.Add(typeInstance.Name, t); - } - } - catch (Exception ee) - { - BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, -1, "Can't import MacroEngine '" + t.FullName + "': " + ee); - } - } - } + EnsureInitialize(); } public static IEnumerable GetSupportedLanguages() { @@ -78,27 +69,15 @@ namespace umbraco.cms.businesslogic.macro public static List GetAll() { - - if (m_allEngines.Count == 0) - { - Initialize(); - foreach (string name in m_engines.Keys) { - m_allEngines.Add(GetEngine(name)); - } - } - - return m_allEngines; + EnsureInitialize(); + return AllEngines; } public static IMacroEngine GetEngine(string name) { - if (m_engines.ContainsKey(name)) - { - var newObject = Activator.CreateInstance(m_engines[name]) as IMacroEngine; - return newObject; - } - - return null; + EnsureInitialize(); + var engine = AllEngines.FirstOrDefault(x => x.Name == name); + return engine; } public static IMacroEngine GetByFilename(string filename) @@ -114,8 +93,7 @@ namespace umbraco.cms.businesslogic.macro public static IMacroEngine GetByExtension(string extension) { - IMacroEngine engine = - GetAll().Find(t => t.SupportedExtensions.Contains(extension)); + var engine = GetAll().Find(t => t.SupportedExtensions.Contains(extension)); if (engine != null) { return engine; From 960dd52750792dd18f3c37176e64c8bece175504 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 12:14:27 +0600 Subject: [PATCH 5/8] Created MacroEngineFactory tests, updated DataTypeFactory tests. --- src/Umbraco.Tests/DataTypeFactoryTests.cs | 87 +++++++++--- src/Umbraco.Tests/MacroEngineFactoryTests.cs | 127 ++++++++++++++++++ ...erBenchmarkTests.cs => TypeFinderTests.cs} | 0 src/Umbraco.Tests/Umbraco.Tests.csproj | 3 +- 4 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Tests/MacroEngineFactoryTests.cs rename src/Umbraco.Tests/{TypeFinderBenchmarkTests.cs => TypeFinderTests.cs} (100%) diff --git a/src/Umbraco.Tests/DataTypeFactoryTests.cs b/src/Umbraco.Tests/DataTypeFactoryTests.cs index 46f0cedd7a..d8f4a22546 100644 --- a/src/Umbraco.Tests/DataTypeFactoryTests.cs +++ b/src/Umbraco.Tests/DataTypeFactoryTests.cs @@ -1,3 +1,4 @@ +using System; using NUnit.Framework; using SqlCE4Umbraco; using Umbraco.Core; @@ -7,9 +8,11 @@ using umbraco.MacroEngines; using umbraco.MacroEngines.Iron; using umbraco.businesslogic; using umbraco.cms.businesslogic; -using umbraco.editorControls; +using umbraco.cms.businesslogic.datatype; +using umbraco.interfaces; using umbraco.uicontrols; using System.Linq; +using BaseDataType = umbraco.editorControls.BaseDataType; namespace Umbraco.Tests { @@ -22,25 +25,7 @@ namespace Umbraco.Tests //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver PluginTypeResolver.Current.AssembliesToScan = new[] { - this.GetType().Assembly, - typeof(ApplicationStartupHandler).Assembly, - typeof(SqlCEHelper).Assembly, - typeof(CMSNode).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(TypeFinder2).Assembly, - typeof(ISqlHelper).Assembly, - typeof(DLRScriptingEngine).Assembly, - typeof(ICultureDictionary).Assembly, - typeof(UmbracoContext).Assembly, - typeof(BaseDataType).Assembly, - + this.GetType().Assembly }; } @@ -48,7 +33,7 @@ namespace Umbraco.Tests public void Find_All_DataTypes() { umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); - Assert.AreEqual(33, umbraco.cms.businesslogic.datatype.controls.Factory._controls.Count); + Assert.AreEqual(2, umbraco.cms.businesslogic.datatype.controls.Factory._controls.Count); } [Test] @@ -56,8 +41,66 @@ namespace Umbraco.Tests { umbraco.cms.businesslogic.datatype.controls.Factory.Initialize(); var factory = new umbraco.cms.businesslogic.datatype.controls.Factory(); - Assert.AreEqual(33, factory.GetAll().Count()); + Assert.AreEqual(2, factory.GetAll().Count()); } + #region Classes for tests + public class DataType1 : AbstractDataEditor + { + public override Guid Id + { + get { return new Guid("FBAEA49B-F704-44FE-B725-6C8FE0767CF2"); } + } + + public override string DataTypeName + { + get { return "DataType1"; } + } + + public override IDataEditor DataEditor + { + get { throw new NotImplementedException(); } + } + + public override IDataPrevalue PrevalueEditor + { + get { throw new NotImplementedException(); } + } + + public override IData Data + { + get { throw new NotImplementedException(); } + } + } + + public class DataType2 : AbstractDataEditor + { + public override Guid Id + { + get { return new Guid("3F58099B-96AC-415E-B3F9-BA273F51E681"); } + } + + public override string DataTypeName + { + get { return "DataType2"; } + } + + public override IDataEditor DataEditor + { + get { throw new NotImplementedException(); } + } + + public override IDataPrevalue PrevalueEditor + { + get { throw new NotImplementedException(); } + } + + public override IData Data + { + get { throw new NotImplementedException(); } + } + } + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/MacroEngineFactoryTests.cs b/src/Umbraco.Tests/MacroEngineFactoryTests.cs new file mode 100644 index 0000000000..5c761d5291 --- /dev/null +++ b/src/Umbraco.Tests/MacroEngineFactoryTests.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using umbraco.cms.businesslogic.macro; +using umbraco.interfaces; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MacroEngineFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly + }; + } + + [Test] + public void Get_All() + { + var engines = MacroEngineFactory.GetAll(); + Assert.AreEqual(2, engines.Count()); + } + + [Test] + public void Get_Engine() + { + var engine1 = MacroEngineFactory.GetEngine("MacroEngine1"); + Assert.IsNotNull(engine1); + } + + [Test] + public void Get_By_Filename() + { + var engine1 = MacroEngineFactory.GetByFilename("test.me1"); + var engine2 = MacroEngineFactory.GetByFilename("test.me2"); + Assert.IsNotNull(engine1); + Assert.IsNotNull(engine2); + Assert.Throws(() => MacroEngineFactory.GetByFilename("test.blah")); + + } + + [Test] + public void Get_By_Extension() + { + var engine1 = MacroEngineFactory.GetByExtension("me1"); + var engine2 = MacroEngineFactory.GetByExtension("me2"); + Assert.IsNotNull(engine1); + Assert.IsNotNull(engine2); + Assert.Throws(() => MacroEngineFactory.GetByExtension("blah")); + } + + #region Classes for tests + public class MacroEngine1 : IMacroEngine + { + public string Name + { + get { return "MacroEngine1"; } + } + + public IEnumerable SupportedExtensions + { + get { return new[] {"me1"}; } + } + + public IEnumerable SupportedUIExtensions + { + get { throw new NotImplementedException(); } + } + + public Dictionary SupportedProperties + { + get { throw new NotImplementedException(); } + } + + public bool Validate(string code, string tempFileName, INode currentPage, out string errorMessage) + { + throw new NotImplementedException(); + } + + public string Execute(MacroModel macro, INode currentPage) + { + throw new NotImplementedException(); + } + } + + public class MacroEngine2 : IMacroEngine + { + public string Name + { + get { return "MacroEngine2"; } + } + + public IEnumerable SupportedExtensions + { + get { return new[] { "me2" }; } + } + + public IEnumerable SupportedUIExtensions + { + get { throw new NotImplementedException(); } + } + + public Dictionary SupportedProperties + { + get { throw new NotImplementedException(); } + } + + public bool Validate(string code, string tempFileName, INode currentPage, out string errorMessage) + { + throw new NotImplementedException(); + } + + public string Execute(MacroModel macro, INode currentPage) + { + throw new NotImplementedException(); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TypeFinderBenchmarkTests.cs b/src/Umbraco.Tests/TypeFinderTests.cs similarity index 100% rename from src/Umbraco.Tests/TypeFinderBenchmarkTests.cs rename to src/Umbraco.Tests/TypeFinderTests.cs diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d00037beb8..71c2fd36b4 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -50,6 +50,7 @@ + @@ -63,7 +64,7 @@ - + From 507b6ab773bbc109c84e1c170210577204a722d9 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 12:23:22 +0600 Subject: [PATCH 6/8] Updated MediaFactory to use PluginTypeResolver and added a unit test to ensure they are being found. --- src/Umbraco.Tests/MediaFactoryTests.cs | 92 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../PluginTypeResolverExtensions.cs | 11 +++ .../businesslogic/media/MediaFactory.cs | 40 ++------ 4 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 src/Umbraco.Tests/MediaFactoryTests.cs diff --git a/src/Umbraco.Tests/MediaFactoryTests.cs b/src/Umbraco.Tests/MediaFactoryTests.cs new file mode 100644 index 0000000000..b989ac8a28 --- /dev/null +++ b/src/Umbraco.Tests/MediaFactoryTests.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic.media; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MediaFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly + }; + } + + /// + /// Ensures that the media factory finds the correct number of IMediaFactory + /// + [Test] + public void Find_Media_Factories() + { + var factories = MediaFactory.Factories; + Assert.AreEqual(2, factories.Count()); + } + + #region Classes for tests + public class MediaFactory1 : IMediaFactory + { + public List Extensions + { + get { throw new NotImplementedException(); } + } + + public int Priority + { + get { return 1; } + } + + public bool CanHandleMedia(int parentNodeId, PostedMediaFile postedFile, User user) + { + throw new NotImplementedException(); + } + + public Media HandleMedia(int parentNodeId, PostedMediaFile postedFile, User user) + { + throw new NotImplementedException(); + } + + public Media HandleMedia(int parentNodeId, PostedMediaFile postedFile, User user, bool replaceExisting) + { + throw new NotImplementedException(); + } + } + + public class MediaFactory2 : IMediaFactory + { + public List Extensions + { + get { throw new NotImplementedException(); } + } + + public int Priority + { + get { return 2; } + } + + public bool CanHandleMedia(int parentNodeId, PostedMediaFile postedFile, User user) + { + throw new NotImplementedException(); + } + + public Media HandleMedia(int parentNodeId, PostedMediaFile postedFile, User user) + { + throw new NotImplementedException(); + } + + public Media HandleMedia(int parentNodeId, PostedMediaFile postedFile, User user, bool replaceExisting) + { + throw new NotImplementedException(); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 71c2fd36b4..3aba5e8def 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/src/umbraco.cms/PluginTypeResolverExtensions.cs b/src/umbraco.cms/PluginTypeResolverExtensions.cs index 6b5c56f1f5..e98fac8d35 100644 --- a/src/umbraco.cms/PluginTypeResolverExtensions.cs +++ b/src/umbraco.cms/PluginTypeResolverExtensions.cs @@ -4,6 +4,7 @@ using Umbraco.Core; using umbraco.BusinessLogic.Actions; using umbraco.businesslogic; using umbraco.cms.businesslogic.macro; +using umbraco.cms.businesslogic.media; using umbraco.interfaces; namespace umbraco.cms @@ -44,5 +45,15 @@ namespace umbraco.cms return resolver.ResolveTypes(); } + /// + /// Returns all available IMediaFactory in application + /// + /// + /// + internal static IEnumerable ResolveMediaFactories(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/media/MediaFactory.cs b/src/umbraco.cms/businesslogic/media/MediaFactory.cs index f11bf97d0b..da9154549d 100644 --- a/src/umbraco.cms/businesslogic/media/MediaFactory.cs +++ b/src/umbraco.cms/businesslogic/media/MediaFactory.cs @@ -3,14 +3,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using Umbraco.Core; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Utils; namespace umbraco.cms.businesslogic.media { + //TODO: This class needs to inherit from the new MultipleResolverBase class + public class MediaFactory { - private static readonly List Factories = new List(); + internal static readonly List Factories = new List(); static MediaFactory() { @@ -19,36 +22,11 @@ namespace umbraco.cms.businesslogic.media private static void Initialize() { - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - - foreach (var t in types) - { - IMediaFactory typeInstance = null; - - try - { - if (t.IsVisible) - { - typeInstance = Activator.CreateInstance(t) as IMediaFactory; - } - } - catch { } - - if (typeInstance != null) - { - try - { - Factories.Add(typeInstance); - } - catch (Exception ee) - { - Log.Add(LogTypes.Error, -1, "Can't import MediaFactory '" + t.FullName + "': " + ee); - } - } - } - - Factories.Sort((f1, f2) => f1.Priority.CompareTo(f2.Priority)); + Factories.AddRange( + PluginTypeResolver.Current.CreateInstances( + PluginTypeResolver.Current.ResolveMediaFactories())); + + Factories.Sort((f1, f2) => f1.Priority.CompareTo(f2.Priority)); } public static IMediaFactory GetMediaFactory(int parentId, PostedMediaFile postedFile, User user) From da0217d3304aad83da18891261a12fe4dd5aa77b Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 12:29:41 +0600 Subject: [PATCH 7/8] Updated PackageAction to use PluginTypeResolver and added unit test to ensure they are found. --- .../PackageActionFactoryTests.cs | 82 ++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../PluginTypeResolverExtensions.cs | 10 ++ .../PackageInstance/PackageActions.cs | 141 +++++++++--------- 4 files changed, 166 insertions(+), 68 deletions(-) create mode 100644 src/Umbraco.Tests/PackageActionFactoryTests.cs diff --git a/src/Umbraco.Tests/PackageActionFactoryTests.cs b/src/Umbraco.Tests/PackageActionFactoryTests.cs new file mode 100644 index 0000000000..a05e9764bb --- /dev/null +++ b/src/Umbraco.Tests/PackageActionFactoryTests.cs @@ -0,0 +1,82 @@ +using System; +using System.Linq; +using System.Xml; +using NUnit.Framework; +using Umbraco.Core; +using umbraco.cms.businesslogic.packager; +using umbraco.interfaces; + +namespace Umbraco.Tests +{ + [TestFixture] + public class PackageActionFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly + }; + } + + /// + /// Ensures all IPackageActions are found + /// + [Test] + public void Find_Package_Actions() + { + var actions = PackageAction.PackageActions; + Assert.AreEqual(2, actions.Count()); + } + + #region Classes for tests + public class PackageAction1 : IPackageAction + { + public bool Execute(string packageName, XmlNode xmlData) + { + throw new NotImplementedException(); + } + + public string Alias() + { + return "pa1"; + } + + public bool Undo(string packageName, XmlNode xmlData) + { + throw new NotImplementedException(); + } + + public XmlNode SampleXml() + { + throw new NotImplementedException(); + } + } + + public class PackageAction2 : IPackageAction + { + public bool Execute(string packageName, XmlNode xmlData) + { + throw new NotImplementedException(); + } + + public string Alias() + { + return "pa2"; + } + + public bool Undo(string packageName, XmlNode xmlData) + { + throw new NotImplementedException(); + } + + public XmlNode SampleXml() + { + throw new NotImplementedException(); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3aba5e8def..89195b7179 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -52,6 +52,7 @@ + diff --git a/src/umbraco.cms/PluginTypeResolverExtensions.cs b/src/umbraco.cms/PluginTypeResolverExtensions.cs index e98fac8d35..3588a855dc 100644 --- a/src/umbraco.cms/PluginTypeResolverExtensions.cs +++ b/src/umbraco.cms/PluginTypeResolverExtensions.cs @@ -55,5 +55,15 @@ namespace umbraco.cms return resolver.ResolveTypes(); } + /// + /// Returns all available IPackageAction in application + /// + /// + /// + internal static IEnumerable ResolvePackageActions(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/Packager/PackageInstance/PackageActions.cs b/src/umbraco.cms/businesslogic/Packager/PackageInstance/PackageActions.cs index 36f24a918e..151de71d00 100644 --- a/src/umbraco.cms/businesslogic/Packager/PackageInstance/PackageActions.cs +++ b/src/umbraco.cms/businesslogic/Packager/PackageInstance/PackageActions.cs @@ -3,7 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Web; using System.Xml; - +using Umbraco.Core; using umbraco.BasePages; using umbraco.BusinessLogic.Utils; using umbraco.cms.businesslogic.web; @@ -11,79 +11,84 @@ using umbraco.cms.businesslogic.workflow; using umbraco.interfaces; -namespace umbraco.cms.businesslogic.packager { +namespace umbraco.cms.businesslogic.packager +{ - /// - /// Package actions are executed on packge install / uninstall. - /// - public class PackageAction { - private static readonly List _packageActions = new List(); + /// + /// Package actions are executed on packge install / uninstall. + /// + public class PackageAction + { + internal static readonly List PackageActions = new List(); - /// - /// Initializes the class. - /// - static PackageAction(){ - RegisterPackageActions(); - } - - private static void RegisterPackageActions() - { - var typeFinder = new Umbraco.Core.TypeFinder2(); - var types = typeFinder.FindClassesOfType(); - foreach (var t in types) - { - var typeInstance = Activator.CreateInstance(t) as IPackageAction; - if (typeInstance != null) - { - _packageActions.Add(typeInstance); - - if (HttpContext.Current != null) - HttpContext.Current.Trace.Write("registerPackageActions", " + Adding package action '" + typeInstance.Alias()); - } - } - } + /// + /// Initializes the class. + /// + static PackageAction() + { + RegisterPackageActions(); + } - /// - /// Runs the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public static void RunPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) { + private static void RegisterPackageActions() + { + PackageActions.AddRange( + PluginTypeResolver.Current.CreateInstances( + PluginTypeResolver.Current.ResolvePackageActions())); + } - foreach (IPackageAction ipa in _packageActions) { - try { - if (ipa.Alias() == actionAlias) { + /// + /// Runs the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + public static void RunPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) + { - ipa.Execute(packageName, actionXml); - } - } catch (Exception ipaExp) { - BusinessLogic.Log.Add(BusinessLogic.LogTypes.Error, BusinessLogic.User.GetUser(0), -1, string.Format("Error loading package action '{0}' for package {1}: {2}", - ipa.Alias(), packageName, ipaExp)); - } - } - } + foreach (IPackageAction ipa in PackageActions) + { + try + { + if (ipa.Alias() == actionAlias) + { - /// - /// Undos the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public static void UndoPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) { + ipa.Execute(packageName, actionXml); + } + } + catch (Exception ipaExp) + { + BusinessLogic.Log.Add(BusinessLogic.LogTypes.Error, BusinessLogic.User.GetUser(0), -1, string.Format("Error loading package action '{0}' for package {1}: {2}", + ipa.Alias(), packageName, ipaExp)); + } + } + } - foreach (IPackageAction ipa in _packageActions) { - try { - if (ipa.Alias() == actionAlias) { + /// + /// Undos the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + public static void UndoPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) + { - ipa.Undo(packageName, actionXml); - } - } catch (Exception ipaExp) { - BusinessLogic.Log.Add(BusinessLogic.LogTypes.Error, BusinessLogic.User.GetUser(0), -1, string.Format("Error undoing package action '{0}' for package {1}: {2}", - ipa.Alias(), packageName, ipaExp)); - } - } - } - - } + foreach (IPackageAction ipa in PackageActions) + { + try + { + if (ipa.Alias() == actionAlias) + { + + ipa.Undo(packageName, actionXml); + } + } + catch (Exception ipaExp) + { + BusinessLogic.Log.Add(BusinessLogic.LogTypes.Error, BusinessLogic.User.GetUser(0), -1, string.Format("Error undoing package action '{0}' for package {1}: {2}", + ipa.Alias(), packageName, ipaExp)); + } + } + } + + } } From 573c5a3387c77ed4a503c540969fb3ee69fdb5f0 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Fri, 27 Jul 2012 12:45:56 +0600 Subject: [PATCH 8/8] Updated MacroControlFactory to use PluginTypeResolver and added unit test to ensure it finds the types. --- src/Umbraco.Core/Properties/AssemblyInfo.cs | 1 + src/Umbraco.Tests/MacroControlFactoryTests.cs | 61 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../PluginTypeResolverExtensions.cs | 29 +++++++++ .../Properties/AssemblyInfo.cs | 2 + .../macrocontainer/MacroControlFactory.cs | 60 ++++++++++-------- .../umbraco.editorControls.csproj | 1 + 7 files changed, 131 insertions(+), 24 deletions(-) create mode 100644 src/Umbraco.Tests/MacroControlFactoryTests.cs create mode 100644 src/umbraco.editorControls/PluginTypeResolverExtensions.cs diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 222f4e76b8..229c77c8d8 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -22,4 +22,5 @@ using System.Runtime.InteropServices; [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("businesslogic")] [assembly: InternalsVisibleTo("cms")] +[assembly: InternalsVisibleTo("umbraco.editorControls")] diff --git a/src/Umbraco.Tests/MacroControlFactoryTests.cs b/src/Umbraco.Tests/MacroControlFactoryTests.cs new file mode 100644 index 0000000000..c70e24a599 --- /dev/null +++ b/src/Umbraco.Tests/MacroControlFactoryTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using System.Web.UI; +using NUnit.Framework; +using Umbraco.Core; +using umbraco.editorControls.macrocontainer; +using umbraco.interfaces; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MacroControlFactoryTests + { + [SetUp] + public void Initialize() + { + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginTypeResolver.Current.AssembliesToScan = new[] + { + this.GetType().Assembly + }; + } + + [Test] + public void Find_Types() + { + var found = MacroControlFactory.MacroControlTypes; + Assert.AreEqual(2, found.Count()); + } + + #region Classes for tests + public class ControlMacroRendering : Control, IMacroGuiRendering + { + public string Value + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public bool ShowCaption + { + get { throw new NotImplementedException(); } + } + } + + public class NonControlMacroRendering : IMacroGuiRendering + { + public string Value + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public bool ShowCaption + { + get { throw new NotImplementedException(); } + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 89195b7179..6e02fd0446 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -50,6 +50,7 @@ + diff --git a/src/umbraco.editorControls/PluginTypeResolverExtensions.cs b/src/umbraco.editorControls/PluginTypeResolverExtensions.cs new file mode 100644 index 0000000000..ae557087a1 --- /dev/null +++ b/src/umbraco.editorControls/PluginTypeResolverExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using umbraco.BusinessLogic.Actions; +using umbraco.cms.businesslogic.macro; +using umbraco.cms.businesslogic.media; +using umbraco.interfaces; + +namespace umbraco.editorControls +{ + /// + /// Extension methods for the PluginTypeResolver + /// + public static class PluginTypeResolverExtensions + { + + /// + /// Returns all available IMacroGuiRendering in application + /// + /// + /// + internal static IEnumerable ResolveMacroRenderings(this PluginTypeResolver resolver) + { + return resolver.ResolveTypes(); + } + + + } +} \ No newline at end of file diff --git a/src/umbraco.editorControls/Properties/AssemblyInfo.cs b/src/umbraco.editorControls/Properties/AssemblyInfo.cs index 15de1fa37b..e84fc669e0 100644 --- a/src/umbraco.editorControls/Properties/AssemblyInfo.cs +++ b/src/umbraco.editorControls/Properties/AssemblyInfo.cs @@ -10,3 +10,5 @@ using System.Runtime.CompilerServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyProduct("")] + +[assembly: InternalsVisibleTo("Umbraco.Tests")] \ No newline at end of file diff --git a/src/umbraco.editorControls/macrocontainer/MacroControlFactory.cs b/src/umbraco.editorControls/macrocontainer/MacroControlFactory.cs index e7b7ff1f6a..da2b1d301b 100644 --- a/src/umbraco.editorControls/macrocontainer/MacroControlFactory.cs +++ b/src/umbraco.editorControls/macrocontainer/MacroControlFactory.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Web.UI; +using Umbraco.Core; using umbraco.BusinessLogic.Utils; using umbraco.interfaces; using umbraco.editorControls; @@ -12,14 +14,17 @@ namespace umbraco.editorControls.macrocontainer { internal class MacroControlFactory { - #region Private Fields + /// /// All Possible Macro types /// private static List _macroControlTypes = null; - #endregion + + private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + #region Methods + /// /// Create an instance of a Macro control and return it. /// Because the macro control uses inline client script whichs is not generated after postback @@ -27,19 +32,22 @@ namespace umbraco.editorControls.macrocontainer /// internal static Control GetMacroRenderControlByType(PersistableMacroProperty prop, string uniqueID) { - Control macroControl; - - Type m = MacroControlTypes.FindLast(delegate(Type macroGuiCcontrol) { return macroGuiCcontrol.ToString() == string.Format("{0}.{1}", prop.AssemblyName, prop.TypeName); }); - IMacroGuiRendering typeInstance; - typeInstance = Activator.CreateInstance(m) as IMacroGuiRendering; - if (!string.IsNullOrEmpty(prop.Value)) - { - ((IMacroGuiRendering)typeInstance).Value = prop.Value; - } - macroControl = (Control)typeInstance; - - macroControl.ID = uniqueID; - return macroControl; + var m = MacroControlTypes.FindLast(macroGuiCcontrol => macroGuiCcontrol.ToString() == string.Format("{0}.{1}", prop.AssemblyName, prop.TypeName)); + var instance = PluginTypeResolver.Current.CreateInstance(m); + if (instance != null) + { + if (!string.IsNullOrEmpty(prop.Value)) + { + instance.Value = prop.Value; + } + var macroControl = instance as Control; + if (macroControl != null) + { + macroControl.ID = uniqueID; + return macroControl; + } + } + return null; } /// @@ -51,25 +59,29 @@ namespace umbraco.editorControls.macrocontainer { return ((IMacroGuiRendering)macroControl).Value; } + #endregion #region Properties /// /// All Possible Macro types /// - private static List MacroControlTypes + internal static List MacroControlTypes { get { - if (_macroControlTypes == null || !_macroControlTypes.Any()) - { - //Populate the list with all the types of IMacroGuiRendering - var typeFinder = new Umbraco.Core.TypeFinder2(); - _macroControlTypes = new List(); - _macroControlTypes = typeFinder.FindClassesOfType().ToList(); - } + using (var readLock = new UpgradeableReadLock(Lock)) + { + if (_macroControlTypes == null || !_macroControlTypes.Any()) + { - return _macroControlTypes; + readLock.UpgradeToWriteLock(); + + _macroControlTypes = new List(PluginTypeResolver.Current.ResolveMacroRenderings()); + } + + return _macroControlTypes; + } } } #endregion diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj index cfdcd26b37..e3a392ee94 100644 --- a/src/umbraco.editorControls/umbraco.editorControls.csproj +++ b/src/umbraco.editorControls/umbraco.editorControls.csproj @@ -176,6 +176,7 @@ + Code