Cybtans.Expressions
ExpressionBuilder.cs
using Cybtans.Expressions.Ast;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Expression = System.Linq.Expressions.Expression;
using MemberExpression = System.Linq.Expressions.MemberExpression;
namespace Cybtans.Expressions
{
public clast ExpressionBuilder : QueryASTContext, IExpressionBuilder
{
protected clast ParamExpresionVisitor : ExpressionVisitor
{
public Expression ReplaceParameter;
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == ReplaceParameter.Type && node != ReplaceParameter)
return ReplaceParameter;
return node;
}
}
protected ParamExpresionVisitor paramExpresionVisitor = new ParamExpresionVisitor();
public ExpressionBuilder(string parameter = "x", MemberExpression referenceExpression = null)
{
ResponseType = typeof(T);
if (referenceExpression != null)
{
ModelType = ((PropertyInfo)referenceExpression.Member).PropertyType;
Parameter = referenceExpression;
paramExpresionVisitor.ReplaceParameter = Parameter;
}
else
{
ModelType = typeof(T);
Parameter = Expression.Parameter(ModelType, parameter);
paramExpresionVisitor.ReplaceParameter = Parameter;
}
}
public ExpressionBuilder(Expression expression)
: this("x", expression.Body as MemberExpression)
{
}
public Expression Where(string expression)
{
QueryParser parser = new QueryParser();
var astExp = parser.Parse(expression);
astExp.CheckSemantic(this);
Expression exp = astExp.GenerateLinqExpression(this);
Expression current = Parameter;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
return Expression.Lambda(exp, (ParameterExpression)current);
}
public IQueryable OrderBy(IQueryable query, string expression)
{
var method = typeof(Queryable).GetMember("OrderBy")[0] as MethodInfo;
var methodDesc = typeof(Queryable).GetMember("OrderByDescending")[0] as MethodInfo;
var thenBy = typeof(Queryable).GetMember("ThenBy")[0] as MethodInfo;
var thenByDesc = typeof(Queryable).GetMember("ThenByDescending")[0] as MethodInfo;
Expression current = Parameter;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
var parts = expression.Split(',');
IQueryable ordered = query;
var index = 0;
QueryParser parser = new QueryParser();
foreach (var part in parts)
{
var value = part.Trim();
string property;
bool descend = false;
if (value.Contains(' '))
{
var split = value.Split(' ');
descend = split[1].Trim().ToLowerInvariant() == "desc";
property = split[0].Trim();
}
else
{
property = value;
}
var astExp = parser.Parse(property);
astExp.CheckSemantic(this);
Expression exp = astExp.GenerateLinqExpression(this);
MethodInfo orderByMethod;
if (index == 0)
{
orderByMethod = descend ? methodDesc : method;
}
else
{
orderByMethod = descend ? thenByDesc : thenBy;
}
index++;
var lambda = Expression.Lambda(exp, (ParameterExpression)current);
ordered = ordered.Provider.CreateQuery(Expression.Call(null, orderByMethod.MakeGenericMethod(new Type[] { typeof(T), exp.Type }), new Expression[] { ordered.Expression, Expression.Quote(lambda) }));
}
return ordered;
}
public IQueryable Query(IQueryable query, string filter, string orderby, int skip, int take)
{
if (filter != null)
query = query.Where(Where(filter));
if (orderby != null)
query = OrderBy(query, orderby);
if (skip > 0)
{
query = query.Skip(skip);
}
if (take > 0)
query = query.Take(take);
return query;
}
public IQueryable Query(IQueryable query, string filter = null, string orderby = null, int skip = -1, int take = -1, string include = null)
{
if (filter != null)
query = query.Where(Where(filter));
if (orderby != null)
query = OrderBy(query, orderby);
if (skip > 0)
{
query = query.Skip(skip);
}
if (take > 0)
query = query.Take(take);
return query.Select(Select(include));
}
public Expression Select(string include = null)
{
List bindings = new List();
foreach (var p in ResponseType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.CanWrite))
{
NavigationPropertyAttribute navAttr = p.GetCustomAttribute();
if (navAttr != null)
{
bindings.Add(Expression.Bind(p, Expression.Property(Expression.Property(Parameter, navAttr.NavigationProperty), navAttr.Property)));
}
else if (IncludeTree.IsMappeable(p))
{
bindings.Add(Expression.Bind(p, Expression.Property(Parameter, p.Name)));
}
}
if (!string.IsNullOrWhiteSpace(include))
{
var incTrees = IncludeTree.Build(include, ResponseType, ModelType, Parameter);
foreach (var item in incTrees)
{
bindings.Add(Expression.Bind(item.ResponseProperty, item.CreateInitExpression()));
}
}
MemberInitExpression initExpression = Expression.MemberInit(Expression.New(typeof(TResponse)), bindings);
Expression current = Parameter;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
return Expression.Lambda(initExpression, (ParameterExpression)current);
}
public Expression[] Include(string expression)
{
var parts = expression.Split(',');
Expression[] includes = new Expression[parts.Length];
QueryParser parser = new QueryParser();
Expression current = Parameter;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
for (int i = 0; i < parts.Length; i++)
{
var part = parts[i];
var value = part.Trim();
var astExp = parser.Parse(value);
astExp.CheckSemantic(this);
Expression exp = astExp.GenerateLinqExpression(this);
includes[i] = Expression.Lambda(exp, (ParameterExpression)current);
}
return includes;
}
clast IncludeTree
{
internal static bool IsMappeable(PropertyInfo p)
{
return p.GetCustomAttribute() == null && (
p.PropertyType.IsValueType ||
p.PropertyType.IsEnum ||
p.PropertyType == typeof(byte[]) ||
p.PropertyType == typeof(string));
}
public string PropertyName;
public PropertyInfo ResponseProperty;
public MemberExpression ModelProperty;
public Dictionary Includes = new Dictionary();
public static IncludeTree[] Build(string include, Type resposeType, Type modelType, Expression parameter)
{
string[][] includesArray = include.Split(',').Select(x => x.Split('.')).ToArray();
Dictionary dic = new Dictionary();
for (int i = 0; i < includesArray.Length; i++)
{
IncludeTree incTree = null;
for (int j = 0; j < includesArray[i].Length; j++)
{
var propertyName = includesArray[i][j];
if (j == 0)
{
if (!dic.TryGetValue(propertyName, out incTree))
{
incTree = new IncludeTree
{
PropertyName = propertyName,
ResponseProperty = resposeType.GetProperty(propertyName),
ModelProperty = Expression.Property(parameter, modelType.GetProperty(propertyName))
};
dic.Add(propertyName, incTree);
}
}
else
{
IncludeTree child;
if (!incTree.Includes.TryGetValue(propertyName, out child))
{
child = new IncludeTree
{
PropertyName = propertyName,
ResponseProperty = incTree.ResponseProperty.PropertyType.GetProperty(propertyName),
ModelProperty = Expression.Property(incTree.ModelProperty, ((PropertyInfo)incTree.ModelProperty.Member).PropertyType.GetProperty(propertyName))
};
incTree.Includes.Add(propertyName, child);
}
incTree = child;
}
}
}
return dic.Values.ToArray();
}
public Expression CreateInitExpression()
{
List bindings = new List();
var modelType = ((PropertyInfo)ModelProperty.Member).PropertyType;
var responseType = ResponseProperty.PropertyType;
var props = responseType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.CanWrite);
foreach (var p in props)
{
IncludeTree include;
Expression bind = null;
if (!Includes.TryGetValue(p.Name, out include))
{
NavigationPropertyAttribute navAttr = p.GetCustomAttribute();
if (navAttr != null)
{
var navModelProp = Expression.Property(ModelProperty, modelType.GetProperty(navAttr.NavigationProperty));
var navProp = Expression.Property(navModelProp, ((PropertyInfo)navModelProp.Member).PropertyType.GetProperty(navAttr.Property));
bind = navProp;
}
else if (IsMappeable(p))
{
bind = Expression.Property(ModelProperty, modelType.GetProperty(p.Name));
}
}
else
{
bind = include.CreateInitExpression();
}
if (bind != null)
{
bindings.Add(Expression.Bind(p, bind));
}
}
NewExpression newResponse = Expression.New(responseType);
MemberInitExpression initExpression = Expression.MemberInit(newResponse, bindings);
return initExpression;
}
}
}
public clast ExpressionBuilder : ExpressionBuilder
{
public ExpressionBuilder(string parameter = "x", MemberExpression referenceExpression = null)
: base(parameter, referenceExpression)
{
ResponseType = typeof(TResponse);
}
public ExpressionBuilder(Expression expression)
: base(expression)
{
ResponseType = typeof(TResponse);
}
}
public static clast ExpressionBuilder
{
clast ParamExpresionVisitor : ExpressionVisitor
{
public Expression ReplaceParameter;
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == ReplaceParameter.Type && node != ReplaceParameter)
return ReplaceParameter;
return node;
}
}
public static IQueryable Where(this IQueryable querable, string expression)
{
if (expression == null)
return querable;
ExpressionBuilder builder = new ExpressionBuilder();
return querable.Where(builder.Where(expression));
}
public static IQueryable OrderBy(this IQueryable querable, string expression)
{
if (expression == null)
return querable;
ExpressionBuilder builder = new ExpressionBuilder();
return builder.OrderBy(querable, expression);
}
public static IQueryable Select(this IQueryable querable, Expression expression = null, string include = null)
{
ExpressionBuilder builder = new ExpressionBuilder(expression);
return querable.Select(builder.Select(include));
}
public static IQueryable Where(this IQueryable querable, IExpressionBuilder builder, string expression)
{
return querable.Where(builder.Where(expression));
}
public static IQueryable OrderBy(this IQueryable querable, IExpressionBuilder builder, string expression)
{
return builder.OrderBy(querable, expression);
}
public static IQueryable Select(this IQueryable querable, IExpressionBuilder builder, Expression expression = null, string include = null)
{
return querable.Select(builder.Select(include));
}
public static IQueryable Query(this IQueryable querable, IExpressionBuilder builder, string filter = null, string orderby = null, int skip = -1, int take = -1, string include = null)
{
return builder.Query(querable, filter, orderby, skip, take, include);
}
public static IQueryable Query(this IQueryable querable, IExpressionBuilder builder, string filter = null, string orderby = null, int skip = -1, int take = -1)
{
return builder.Query(querable, filter, orderby, skip, take);
}
public static IQueryable Query(this IQueryable querable, string filter = null, string orderby = null, int skip = -1, int take = -1)
{
ExpressionBuilder builder = new ExpressionBuilder();
return builder.Query(querable, filter, orderby, skip, take);
}
public static IQueryable Where(this IQueryable querable, IExpressionBuilder builder, string expression)
{
if (expression == null)
return querable;
return querable.Where(builder.Where(expression));
}
public static Expression Combine(Expression exp1, Expression exp2, bool or)
{
if (exp1 == null && exp2 == null) return null;
else if (exp1 == null) return exp2;
else if (exp2 == null) return exp1;
var body1 = exp1.Body;
var body2 = exp2.Body;
var parameter = exp1.Parameters[0];
Expression exp = or ? Expression.OrElse(exp1, exp2) : Expression.AndAlso(exp1, exp2);
ParamExpresionVisitor visitor = new ParamExpresionVisitor { ReplaceParameter = parameter };
exp = visitor.Visit(exp);
return Expression.Lambda(exp, parameter);
}
public static Expression Combine(Expression targetExp, Expression expression)
{
ParamExpresionVisitor visitor = new ParamExpresionVisitor { ReplaceParameter = targetExp.Body };
var exp = visitor.Visit(expression.Body);
Expression current = targetExp.Body;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
return Expression.Lambda(exp, (ParameterExpression)current);
}
public static Expression Combine(Expression targetExp, Expression expression)
{
ParamExpresionVisitor visitor = new ParamExpresionVisitor { ReplaceParameter = targetExp.Body };
var exp = visitor.Visit(expression.Body);
Expression current = targetExp.Body;
while (current != null && !(current is ParameterExpression))
{
current = ((MemberExpression)current).Expression;
}
return Expression.Lambda(exp, (ParameterExpression)current);
}
}
}