From 48c45447e4a16eb9593e5870a700233c6af6dbf2 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Sun, 18 Nov 2012 10:28:53 +0500 Subject: [PATCH] Fixes: #U4-1207, Enhancements to DynamicXml (legacy and new), now implements proper IEnumerable interfaces. Added DynamicInstanceHelper for any dynamic object that should find matches on methods, properties and extension methods. Fixed ExtensionMethodFinder to find the Enumerable extensions (was previously set to IEnumerable which would find nothing). --- .../Dynamics/DynamicInstanceHelper.cs | 178 ++++++++++++++++++ src/Umbraco.Core/Dynamics/DynamicXml.cs | 86 ++++++++- .../Dynamics/ExtensionMethodFinder.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../DynamicPublishedContentTests.cs | 1 + .../PublishedContent/DynamicXmlTests.cs | 60 ++++++ .../Models/DynamicPublishedContent.cs | 132 ++++--------- .../Models/DynamicPublishedContentList.cs | 168 ++++------------- .../RazorDynamicNode/DynamicXml.cs | 31 ++- 9 files changed, 418 insertions(+), 241 deletions(-) create mode 100644 src/Umbraco.Core/Dynamics/DynamicInstanceHelper.cs diff --git a/src/Umbraco.Core/Dynamics/DynamicInstanceHelper.cs b/src/Umbraco.Core/Dynamics/DynamicInstanceHelper.cs new file mode 100644 index 0000000000..a7bb20ef5b --- /dev/null +++ b/src/Umbraco.Core/Dynamics/DynamicInstanceHelper.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Dynamics +{ + /// + /// A helper class to try invoke members, find properties, etc... + /// + internal class DynamicInstanceHelper + { + + internal class TryInvokeMemberResult + { + public object ObjectResult { get; private set; } + public TryInvokeMemberSuccessReason Reason { get; private set; } + + public TryInvokeMemberResult(object result, TryInvokeMemberSuccessReason reason) + { + ObjectResult = result; + Reason = reason; + } + } + + internal enum TryInvokeMemberSuccessReason + { + FoundProperty, + FoundMethod, + FoundExtensionMethod + } + + /// + /// Attempts to invoke a member based on the dynamic instance + /// + /// + /// The object instance to invoke the extension method for + /// + /// + /// + /// + /// First tries to find a property with the binder name, if that fails it will try to find a static or instance method + /// on the object that matches the binder name + /// + public static Attempt TryInvokeMember(T thisObject, InvokeMemberBinder binder, object[] args) + { + return TryInvokeMember(thisObject, binder, args, null); + } + + /// + /// Attempts to invoke a member based on the dynamic instance + /// + /// + /// The object instance to invoke the extension method for + /// + /// + /// The types to scan for extension methods + /// + /// + /// First tries to find a property with the binder name, if that fails it will try to find a static or instance method + /// on the object that matches the binder name, if that fails it will then attempt to invoke an extension method + /// based on the binder name and the extension method types to scan. + /// + public static Attempt TryInvokeMember(T thisObject, + InvokeMemberBinder binder, + object[] args, + IEnumerable findExtensionMethodsOnTypes) + { + //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! + object result; + try + { + //Property? + result = typeof(T).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.GetProperty, + null, + thisObject, + args); + return new Attempt(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundProperty)); + } + catch (MissingMethodException) + { + try + { + //Static or Instance Method? + result = typeof(T).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.InvokeMethod, + null, + thisObject, + args); + return new Attempt(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundMethod)); + } + catch (MissingMethodException) + { + if (findExtensionMethodsOnTypes != null) + { + try + { + result = FindAndExecuteExtensionMethod(thisObject, args, binder.Name, findExtensionMethodsOnTypes); + return new Attempt(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundExtensionMethod)); + } + catch (TargetInvocationException ext) + { + //don't log here, we return this exception because the caller may need to do something specific when + //this exception occurs. + return new Attempt(ext); + } + catch (Exception ex) + { + var sb = new StringBuilder("An error occurred finding an executing an extension method for type "); + sb.Append(typeof (T)); + sb.Append("Types searched for extension methods were "); + foreach(var t in findExtensionMethodsOnTypes) + { + sb.Append(t + ","); + } + LogHelper.Error(sb.ToString(), ex); + return new Attempt(ex); + } + } + return Attempt.False; + } + } + catch (Exception ex) + { + LogHelper.Error("An unhandled exception occurred in method TryInvokeMember", ex); + return new Attempt(ex); + } + } + + /// + /// Attempts to find an extension method that matches the name and arguments based on scanning the Type's passed in + /// to the findMethodsOnTypes parameter + /// + /// The instance object to execute the extension method for + /// + /// + /// + /// + internal static object FindAndExecuteExtensionMethod(T thisObject, + object[] args, + string name, + IEnumerable findMethodsOnTypes) + { + object result = null; + + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach (var t in findMethodsOnTypes) + { + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; + } + + if (toExecute != null) + { + var genericArgs = (new[] { (object)thisObject }).Concat(args); + result = toExecute.Invoke(null, genericArgs.ToArray()); + } + else + { + throw new MissingMethodException(); + } + return result; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Dynamics/DynamicXml.cs b/src/Umbraco.Core/Dynamics/DynamicXml.cs index 80a3303a60..ef3114f111 100644 --- a/src/Umbraco.Core/Dynamics/DynamicXml.cs +++ b/src/Umbraco.Core/Dynamics/DynamicXml.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Dynamic; +using System.Reflection; using System.Xml.Linq; using System.Xml.XPath; using System.Collections; @@ -10,7 +11,7 @@ using System.Web; namespace Umbraco.Core.Dynamics { - public class DynamicXml : DynamicObject, IEnumerable + public class DynamicXml : DynamicObject, IEnumerable, IEnumerable { public XElement BaseElement { get; set; } @@ -70,7 +71,51 @@ namespace Umbraco.Core.Dynamics HandleIEnumerableXElement(elements, out result); return true; //anyway } - return base.TryInvokeMember(binder, args, out result); + + //ok, now lets try to match by member, property, extensino method + var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[] + { + typeof (IEnumerable), + typeof (DynamicXml) + }); + + if (attempt.Success) + { + result = attempt.Result.ObjectResult; + + //need to check the return type and possibly cast if result is from an extension method found + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod) + { + if (attempt.Result.ObjectResult != null) + { + if (attempt.Result.ObjectResult is XElement) + { + result = new DynamicXml((XElement) attempt.Result.ObjectResult); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = ((IEnumerable) attempt.Result.ObjectResult).Select(x => new DynamicXml(x)); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = ((IEnumerable)attempt.Result.ObjectResult).Select(x => new DynamicXml(x.BaseElement)); + } + } + } + return true; + } + + //this is the result of an extension method execution gone wrong so we return dynamic null + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod + && attempt.Error != null && attempt.Error is TargetInvocationException) + { + result = new DynamicNull(); + return true; + } + + result = null; + return false; + } public override bool TryGetMember(GetMemberBinder binder, out object result) { @@ -181,20 +226,41 @@ namespace Umbraco.Core.Dynamics return new DynamicXml(this.BaseElement.XPathSelectElements(expression).FirstOrDefault()); } - public IEnumerator GetEnumerator() - { - return this.BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return this.BaseElement.Elements().GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return this.BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + public int Count() { - return this.BaseElement.Elements().Count(); + return ((IEnumerable)this).Count(); } public bool Any() { - return this.BaseElement.Elements().Any(); + return ((IEnumerable)this).Any(); } + public IEnumerable Take(int count) + { + return ((IEnumerable)this).Take(count); + } + + public IEnumerable Skip(int count) + { + return ((IEnumerable)this).Skip(count); + } + public bool IsNull() { return false; @@ -514,7 +580,7 @@ namespace Umbraco.Core.Dynamics } public IEnumerable Ancestors(Func func) { - List ancestorList = new List(); + var ancestorList = new List(); var node = this.BaseElement; while (node != null) { @@ -661,5 +727,7 @@ namespace Umbraco.Core.Dynamics } } } + + } } diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs index 6dd8a1ad12..2fe86ddeec 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Dynamics ); //add the extension methods defined in IEnumerable - candidates = candidates.Concat(typeof(IEnumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)); + candidates = candidates.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)); //filter by name var methodsByName = candidates.Where(m => m.Name == name); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1415d981ae..36b279ce81 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -65,6 +65,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs index 3e0d52feb1..e8bb815802 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs @@ -77,6 +77,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("Hello world!" + 123 + false, asDynamic.DynamicDocumentMultiParam("Hello world!", 123, false)); Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentListMultiParam("Hello world!", 123, false)); Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentEnumerableMultiParam("Hello world!", 123, false)); + } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs index 479fcc5929..7c1a3598fb 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Microsoft.CSharp.RuntimeBinder; using NUnit.Framework; using Umbraco.Core.Dynamics; +using System.Linq; namespace Umbraco.Tests.PublishedContent { @@ -10,6 +11,65 @@ namespace Umbraco.Tests.PublishedContent public class DynamicXmlTests { + [Test] + public void Custom_Extension_Method_Legacy() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var typedXml = new global::umbraco.MacroEngines.DynamicXml(xml); + dynamic dynamicXml = typedXml; + + //we haven't explicitly defined ElementAt so this will dynamically invoke this method + var element = dynamicXml.ElementAt(0); + + Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value); + } + + [Test] + public void Custom_Extension_Method() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var typedXml = new DynamicXml(xml); + + dynamic dynamicXml = typedXml; + + //we haven't explicitly defined ElementAt so this will dynamically invoke this method + var element = dynamicXml.ElementAt(0); + + Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value); + } + + [Test] + public void Take_Legacy() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var typedXml = new global::umbraco.MacroEngines.DynamicXml(xml); + dynamic dynamicXml = typedXml; + var typedTaken = typedXml.Take(1); + var dynamicTaken = dynamicXml.Take(1); + + Assert.AreEqual(1, typedTaken.Count()); + Assert.AreEqual(1, Enumerable.Count(dynamicTaken)); + + Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value); + Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value); + } + + [Test] + public void Take() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var typedXml = new DynamicXml(xml); + dynamic dynamicXml = typedXml; + var typedTaken = typedXml.Take(1); + var dynamicTaken = dynamicXml.Take(1); + + Assert.AreEqual(1, typedTaken.Count()); + Assert.AreEqual(1, Enumerable.Count(dynamicTaken)); + + Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value); + Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value); + } + [Test] public void Ensure_Legacy_Objects_Are_Returned() { diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs index 107c20b70f..c41fd4b540 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContent.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -54,111 +54,47 @@ namespace Umbraco.Web.Models /// public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { - //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! - - try - { - //Property? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.GetProperty, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - //Static or Instance Method? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.InvokeMethod, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - result = ExecuteExtensionMethod(args, binder.Name); - return true; - } - catch (TargetInvocationException) - { - result = new DynamicNull(); - return true; - } - - catch - { - //TODO: LOg this! - - result = null; - return false; - } - - } - - - } - catch - { - result = null; - return false; - } - - } - - private object ExecuteExtensionMethod(object[] args, string name) - { - object result = null; - - var methodTypesToFind = new[] + var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[] { typeof(DynamicPublishedContent) - }; + }); - //find known extension methods that match the first type in the list - MethodInfo toExecute = null; - foreach (var t in methodTypesToFind) + if (attempt.Success) { - toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); - if (toExecute != null) - break; - } + result = attempt.Result.ObjectResult; - if (toExecute != null) - { - var genericArgs = (new[] { this }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else - { - throw new MissingMethodException(); - } - if (result != null) - { - if (result is IPublishedContent) - { - result = new DynamicPublishedContent((IPublishedContent)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); + //need to check the return type and possibly cast if result is from an extension method found + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod) + { + if (attempt.Result.ObjectResult != null) + { + if (attempt.Result.ObjectResult is IPublishedContent) + { + result = new DynamicPublishedContent((IPublishedContent)attempt.Result.ObjectResult); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); + } + } } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } + return true; } - return result; + + //this is the result of an extension method execution gone wrong so we return dynamic null + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod + && attempt.Error != null && attempt.Error is TargetInvocationException) + { + result = new DynamicNull(); + return true; + } + + result = null; + return false; } /// diff --git a/src/Umbraco.Web/Models/DynamicPublishedContentList.cs b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs index 7740a485fd..94d32dade3 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContentList.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs @@ -46,9 +46,6 @@ namespace Umbraco.Web.Models } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { - - //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods and processing this stuff! - //TODO: Nowhere here are we checking if args is the correct length! //NOTE: For many of these we could actually leave them out since we are executing custom extension methods and because @@ -196,11 +193,6 @@ namespace Umbraco.Web.Models { result = new DynamicPublishedContentList(this.Items.Except(firstArg as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; - } - if ((firstArg as DynamicPublishedContentList) != null) - { - result = new DynamicPublishedContentList(this.Items.Except((firstArg as DynamicPublishedContentList).Items, new DynamicPublishedContentIdEqualityComparer())); - return true; } } if (name == "Intersect") @@ -209,11 +201,6 @@ namespace Umbraco.Web.Models { result = new DynamicPublishedContentList(this.Items.Intersect(firstArg as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; - } - if ((firstArg as DynamicPublishedContentList) != null) - { - result = new DynamicPublishedContentList(this.Items.Intersect((firstArg as DynamicPublishedContentList).Items, new DynamicPublishedContentIdEqualityComparer())); - return true; } } if (name == "Distinct") @@ -226,65 +213,50 @@ namespace Umbraco.Web.Models result = Pluck(args); return true; } - try - { - //Property? - result = Items.GetType().InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.GetProperty, - null, - Items, - args); - return true; - } - catch (MissingMethodException) - { - try - { - //Static or Instance Method? - result = Items.GetType().InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.InvokeMethod, - null, - Items, - args); - return true; - } - catch (MissingMethodException) - { - try - { - result = ExecuteExtensionMethod(args, name); - return true; - } - catch (TargetInvocationException) - { - //We do this to enable error checking of Razor Syntax when a method e.g. ElementAt(2) is used. - //When the Script is tested, there's no Children which means ElementAt(2) is invalid (IndexOutOfRange) - //Instead, we are going to return DynamicNull; - result = new DynamicNull(); - return true; - } + //ok, now lets try to match by member, property, extensino method + var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[] + { + typeof (IEnumerable), + typeof (DynamicPublishedContentList) + }); - catch - { - result = null; - return false; - } + if (attempt.Success) + { + result = attempt.Result.ObjectResult; - } + //need to check the return type and possibly cast if result is from an extension method found + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod) + { + if (attempt.Result.ObjectResult != null) + { + if (attempt.Result.ObjectResult is IPublishedContent) + { + result = new DynamicPublishedContent((IPublishedContent)attempt.Result.ObjectResult); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); + } + if (attempt.Result.ObjectResult is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); + } + } + } + return true; + } + //this is the result of an extension method execution gone wrong so we return dynamic null + if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod + && attempt.Error != null && attempt.Error is TargetInvocationException) + { + result = new DynamicNull(); + return true; + } - } - catch - { - result = null; - return false; - } + result = null; + return false; } private T Aggregate(IEnumerable data, string name) where T : struct @@ -425,67 +397,7 @@ namespace Umbraco.Web.Models } return result; } - - private object ExecuteExtensionMethod(object[] args, string name) - { - object result = null; - - var methodTypesToFind = new[] - { - typeof(IEnumerable), - typeof(DynamicPublishedContentList) - }; - - //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) - { - if (toExecute.GetParameters().First().ParameterType == typeof(DynamicPublishedContentList)) - { - var genericArgs = (new[] { this }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else if (TypeHelper.IsTypeAssignableFrom(toExecute.GetParameters().First().ParameterType)) - { - //if it is IQueryable, we'll need to cast Items AsQueryable - var genericArgs = (new[] { Items.AsQueryable() }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else - { - var genericArgs = (new[] { Items }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - } - else - { - throw new MissingMethodException(); - } - if (result != null) - { - if (result is IPublishedContent) - { - result = new DynamicPublishedContent((IPublishedContent)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - } - return result; - } - + public T Single(string predicate, params object[] values) { return predicate.IsNullOrWhiteSpace() diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicXml.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicXml.cs index fa45127c52..1eff680b30 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicXml.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicXml.cs @@ -11,8 +11,8 @@ using System.Web; namespace umbraco.MacroEngines { - [Obsolete("This class has been superceded by Umbraco.Core.Dynamics.DynamicXml")] - public class DynamicXml : DynamicObject, IEnumerable + [Obsolete("This class has been superceded by Umbraco.Core.Dynamics.DynamicXml")] + public class DynamicXml : DynamicObject, IEnumerable, IEnumerable { private readonly Umbraco.Core.Dynamics.DynamicXml _inner; @@ -92,10 +92,21 @@ namespace umbraco.MacroEngines return new DynamicXml(_inner.BaseElement.XPathSelectElements(expression).FirstOrDefault()); } - public IEnumerator GetEnumerator() - { + IEnumerator IEnumerable.GetEnumerator() + { + return this.BaseElement.Elements().GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { return this.BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); - } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + public int Count() { return _inner.Count(); @@ -115,6 +126,16 @@ namespace umbraco.MacroEngines return _inner.HasValue(); } + public IEnumerable Take(int count) + { + return _inner.Take(count).Select(x => new DynamicXml(x.BaseElement)); + } + + public IEnumerable Skip(int count) + { + return _inner.Skip(count).Select(x => new DynamicXml(x.BaseElement)); + } + public bool IsFirst() { return _inner.IsFirst();