From bc3a5122dddd3db5b4272fb0f9f5316f94936acb Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 28 Aug 2012 09:20:30 +0700 Subject: [PATCH] Fixes DynamicNode issue not implementing IEnumerable Adds a few unit tests for DynamicDocument/DynamicNode and fixes both to properly support the 'Take' method as this wasn't really working. Not sure how or why it was working previously. Have also allowed for extension methods to be executed against either of these objects that accept IQueryable which was not supported before. --- src/Umbraco.Core/ApplicationContext.cs | 4 ++ src/Umbraco.Core/Dynamics/DynamicDocument.cs | 28 +++++----- .../Dynamics/DynamicDocumentList.cs | 54 +++++++++++++------ .../DynamicDocumentTestsBase.cs | 53 +++++++++++++++++- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 5 +- src/Umbraco.Web/Mvc/RenderViewPage.cs | 6 +++ .../RazorDynamicNode/DynamicNodeList.cs | 12 +++++ 7 files changed, 131 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 03c6154124..610af5380e 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -1,8 +1,12 @@ using System; +using System.Collections; using System.Configuration; +using System.Data; using System.Diagnostics; +using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Models; namespace Umbraco.Core diff --git a/src/Umbraco.Core/Dynamics/DynamicDocument.cs b/src/Umbraco.Core/Dynamics/DynamicDocument.cs index 50a474be6c..64795205db 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocument.cs +++ b/src/Umbraco.Core/Dynamics/DynamicDocument.cs @@ -13,6 +13,10 @@ using System.Xml.Linq; namespace Umbraco.Core.Dynamics { + + /// + /// The dynamic model for views + /// public class DynamicDocument : DynamicObject { private readonly DynamicBackingItem _backingItem; @@ -943,7 +947,7 @@ namespace Umbraco.Core.Dynamics var umbracoNaviHide = _backingItem.GetUserProperty("umbracoNaviHide"); if (umbracoNaviHide != null) { - return umbracoNaviHide.Value != "1"; + return umbracoNaviHide.Value.ToString().Trim() != "1"; } return true; } @@ -1083,18 +1087,7 @@ namespace Umbraco.Core.Dynamics } #endregion - - //public System.Data.DataTable ChildrenAsTable() - //{ - // if (_n == null) return null; - // return _n.ChildrenAsTable(); - //} - - //public System.Data.DataTable ChildrenAsTable(string nodeTypeAliasFilter) - //{ - // if (_n == null) return null; - // return _n.ChildrenAsTable(nodeTypeAliasFilter); - //} + public bool IsNull(string alias, bool recursive) { var prop = _backingItem.GetUserProperty(alias, recursive); @@ -1482,5 +1475,14 @@ namespace Umbraco.Core.Dynamics } return false; } + + /// + /// Returns the value as as string regardless of xml content or data type + /// + /// + public override string ToString() + { + return base.ToString(); + } } } diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs b/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs index 54bd02689a..bf4462c212 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs +++ b/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs @@ -56,20 +56,34 @@ namespace Umbraco.Core.Dynamics //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 + // we implement IEnumerable they will execute just fine, however, to do that will be quite a bit slower than checking here. + var name = binder.Name; if (name == "Where") { string predicate = args.First().ToString(); var values = args.Skip(1).ToArray(); - result = new DynamicDocumentList(this.Where(predicate, values).ToList()); + //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses + // are nested! We should somehow support an QueryableDocumentList! + result = new DynamicDocumentList(this.Where(predicate, values).ToList()); return true; } if (name == "OrderBy") { - result = new DynamicDocumentList(this.OrderBy(args.First().ToString()).ToList()); + //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses + // are nested! We should somehow support an QueryableDocumentList! + result = new DynamicDocumentList(this.OrderBy(args.First().ToString()).ToList()); return true; } - if (name == "InGroupsOf") + if (name == "Take") + { + result = new DynamicDocumentList(this.Take((int)args.First())); + return true; + } + if (name == "InGroupsOf") { int groupSize = 0; if (int.TryParse(args.First().ToString(), out groupSize)) @@ -105,12 +119,12 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Union(args.First() as IEnumerable)); + result = new DynamicDocumentList(this.Items.Union(args.First() as IEnumerable)); return true; } if ((args.First() as DynamicDocumentList) != null) { - result = new DynamicDocumentList(this.Items.Union((args.First() as DynamicDocumentList).Items)); + result = new DynamicDocumentList(this.Items.Union((args.First() as DynamicDocumentList).Items)); return true; } } @@ -118,12 +132,12 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Except(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); + result = new DynamicDocumentList(this.Items.Except(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); return true; } if ((args.First() as DynamicDocumentList) != null) { - result = new DynamicDocumentList(this.Items.Except((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); + result = new DynamicDocumentList(this.Items.Except((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); return true; } } @@ -131,12 +145,12 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); + result = new DynamicDocumentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); return true; } if ((args.First() as DynamicDocumentList) != null) { - result = new DynamicDocumentList(this.Items.Intersect((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); + result = new DynamicDocumentList(this.Items.Intersect((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); return true; } } @@ -376,11 +390,17 @@ namespace Umbraco.Core.Dynamics var genericArgs = (new[] { this }).Concat(args); result = toExecute.Invoke(null, genericArgs.ToArray()); } - else - { - var genericArgs = (new[] { Items }).Concat(args); + 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 { @@ -418,6 +438,10 @@ namespace Umbraco.Core.Dynamics // return Items.GetEnumerator(); //} + //public IQueryable Take(int count) + //{ + // return ((IQueryable)Items.AsQueryable()).Take(count); + //} public IQueryable Where(string predicate, params object[] values) { return ((IQueryable)Items.AsQueryable()).Where(predicate, values); @@ -428,7 +452,7 @@ namespace Umbraco.Core.Dynamics } public DynamicGrouping GroupBy(string key) { - DynamicGrouping group = new DynamicGrouping(this, key); + var group = new DynamicGrouping(this, key); return group; } public DynamicGrouping GroupedInto(int groupCount) @@ -462,7 +486,7 @@ namespace Umbraco.Core.Dynamics public IQueryable Select(string predicate, params object[] values) { - return DynamicQueryable.Select(Items.AsQueryable(), predicate, values); + return Items.AsQueryable().Select(predicate, values); } public void Add(DynamicDocument document) diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index 1adabb1833..9a82cc6939 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -18,7 +18,58 @@ namespace Umbraco.Tests.DynamicDocument /// /// protected abstract dynamic GetDynamicNode(int id); - + + [Test] + public void Children_Order_By_Update_Date() + { + var asDynamic = GetDynamicNode(1173); + + var ordered = asDynamic.Children.OrderBy("UpdateDate"); + var casted = (IEnumerable)ordered; + + var correctOrder = new[] { 1178, 1177, 1174, 1176 }; + for (var i = 0; i < correctOrder.Length ;i++) + { + Assert.AreEqual(correctOrder[i], ((dynamic)casted.ElementAt(i)).Id); + } + + } + + [Test] + public void Take() + { + var asDynamic = GetDynamicNode(1173); + + var ordered = asDynamic.Children.OrderBy("UpdateDate"); + var take = ordered.Take(2); + var casted = (IEnumerable)take; + + Assert.AreEqual(2, casted.Count()); + + } + + [Test] + public void Ancestors_Where_Visible() + { + var asDynamic = GetDynamicNode(1174); + + var whereVisible = asDynamic.Ancestors().Where("Visible"); + var casted = (IEnumerable)whereVisible; + + Assert.AreEqual(1, casted.Count()); + + } + + [Test] + public void Visible() + { + var asDynamicHidden = GetDynamicNode(1046); + var asDynamicVisible = GetDynamicNode(1173); + + Assert.IsFalse(asDynamicHidden.Visible); + Assert.IsTrue(asDynamicVisible.Visible); + } + [Test] public void Ensure_TinyMCE_Converted_Type_User_Property() { diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index b6754cfe7a..96e4da64f6 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -72,9 +72,10 @@ namespace Umbraco.Tests.TestHelpers + 1 This is some content]]> - + @@ -84,7 +85,7 @@ namespace Umbraco.Tests.TestHelpers - + diff --git a/src/Umbraco.Web/Mvc/RenderViewPage.cs b/src/Umbraco.Web/Mvc/RenderViewPage.cs index b58ea62444..3dff08149f 100644 --- a/src/Umbraco.Web/Mvc/RenderViewPage.cs +++ b/src/Umbraco.Web/Mvc/RenderViewPage.cs @@ -52,6 +52,12 @@ namespace Umbraco.Web.Mvc public dynamic CurrentPage { get; private set; } private ICultureDictionary _cultureDictionary; + + /// + /// Returns the dictionary value for the key specified + /// + /// + /// public string GetDictionaryValue(string key) { if (_cultureDictionary == null) diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNodeList.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNodeList.cs index e6c275d459..24e2101dae 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNodeList.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNodeList.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Dynamic; +using Umbraco.Core; using umbraco.interfaces; using System.Collections; using System.Reflection; @@ -78,6 +79,11 @@ namespace umbraco.MacroEngines result = new DynamicNodeList(this.OrderBy(args.First().ToString()).ToList()); return true; } + if (name == "Take") + { + result = new DynamicNodeList(this.Take((int)args.First())); + return true; + } if (name == "InGroupsOf") { int groupSize = 0; @@ -375,6 +381,12 @@ namespace umbraco.MacroEngines var genericArgs = (new[] { this }).Concat(args); result = methodToExecute.Invoke(null, genericArgs.ToArray()); } + else if (TypeHelper.IsTypeAssignableFrom(methodToExecute.GetParameters().First().ParameterType)) + { + //if it is IQueryable, we'll need to cast Items AsQueryable + var genericArgs = (new[] { Items.AsQueryable() }).Concat(args); + result = methodToExecute.Invoke(null, genericArgs.ToArray()); + } else { var genericArgs = (new[] { Items }).Concat(args);