From d7f8214d24adbc9e00b1a25421f822ca2c8def3e Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 22 Aug 2012 08:33:19 +0600 Subject: [PATCH] Got extension methods working for DynamicDocument. Exposes some new APIs publicly because they need to work with type finding and med trust, plus we need to expose these for MVC --- src/Umbraco.Core/Dynamics/DynamicDocument.cs | 42 +++++++++---- .../Dynamics/DynamicDocumentList.cs | 61 ++++++++++++------- src/Umbraco.Core/Dynamics/DynamicGrouping.cs | 6 +- .../Dynamics/ExtensionMethodFinder.cs | 12 +++- src/Umbraco.Core/Models/IDocument.cs | 2 +- src/Umbraco.Tests/DynamicDocumentTests.cs | 2 +- 6 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/Umbraco.Core/Dynamics/DynamicDocument.cs b/src/Umbraco.Core/Dynamics/DynamicDocument.cs index d5f7481e88..9a07d5715c 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocument.cs +++ b/src/Umbraco.Core/Dynamics/DynamicDocument.cs @@ -13,7 +13,7 @@ using System.Xml.Linq; namespace Umbraco.Core.Dynamics { - internal class DynamicDocument : DynamicObject + public class DynamicDocument : DynamicObject { private readonly DynamicBackingItem _backingItem; private DynamicDocumentList _cachedChildren; @@ -21,7 +21,7 @@ namespace Umbraco.Core.Dynamics internal DynamicDocumentList OwnerList { get; set; } - public DynamicDocument(DynamicBackingItem n) + internal DynamicDocument(DynamicBackingItem n) { if (n == null) throw new ArgumentNullException("n"); _backingItem = n; @@ -313,16 +313,26 @@ namespace Umbraco.Core.Dynamics private object ExecuteExtensionMethod(object[] args, string name, bool argsContainsThis) { object result = null; + + var methodTypesToFind = new[] + { + typeof(IDocument), + typeof(DynamicDocument) + }; - MethodInfo methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(IEnumerable), args, name, false); - if (methodToExecute == null) + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach (var t in methodTypesToFind) { - methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(DynamicDocumentList), args, name, false); + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; } - if (methodToExecute != null) + + if (toExecute != null) { var genericArgs = (new[] { this }).Concat(args); - result = methodToExecute.Invoke(null, genericArgs.ToArray()); + result = toExecute.Invoke(null, genericArgs.ToArray()); } else { @@ -330,14 +340,22 @@ namespace Umbraco.Core.Dynamics } if (result != null) { - if (result is IEnumerable) + if (result is IDocument) { - result = new DynamicDocumentList((IEnumerable)result); - } + result = new DynamicDocument((IDocument)result); + } if (result is DynamicBackingItem) { result = new DynamicDocument((DynamicBackingItem)result); } + if (result is IEnumerable) + { + result = new DynamicDocumentList((IEnumerable)result); + } + if (result is IEnumerable) + { + result = new DynamicDocumentList((IEnumerable)result); + } } return result; } @@ -808,7 +826,7 @@ namespace Umbraco.Core.Dynamics { return Descendants(n => true); } - public DynamicDocumentList Descendants(Func func) + internal DynamicDocumentList Descendants(Func func) { var flattenedNodes = this._backingItem.Children.Map(func, (DynamicBackingItem n) => n.Children); return new DynamicDocumentList(flattenedNodes.ToList().ConvertAll(dynamicBackingItem => new DynamicDocument(dynamicBackingItem))); @@ -825,7 +843,7 @@ namespace Umbraco.Core.Dynamics { return DescendantsOrSelf(p => true); } - public DynamicDocumentList DescendantsOrSelf(Func func) + internal DynamicDocumentList DescendantsOrSelf(Func func) { if (this._backingItem != null) { diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs b/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs index aa563d7860..8b37f007fe 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs +++ b/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs @@ -9,7 +9,7 @@ using System.Reflection; namespace Umbraco.Core.Dynamics { - internal class DynamicDocumentList : DynamicObject, IEnumerable + public class DynamicDocumentList : DynamicObject, IEnumerable { internal List Items { get; set; } @@ -179,7 +179,7 @@ namespace Umbraco.Core.Dynamics try { - result = ExecuteExtensionMethod(args, name, false); + result = ExecuteExtensionMethod(args, name); return true; } catch (TargetInvocationException) @@ -347,26 +347,37 @@ namespace Umbraco.Core.Dynamics return result; } - private object ExecuteExtensionMethod(object[] args, string name, bool argsContainsThis) + private object ExecuteExtensionMethod(object[] args, string name) { object result = null; - MethodInfo methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(IEnumerable), args, name, false); - if (methodToExecute == null) + var methodTypesToFind = new[] + { + typeof(IEnumerable), + typeof(IEnumerable), + typeof(DynamicDocumentList) + }; + + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach(var t in methodTypesToFind) + { + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; + } + + if (toExecute != null) { - methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(DynamicDocumentList), args, name, false); - } - if (methodToExecute != null) - { - if (methodToExecute.GetParameters().First().ParameterType == typeof(DynamicDocumentList)) + if (toExecute.GetParameters().First().ParameterType == typeof(DynamicDocumentList)) { var genericArgs = (new[] { this }).Concat(args); - result = methodToExecute.Invoke(null, genericArgs.ToArray()); + result = toExecute.Invoke(null, genericArgs.ToArray()); } else { var genericArgs = (new[] { Items }).Concat(args); - result = methodToExecute.Invoke(null, genericArgs.ToArray()); + result = toExecute.Invoke(null, genericArgs.ToArray()); } } else @@ -375,18 +386,22 @@ namespace Umbraco.Core.Dynamics } if (result != null) { - if (result is IEnumerable) - { + if (result is IDocument) + { + result = new DynamicDocument((IDocument)result); + } + if (result is DynamicBackingItem) + { + result = new DynamicDocument((DynamicBackingItem)result); + } + if (result is IEnumerable) + { result = new DynamicDocumentList((IEnumerable)result); - } - if (result is IEnumerable) - { - result = new DynamicDocumentList((IEnumerable)result); - } - if (result is INode) - { - result = new DynamicDocument((IDocument)result); - } + } + if (result is IEnumerable) + { + result = new DynamicDocumentList((IEnumerable)result); + } } return result; } diff --git a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs index 5d932869fc..d843fa52b1 100644 --- a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs +++ b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs @@ -4,9 +4,9 @@ using System.Collections; namespace Umbraco.Core.Dynamics { - internal class DynamicGrouping : IEnumerable + public class DynamicGrouping : IEnumerable { - public IEnumerable> Inner; + internal IEnumerable> Inner; public DynamicGrouping OrderBy(string expression) { @@ -38,7 +38,7 @@ namespace Umbraco.Core.Dynamics Elements = item.Select(inner => inner.Node) }); } - public DynamicGrouping(IEnumerable> source) + internal DynamicGrouping(IEnumerable> source) { this.Inner = source; } diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs index 3bb90e47d8..1939ace694 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs @@ -11,13 +11,21 @@ namespace Umbraco.Core.Dynamics { internal static class ExtensionMethodFinder { - //TODO: This should be highly optimized!!! Calls to BuildManager.GetReferencedAssemblies() are really expensive! + + private static List GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis) { + //only scan assemblies we know to contain extension methods (user assemblies) + //var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions(); + var assembliesToScan = new List() + { + Assembly.Load("Umbraco.Tests") + }; + //get extension methods from runtime var candidates = ( - from assembly in BuildManager.GetReferencedAssemblies().Cast() + from assembly in assembliesToScan where assembly.IsDefined(typeof(ExtensionAttribute), false) from type in assembly.GetTypes() where (type.IsDefined(typeof(ExtensionAttribute), false) diff --git a/src/Umbraco.Core/Models/IDocument.cs b/src/Umbraco.Core/Models/IDocument.cs index 2bfd4b5840..c162157eab 100644 --- a/src/Umbraco.Core/Models/IDocument.cs +++ b/src/Umbraco.Core/Models/IDocument.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Models /// A replacement for INode which needs to occur since INode doesn't contain the document type alias /// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable) /// - internal interface IDocument + public interface IDocument { IDocument Parent { get; } int Id { get; } diff --git a/src/Umbraco.Tests/DynamicDocumentTests.cs b/src/Umbraco.Tests/DynamicDocumentTests.cs index 1bfbfc5a67..c7b30229a6 100644 --- a/src/Umbraco.Tests/DynamicDocumentTests.cs +++ b/src/Umbraco.Tests/DynamicDocumentTests.cs @@ -321,7 +321,7 @@ namespace Umbraco.Tests public static class DynamicDocumentCustomExtensionMethods { - internal static string ReturnHelloWorld(this DynamicDocument doc) + public static string ReturnHelloWorld(this DynamicDocument doc) { return "Hello world"; }