2012-08-17 04:27:47 +06:00
|
|
|
|
//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;
|
|
|
|
|
|
|
|
|
|
|
|
namespace 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((IQueryable)source, predicate, values);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static IQueryable Where(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(source.ElementType, typeof(bool), predicate, true, values);
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicDocument))
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
//source list is DynamicNode and the lambda returns a Func<object>
|
2012-08-18 11:03:30 +06:00
|
|
|
|
IQueryable<DynamicDocument> typedSource = source as IQueryable<DynamicDocument>;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
var compiledFunc = lambda.Compile();
|
2012-08-18 11:03:30 +06:00
|
|
|
|
Func<DynamicDocument, object> func = null;
|
|
|
|
|
|
Func<DynamicDocument, bool> boolFunc = null;
|
|
|
|
|
|
if (compiledFunc is Func<DynamicDocument, object>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
func = (Func<DynamicDocument, object>)compiledFunc;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (compiledFunc is Func<DynamicDocument, bool>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
boolFunc = (Func<DynamicDocument, bool>)compiledFunc;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
2012-08-18 11:03:30 +06:00
|
|
|
|
return typedSource.Where(delegate(DynamicDocument node)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (firstFuncResult is Func<DynamicDocument, object>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
value = (firstFuncResult as Func<DynamicDocument, object>)(node);
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (firstFuncResult is Func<DynamicDocument, bool>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
value = (firstFuncResult as Func<DynamicDocument, bool>)(node);
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
|
|
|
|
|
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)));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-18 11:03:30 +06:00
|
|
|
|
public static IQueryable Select(this IQueryable<DynamicDocument> source, string selector, params object[] values)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicDocument))
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
//source list is DynamicNode and the lambda returns a Func<object>
|
2012-08-18 11:03:30 +06:00
|
|
|
|
IQueryable<DynamicDocument> typedSource = source as IQueryable<DynamicDocument>;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
var compiledFunc = lambda.Compile();
|
2012-08-18 11:03:30 +06:00
|
|
|
|
Func<DynamicDocument, object> func = null;
|
|
|
|
|
|
if (compiledFunc is Func<DynamicDocument, object>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
func = (Func<DynamicDocument, object>)compiledFunc;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
2012-08-18 11:03:30 +06:00
|
|
|
|
return typedSource.Select(delegate(DynamicDocument node)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
object value = null;
|
|
|
|
|
|
value = func(node);
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (value is Func<DynamicDocument, object>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
var innerValue = (value as Func<DynamicDocument, object>)(node);
|
2012-08-17 04:27:47 +06:00
|
|
|
|
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, params object[] values)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (source == null) throw new ArgumentNullException("source");
|
|
|
|
|
|
if (ordering == null) throw new ArgumentNullException("ordering");
|
|
|
|
|
|
|
2012-08-18 11:03:30 +06:00
|
|
|
|
IQueryable<DynamicDocument> typedSource = source as IQueryable<DynamicDocument>;
|
2012-08-17 04:27:47 +06:00
|
|
|
|
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(source.ElementType, typeof(object), ordering, false, values);
|
2012-08-18 11:03:30 +06:00
|
|
|
|
if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicDocument))
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
//source list is DynamicNode and the lambda returns a Func<object>
|
2012-08-18 11:03:30 +06:00
|
|
|
|
Func<DynamicDocument, object> func = (Func<DynamicDocument, object>)lambda.Compile();
|
2012-08-17 04:27:47 +06:00
|
|
|
|
//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, "") };
|
|
|
|
|
|
ExpressionParser parser = new ExpressionParser(parameters, ordering, values);
|
|
|
|
|
|
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(
|
2012-08-18 11:03:30 +06:00
|
|
|
|
typeof(DynamicDocumentListOrdering),
|
2012-08-17 04:27:47 +06:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-08-18 11:03:30 +06:00
|
|
|
|
private static object EvaluateDynamicNodeFunc(DynamicDocument document, Func<DynamicDocument, object> func)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
|
|
|
|
|
object value = -1;
|
2012-08-18 11:03:30 +06:00
|
|
|
|
var firstFuncResult = func(document);
|
|
|
|
|
|
if (firstFuncResult is Func<DynamicDocument, object>)
|
2012-08-17 04:27:47 +06:00
|
|
|
|
{
|
2012-08-18 11:03:30 +06:00
|
|
|
|
value = (firstFuncResult as Func<DynamicDocument, object>)(document);
|
2012-08-17 04:27:47 +06:00
|
|
|
|
}
|
|
|
|
|
|
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(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);
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|