csharp/ansel86castro/cybtans-sdk/CybtansSDK/Cybtans.Expressions/ExpressionBuilder.cs

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);
        }
    }
}