Improved the handling of DynamicNull when using OrderBy

This commit is contained in:
agrath
2011-08-29 11:19:34 -12:00
parent 9925c95d2c
commit 265fc0b3ca
2 changed files with 72 additions and 51 deletions

View File

@@ -258,6 +258,7 @@ namespace umbraco.MacroEngines
{
var types = from i in query
group i by i.GetType() into g
where g.Key != typeof(DynamicNull)
orderby g.Count() descending
select new { g, Instances = g.Count() };
var dominantType = types.First().g.Key;
@@ -314,6 +315,7 @@ namespace umbraco.MacroEngines
{
var types = from i in query
group i by i.GetType() into g
where g.Key != typeof(DynamicNull)
orderby g.Count() descending
select new { g, Instances = g.Count() };
var dominantType = types.First().g.Key;

View File

@@ -25,7 +25,7 @@ namespace System.Linq.Dynamic
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values);
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode))
{
//source list is DynamicNode and the lambda returns a Func<object>
@@ -95,7 +95,7 @@ namespace System.Linq.Dynamic
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, values);
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values);
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode))
{
//source list is DynamicNode and the lambda returns a Func<object>
@@ -153,44 +153,27 @@ namespace System.Linq.Dynamic
descending = true;
}
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, values);
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values);
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode))
{
//source list is DynamicNode and the lambda returns a Func<object>
Func<DynamicNode, object> func = (Func<DynamicNode, object>)lambda.Compile();
//get the values out
var query = typedSource.ToList().ConvertAll(item => new { node = item, key = EvaluateDynamicNodeFunc(item, func) });
var types = from i in query
group i by i.key.GetType() into g
where g.Key != typeof(DynamicNull)
orderby g.Count() descending
select new { g, Instances = g.Count() };
var dominantType = types.First().g.Key;
if (!descending)
{
return typedSource.OrderBy(delegate(DynamicNode node)
{
object value = -1;
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
if (firstFuncResult.GetType().IsValueType)
{
value = firstFuncResult;
}
return value;
}).AsQueryable();
return query.OrderBy(item => GetObjectAsTypeOrDefault(item.key, dominantType)).Select(item => item.node).AsQueryable();
}
else
{
return typedSource.OrderByDescending(delegate(DynamicNode node)
{
object value = -1;
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
if (firstFuncResult.GetType().IsValueType)
{
value = firstFuncResult;
}
return value;
}).AsQueryable();
return query.OrderByDescending(item => GetObjectAsTypeOrDefault(item.key, dominantType)).Select(item => item.node).AsQueryable();
}
}
}
@@ -239,7 +222,35 @@ namespace System.Linq.Dynamic
return source.Provider.CreateQuery(queryExpr);
}
private static object GetObjectAsTypeOrDefault(object value, Type type)
{
if (type.IsAssignableFrom(value.GetType()))
{
return (object)Convert.ChangeType(value, type);
}
else
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
private static object EvaluateDynamicNodeFunc(DynamicNode node, Func<DynamicNode, object> func)
{
object value = -1;
var firstFuncResult = func(node);
if (firstFuncResult is Func<DynamicNode, object>)
{
value = (firstFuncResult as Func<DynamicNode, object>)(node);
}
if (firstFuncResult.GetType().IsValueType)
{
value = firstFuncResult;
}
return value;
}
public static IQueryable Take(this IQueryable source, int count)
{
if (source == null) throw new ArgumentNullException("source");
@@ -265,8 +276,8 @@ namespace System.Linq.Dynamic
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, values);
LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, 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",
@@ -338,26 +349,29 @@ namespace System.Linq.Dynamic
public static class DynamicExpression
{
public static Expression Parse(Type resultType, string expression, params object[] values)
public static bool ConvertDynamicNullToBooleanFalse = false;
public static Expression Parse(Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values)
{
ConvertDynamicNullToBooleanFalse = convertDynamicNullToBooleanFalse;
ExpressionParser parser = new ExpressionParser(null, expression, values);
return parser.Parse(resultType);
}
public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, 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, values);
return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, convertDynamicNullToBooleanFalse, values);
}
public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, 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);
return Expression.Lambda(parser.Parse(resultType), parameters);
}
public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values)
public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, bool convertDynamicNullToBooleanFalse, params object[] values)
{
return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values);
return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, convertDynamicNullToBooleanFalse, values);
}
public static Type CreateClass(params DynamicProperty[] properties)
@@ -1532,6 +1546,7 @@ namespace System.Linq.Dynamic
//so that when it's evaluated, DynamicNode should be supported
ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicNode), "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");
@@ -1541,17 +1556,19 @@ namespace System.Linq.Dynamic
BlockExpression block = Expression.Block(
typeof(object),
new[] { ignoreCase, binder, result },
new[] { ignoreCase, binder, result, convertDynamicNullToBooleanFalse },
Expression.Assign(convertDynamicNullToBooleanFalse, Expression.Constant(DynamicExpression.ConvertDynamicNullToBooleanFalse, typeof(bool))),
Expression.Assign(ignoreCase, Expression.Constant(false, typeof(bool))),
Expression.Assign(binder, Expression.New(getMemberBinderConstructor, Expression.Constant(id, typeof(string)), ignoreCase)),
Expression.Assign(result, Expression.Constant(null)),
Expression.IfThen(Expression.NotEqual(Expression.Constant(null), instanceExpression),
Expression.Call(instanceExpression, method, binder, result)),
Expression.IfThen(
Expression.TypeEqual(result, typeof(DynamicNull)),
Expression.Assign(result,
Expression.Constant(false, typeof(object))
)
Expression.AndAlso(
Expression.TypeEqual(result, typeof(DynamicNull)),
Expression.Equal(convertDynamicNullToBooleanFalse, Expression.Constant(true, typeof(bool)))
),
Expression.Assign(result, Expression.Constant(false, typeof(object)))
),
Expression.Return(blockReturnLabel, result),
Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object)))
@@ -1564,7 +1581,7 @@ namespace System.Linq.Dynamic
//accessing a property off an already resolved DynamicNode TryGetMember call
//e.g. uBlogsyPostDate.Date
MethodInfo ReflectPropertyValue = this.GetType().GetMethod("ReflectPropertyValue", BindingFlags.NonPublic | BindingFlags.Static);
ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
ParameterExpression idParam = Expression.Parameter(typeof(string), "id");
ParameterExpression lambdaResult = Expression.Parameter(typeof(object), "lambdaResult");
@@ -1574,14 +1591,16 @@ namespace System.Linq.Dynamic
BlockExpression block = Expression.Block(
typeof(object),
new[] { lambdaResult, result, idParam },
new[] { lambdaResult, result, idParam, convertDynamicNullToBooleanFalse },
Expression.Assign(convertDynamicNullToBooleanFalse, Expression.Constant(DynamicExpression.ConvertDynamicNullToBooleanFalse, typeof(bool))),
Expression.Assign(lambdaResult, Expression.Invoke(instance, lambdaInstanceExpression)),
Expression.Assign(result, Expression.Call(ReflectPropertyValue, lambdaResult, Expression.Constant(id))),
Expression.IfThen(
Expression.TypeEqual(result, typeof(DynamicNull)),
Expression.Assign(result,
Expression.Constant(false, typeof(object))
)
Expression.AndAlso(
Expression.TypeEqual(result, typeof(DynamicNull)),
Expression.Equal(convertDynamicNullToBooleanFalse, Expression.Constant(true, typeof(bool)))
),
Expression.Assign(result, Expression.Constant(false, typeof(object)))
),
Expression.Return(blockReturnLabel, result),
Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object)))