323 lines
14 KiB
C#
323 lines
14 KiB
C#
//Copyright (C) Microsoft Corporation. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Diagnostics;
|
|
using Umbraco.Core.Dynamics;
|
|
using Umbraco.Web.Models;
|
|
|
|
namespace Umbraco.Web.Dynamics
|
|
{
|
|
//TODO: Much of this can move to Umbraco.Core.Dynamics - but somehow need to remove all the hard coded references to things like
|
|
// dynamicnull, etc...
|
|
//NOTE: The OrderBy stuff here seems to be a bit hacked with hard references to umbraco node objects so don't think it can be
|
|
// re-used which is why we have the OrderBy stuff that hasn't been hacked in teh Umbraco.Core.Dynamics
|
|
|
|
internal static class DynamicQueryable
|
|
{
|
|
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
|
|
{
|
|
return (IQueryable<T>)Where<T>((IQueryable)source, predicate, values);
|
|
}
|
|
|
|
public static IQueryable Where<T>(this IQueryable source, string predicate, params object[] values)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
if (predicate == null) throw new ArgumentNullException("predicate");
|
|
LambdaExpression lambda = DynamicExpression.ParseLambda<T>(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<object>
|
|
IQueryable<T> typedSource = source as IQueryable<T>;
|
|
var compiledFunc = lambda.Compile();
|
|
Func<T, object> func = null;
|
|
Func<T, bool> boolFunc = null;
|
|
if (compiledFunc is Func<T, object>)
|
|
{
|
|
func = (Func<T, object>)compiledFunc;
|
|
}
|
|
if (compiledFunc is Func<T, bool>)
|
|
{
|
|
boolFunc = (Func<T, bool>)compiledFunc;
|
|
}
|
|
return typedSource.Where(delegate(T node)
|
|
{
|
|
object value = -1;
|
|
//value = func(node);
|
|
//I can't figure out why this is double func<>'d
|
|
try
|
|
{
|
|
if (func != null)
|
|
{
|
|
var firstFuncResult = func(node);
|
|
if (firstFuncResult is Func<T, object>)
|
|
{
|
|
value = (firstFuncResult as Func<T, object>)(node);
|
|
}
|
|
if (firstFuncResult is Func<T, bool>)
|
|
{
|
|
value = (firstFuncResult as Func<T, bool>)(node);
|
|
}
|
|
if (firstFuncResult is bool)
|
|
{
|
|
return (bool)firstFuncResult;
|
|
}
|
|
if (value is bool)
|
|
{
|
|
return (bool)value;
|
|
}
|
|
}
|
|
if (boolFunc != null)
|
|
{
|
|
return boolFunc(node);
|
|
}
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Trace.WriteLine(ex.Message);
|
|
return false;
|
|
}
|
|
}).AsQueryable();
|
|
}
|
|
else
|
|
{
|
|
return source.Provider.CreateQuery(
|
|
Expression.Call(
|
|
typeof(Queryable), "Where",
|
|
new Type[] { source.ElementType },
|
|
source.Expression, Expression.Quote(lambda)));
|
|
}
|
|
}
|
|
|
|
public static IQueryable Select<T>(this IQueryable<T> source, string selector, params object[] values)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
if (selector == null) throw new ArgumentNullException("selector");
|
|
LambdaExpression lambda = DynamicExpression.ParseLambda<T>(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<object>
|
|
IQueryable<T> typedSource = source as IQueryable<T>;
|
|
var compiledFunc = lambda.Compile();
|
|
Func<T, object> func = null;
|
|
if (compiledFunc is Func<T, object>)
|
|
{
|
|
func = (Func<T, object>)compiledFunc;
|
|
}
|
|
return typedSource.Select(delegate(T node)
|
|
{
|
|
object value = null;
|
|
value = func(node);
|
|
if (value is Func<T, object>)
|
|
{
|
|
var innerValue = (value as Func<T, object>)(node);
|
|
return innerValue;
|
|
}
|
|
return value;
|
|
}).AsQueryable();
|
|
}
|
|
else
|
|
{
|
|
return source.Provider.CreateQuery(
|
|
Expression.Call(
|
|
typeof(Queryable), "Select",
|
|
new Type[] { source.ElementType, lambda.Body.Type },
|
|
source.Expression, Expression.Quote(lambda)));
|
|
}
|
|
}
|
|
|
|
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, Func<Type> getDynamicListTypeCallback, params object[] values)
|
|
{
|
|
return (IQueryable<T>)OrderBy<T>((IQueryable)source, ordering, getDynamicListTypeCallback, values);
|
|
}
|
|
|
|
public static IQueryable OrderBy<T>(this IQueryable source, string ordering, Func<Type> getDynamicListTypeCallback, params object[] values)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
if (ordering == null) throw new ArgumentNullException("ordering");
|
|
|
|
IQueryable<T> typedSource = source as IQueryable<T>;
|
|
if (!ordering.Contains(","))
|
|
{
|
|
bool descending = false;
|
|
if (ordering.IndexOf(" descending", StringComparison.CurrentCultureIgnoreCase) >= 0)
|
|
{
|
|
ordering = ordering.Replace(" descending", "");
|
|
descending = true;
|
|
}
|
|
if (ordering.IndexOf(" desc", StringComparison.CurrentCultureIgnoreCase) >= 0)
|
|
{
|
|
ordering = ordering.Replace(" desc", "");
|
|
descending = true;
|
|
}
|
|
|
|
LambdaExpression lambda = DynamicExpression.ParseLambda<T>(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<object>
|
|
Func<T, object> func = (Func<T, object>)lambda.Compile();
|
|
//get the values out
|
|
var query = typedSource.ToList().ConvertAll(item => new { node = item, key = EvaluateDynamicNodeFunc(item, func) });
|
|
if (query.Count == 0)
|
|
{
|
|
return source;
|
|
}
|
|
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;
|
|
|
|
// NH - add culture dependencies
|
|
StringComparer comp = StringComparer.Create(CultureInfo.CurrentCulture, true);
|
|
|
|
if (!descending)
|
|
{
|
|
// if the dominant type is a string we'll ensure that strings are sorted based on culture settings on node
|
|
if (dominantType.FullName == "System.String")
|
|
return query.OrderBy(item => item.key.ToString(), comp).Select(item => item.node).AsQueryable();
|
|
else
|
|
return query.OrderBy(item => GetObjectAsTypeOrDefault(item.key, dominantType)).Select(item => item.node).AsQueryable();
|
|
}
|
|
else
|
|
{
|
|
if (dominantType.FullName == "System.String")
|
|
return query.OrderByDescending(item => item.key.ToString(), comp).Select(item => item.node).AsQueryable();
|
|
else
|
|
return query.OrderByDescending(item => GetObjectAsTypeOrDefault(item.key, dominantType)).Select(item => item.node).AsQueryable();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isDynamicNodeList = false;
|
|
if (typedSource != null)
|
|
{
|
|
isDynamicNodeList = true;
|
|
}
|
|
|
|
ParameterExpression[] parameters = new ParameterExpression[] {
|
|
Expression.Parameter(source.ElementType, "") };
|
|
var parser = new ExpressionParser<T>(parameters, ordering, values, false);
|
|
IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering();
|
|
Expression queryExpr = source.Expression;
|
|
string methodAsc = "OrderBy";
|
|
string methodDesc = "OrderByDescending";
|
|
foreach (DynamicOrdering o in orderings)
|
|
{
|
|
if (!isDynamicNodeList)
|
|
{
|
|
queryExpr = Expression.Call(
|
|
typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
|
|
new Type[] { source.ElementType, o.Selector.Type },
|
|
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
|
|
}
|
|
else
|
|
{
|
|
//reroute each stacked Expression.Call into our own methods that know how to deal
|
|
//with DynamicNode
|
|
queryExpr = Expression.Call(
|
|
getDynamicListTypeCallback(),
|
|
o.Ascending ? methodAsc : methodDesc,
|
|
null,
|
|
queryExpr,
|
|
Expression.Quote(Expression.Lambda(o.Selector, parameters))
|
|
);
|
|
}
|
|
methodAsc = "ThenBy";
|
|
methodDesc = "ThenByDescending";
|
|
}
|
|
if (isDynamicNodeList)
|
|
{
|
|
return typedSource.Provider.CreateQuery(queryExpr);
|
|
}
|
|
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<T>(T publishedContent, Func<T, object> func)
|
|
{
|
|
object value = -1;
|
|
var firstFuncResult = func(publishedContent);
|
|
if (firstFuncResult is Func<T, object>)
|
|
{
|
|
value = (firstFuncResult as Func<T, object>)(publishedContent);
|
|
}
|
|
if (firstFuncResult.GetType().IsValueType || firstFuncResult is string)
|
|
{
|
|
value = firstFuncResult;
|
|
}
|
|
return value;
|
|
}
|
|
public static IQueryable Take(this IQueryable source, int count)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
return source.Provider.CreateQuery(
|
|
Expression.Call(
|
|
typeof(Queryable), "Take",
|
|
new Type[] { source.ElementType },
|
|
source.Expression, Expression.Constant(count)));
|
|
}
|
|
|
|
public static IQueryable Skip(this IQueryable source, int count)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
return source.Provider.CreateQuery(
|
|
Expression.Call(
|
|
typeof(Queryable), "Skip",
|
|
new Type[] { source.ElementType },
|
|
source.Expression, Expression.Constant(count)));
|
|
}
|
|
|
|
public static IQueryable GroupBy<T>(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<T>(source.ElementType, null, keySelector, true, values);
|
|
LambdaExpression elementLambda = DynamicExpression.ParseLambda<T>(source.ElementType, null, elementSelector, true, values);
|
|
return source.Provider.CreateQuery(
|
|
Expression.Call(
|
|
typeof(Queryable), "GroupBy",
|
|
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
|
|
source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda)));
|
|
}
|
|
|
|
public static bool Any(this IQueryable source)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
return (bool)source.Provider.Execute(
|
|
Expression.Call(
|
|
typeof(Queryable), "Any",
|
|
new Type[] { source.ElementType }, source.Expression));
|
|
}
|
|
|
|
public static int Count(this IQueryable source)
|
|
{
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
return (int)source.Provider.Execute(
|
|
Expression.Call(
|
|
typeof(Queryable), "Count",
|
|
new Type[] { source.ElementType }, source.Expression));
|
|
}
|
|
}
|
|
}
|