From cd38123fbdf2c179998b25beb887900c3c40f420 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Sun, 7 Oct 2012 00:07:51 +0500 Subject: [PATCH] Fixes issue with DynamicExpression and parsing to ensure that it is using generic types, not hard coded types. Ensures unit tests passing for Where, Single, SingleOrDefault with filters with dynamics. --- .../DynamicDocumentTestsBase.cs | 12 ++-- src/Umbraco.Web/Dynamics/DynamicExpression.cs | 16 ++--- src/Umbraco.Web/Dynamics/DynamicQueryable.cs | 14 ++--- src/Umbraco.Web/Dynamics/ExpressionParser.cs | 58 +++++++++---------- .../RazorDynamicNode/DynamicExpression.cs | 9 +-- .../RazorDynamicNode/DynamicQueryable.cs | 2 +- .../RazorDynamicNode/ExpressionParser.cs | 3 +- 7 files changed, 58 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index 38a781f019..523befc611 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -116,7 +116,7 @@ namespace Umbraco.Tests.DynamicDocument { var doc = GetDynamicNode(1173); - var result = doc.Children().First("content==\"some content\""); + var result = doc.Children().First("blah==\"some content\""); Assert.IsNotNull(result); Assert.AreEqual(1176, result.Id); @@ -127,23 +127,23 @@ namespace Umbraco.Tests.DynamicDocument { var doc = GetDynamicNode(1173); - //var result = (IEnumerable) doc.Children().Where("content==\"some content\""); var result = (IEnumerable)doc.Children().Where("blah==\"some content\""); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count()); - Assert.AreEqual(1176, result.Single().Id); + Assert.AreEqual(1176, result.Single().Id); } [Test] public void Complex_Linq() { var doc = GetDynamicNode(1173); + var result = doc.Ancestors().OrderBy("level") .Single() .Descendants() - .Where("GetPropertyValue(\"selectedNodes\", \"\").Split(',').Contains(\"1173\")") + .Where("selectedNodes != null && selectedNodes != \"\" && selectedNodes.Split(new char[] {','}).Contains(\"1173\")") .FirstOrDefault(); Assert.IsNotNull(result); @@ -476,7 +476,7 @@ namespace Umbraco.Tests.DynamicDocument Assert.IsNotNull(result); var list = (IEnumerable)result; - Assert.AreEqual(8, list.Count()); + Assert.AreEqual(9, list.Count()); Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175, 4444 })); } @@ -490,7 +490,7 @@ namespace Umbraco.Tests.DynamicDocument Assert.IsNotNull(result); var list = (IEnumerable)result; - Assert.AreEqual(7, list.Count()); + Assert.AreEqual(8, list.Count()); Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 })); } diff --git a/src/Umbraco.Web/Dynamics/DynamicExpression.cs b/src/Umbraco.Web/Dynamics/DynamicExpression.cs index 848947b458..0b84c97ec7 100644 --- a/src/Umbraco.Web/Dynamics/DynamicExpression.cs +++ b/src/Umbraco.Web/Dynamics/DynamicExpression.cs @@ -8,28 +8,28 @@ namespace Umbraco.Web.Dynamics internal static class DynamicExpression { public static bool ConvertDynamicNullToBooleanFalse = false; - public static Expression Parse(Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) + public static Expression Parse(Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { ConvertDynamicNullToBooleanFalse = convertDynamicNullToBooleanFalse; - ExpressionParser parser = new ExpressionParser(null, expression, values); + var parser = new ExpressionParser(null, expression, values); return parser.Parse(resultType); } - public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) + public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, convertDynamicNullToBooleanFalse, values); + return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, convertDynamicNullToBooleanFalse, values); } - public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) + public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { ConvertDynamicNullToBooleanFalse = convertDynamicNullToBooleanFalse; - ExpressionParser parser = new ExpressionParser(parameters, expression, values); + var parser = new ExpressionParser(parameters, expression, values); return Expression.Lambda(parser.Parse(resultType), parameters); } - public static Expression> ParseLambda(string expression, bool convertDynamicNullToBooleanFalse, params object[] values) + public static Expression> ParseLambda(string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return (Expression>)ParseLambda(typeof(T), typeof(S), expression, convertDynamicNullToBooleanFalse, values); + return (Expression>)ParseLambda(typeof(T), typeof(S), expression, convertDynamicNullToBooleanFalse, values); } public static Type CreateClass(params DynamicProperty[] properties) diff --git a/src/Umbraco.Web/Dynamics/DynamicQueryable.cs b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs index 43db642871..817e58063e 100644 --- a/src/Umbraco.Web/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Dynamics { if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); - LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(T)) { //source list is DynamicNode and the lambda returns a Func @@ -92,7 +92,7 @@ namespace Umbraco.Web.Dynamics { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); - LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(T)) { //source list is DynamicNode and the lambda returns a Func @@ -150,7 +150,7 @@ namespace Umbraco.Web.Dynamics descending = true; } - LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(T)) { //source list is DynamicNode and the lambda returns a Func @@ -197,7 +197,7 @@ namespace Umbraco.Web.Dynamics ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(source.ElementType, "") }; - ExpressionParser parser = new ExpressionParser(parameters, ordering, values); + var parser = new ExpressionParser(parameters, ordering, values); IEnumerable orderings = parser.ParseOrdering(); Expression queryExpr = source.Expression; string methodAsc = "OrderBy"; @@ -282,13 +282,13 @@ namespace Umbraco.Web.Dynamics source.Expression, Expression.Constant(count))); } - public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) + public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (elementSelector == null) throw new ArgumentNullException("elementSelector"); - LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, true, values); - LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, true, values); + LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, true, values); + LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, true, values); return source.Provider.CreateQuery( Expression.Call( typeof(Queryable), "GroupBy", diff --git a/src/Umbraco.Web/Dynamics/ExpressionParser.cs b/src/Umbraco.Web/Dynamics/ExpressionParser.cs index 2808a8f0db..46e49b38e0 100644 --- a/src/Umbraco.Web/Dynamics/ExpressionParser.cs +++ b/src/Umbraco.Web/Dynamics/ExpressionParser.cs @@ -10,7 +10,7 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Dynamics { - internal class ExpressionParser + internal class ExpressionParser { struct Token { @@ -510,7 +510,7 @@ namespace Umbraco.Web.Dynamics (expr as LambdaExpression).Parameters.CopyTo(parameters, 0); var invokedExpr = Expression.Invoke(expr, parameters); var not = Expression.Not(Expression.TypeAs(invokedExpr, typeof(Nullable))); - expr = Expression.Lambda>( + expr = Expression.Lambda>( Expression.Condition( Expression.Property(not, "HasValue"), Expression.Property(not, "Value"), @@ -857,11 +857,11 @@ namespace Umbraco.Web.Dynamics Expression[] args = ParseArgumentList(); MethodBase mb; LambdaExpression instanceAsString = null; - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(T), "instance"); if (type.IsGenericType && type != typeof(string)) { var typeArgs = type.GetGenericArguments(); - if (typeArgs[0] == typeof(DynamicPublishedContent)) + if (typeArgs[0] == typeof(T)) { if (instance != null && instance is LambdaExpression) { @@ -932,14 +932,14 @@ namespace Umbraco.Web.Dynamics //this will invoke TryGetMember (but wrapped in an expression tree) //so that when it's evaluated, DynamicNode should be supported - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(T), "instance"); ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse"); ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression binder = Expression.Variable(typeof(DynamicQueryableGetMemberBinder), "binder"); ParameterExpression ignoreCase = Expression.Variable(typeof(bool), "ignoreCase"); ConstructorInfo getMemberBinderConstructor = typeof(DynamicQueryableGetMemberBinder).GetConstructor(new Type[] { typeof(string), typeof(bool) }); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); - MethodInfo method = typeof(DynamicPublishedContent).GetMethod("TryGetMember"); + MethodInfo method = typeof(T).GetMethod("TryGetMember"); BlockExpression block = Expression.Block( typeof(object), @@ -960,10 +960,10 @@ namespace Umbraco.Web.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, instanceExpression); + LambdaExpression lax = Expression.Lambda>(block, instanceExpression); return lax; } - if (typeof(Func).IsAssignableFrom(type)) + if (typeof(Func).IsAssignableFrom(type)) { //accessing a property off an already resolved DynamicNode TryGetMember call //e.g. uBlogsyPostDate.Date @@ -974,8 +974,8 @@ namespace Umbraco.Web.Dynamics ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression idParam = Expression.Parameter(typeof(string), "id"); ParameterExpression lambdaResult = Expression.Parameter(typeof(object), "lambdaResult"); - ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "lambdaInstanceExpression"); - ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); + ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(T), "lambdaInstanceExpression"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); BlockExpression block = Expression.Block( @@ -994,7 +994,7 @@ namespace Umbraco.Web.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); + LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); return lax; } } @@ -1053,11 +1053,11 @@ namespace Umbraco.Web.Dynamics switch (methodReturnType.Name) { case "String": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Int32": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Boolean": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); } return Expression.Call(instance, (MethodInfo)method, args); } @@ -1095,8 +1095,8 @@ namespace Umbraco.Web.Dynamics Expression.Return(cblockReturnLabel, cresult), Expression.Label(cblockReturnLabel, Expression.Constant(null, typeof(string)))); - LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); - var expression = Expression.Lambda>(cblock, instanceExpression); + LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); + var expression = Expression.Lambda>(cblock, instanceExpression); return expression; } @@ -1413,7 +1413,7 @@ namespace Umbraco.Web.Dynamics //if the type of the expression is a func - invokable returning object, //we are going to return it here, because we can get the real value when we actually have the instance //if (typeof(Func).IsAssignableFrom(expr.Type)) return expr; - if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContent)) + if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(T)) { return expr; } @@ -1692,12 +1692,12 @@ namespace Umbraco.Web.Dynamics UnaryExpression unboxedLeft = null, unboxedRight = null; ParameterExpression[] parameters = null; - if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(T)) { leftIsLambda = true; } - if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(T)) { rightIsLambda = true; } @@ -1751,11 +1751,11 @@ namespace Umbraco.Web.Dynamics if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, right as Expression>); + return ExpressionExtensions.And(left as Expression>, right as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.Or(left as Expression>, right as Expression>); + return ExpressionExtensions.Or(left as Expression>, right as Expression>); } } @@ -1786,11 +1786,11 @@ namespace Umbraco.Web.Dynamics //left is invoked and unboxed to right's TOut, right was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } } else @@ -1807,11 +1807,11 @@ namespace Umbraco.Web.Dynamics //right is invoked and unboxed to left's TOut, left was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } } @@ -1882,7 +1882,7 @@ namespace Umbraco.Web.Dynamics break; case ExpressionType.Modulo: binaryExpression = Expression.Modulo(finalLeft, finalRight); - return (Expression.Lambda>(binaryExpression, parameters)); + return (Expression.Lambda>(binaryExpression, parameters)); case ExpressionType.AndAlso: if ((leftIsLambda && rightIsLambda && sequenceEqual) || (!leftIsLambda && !rightIsLambda)) { @@ -1890,7 +1890,7 @@ namespace Umbraco.Web.Dynamics } else { - return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); } case ExpressionType.OrElse: if (leftIsLambda && rightIsLambda && sequenceEqual || (!leftIsLambda && !rightIsLambda)) @@ -1899,7 +1899,7 @@ namespace Umbraco.Web.Dynamics } else { - return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); } default: return Expression.Equal(left, right); @@ -1907,7 +1907,7 @@ namespace Umbraco.Web.Dynamics if (leftIsLambda || rightIsLambda) { var body = Expression.Condition(Expression.TypeEqual(innerLeft, right.Type), binaryExpression, Expression.Constant(false)); - return Expression.Lambda>(body, parameters); + return Expression.Lambda>(body, parameters); } else { diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs index f872950344..db823e1b69 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq.Expressions; +using umbraco.MacroEngines; namespace System.Linq.Dynamic { @@ -14,22 +15,22 @@ namespace System.Linq.Dynamic } public static Expression Parse(Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Web.Dynamics.DynamicExpression.Parse(resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.Parse(resultType, expression, convertDynamicNullToBooleanFalse, values); } public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(itType, resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(itType, resultType, expression, convertDynamicNullToBooleanFalse, values); } public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(parameters, resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(parameters, resultType, expression, convertDynamicNullToBooleanFalse, values); } public static Expression> ParseLambda(string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(expression, convertDynamicNullToBooleanFalse, values); } public static Type CreateClass(params DynamicProperty[] properties) diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs index 032997d987..7728b12afd 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs @@ -51,7 +51,7 @@ namespace System.Linq.Dynamic public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { - return (IQueryable)Umbraco.Web.Dynamics.DynamicQueryable.GroupBy(source, keySelector, elementSelector, values); + return (IQueryable)Umbraco.Web.Dynamics.DynamicQueryable.GroupBy(source, keySelector, elementSelector, values); } public static bool Any(this IQueryable source) diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs b/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs index a0673b9425..c9001e9495 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs @@ -6,7 +6,8 @@ using umbraco.MacroEngines; namespace System.Linq.Dynamic { - internal class ExpressionParser : Umbraco.Web.Dynamics.ExpressionParser + [Obsolete("This class is no longer used, use Umbraco.Web.Dynamics.ExpressionParser instead")] + internal class ExpressionParser : Umbraco.Web.Dynamics.ExpressionParser { public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) : base(parameters, expression, values)