private IQueryable <T> ApplySort(IQueryable <T> queryable, AdvancedQueryRequest request)
        {
            var sorted    = false;
            var paramExpr = Expression.Parameter(_type, "val");

            foreach (var param in request.Sorted)
            {
                if (!_propertyMap.ContainsKey(param.Id.ToLower()) || !_propertyMap[param.Id.ToLower()].Orderable)
                {
                    continue;
                }

                var sortProperty = _propertyMap[param.Id.ToLower()].Property;
                var expression1  = Expression.Property(paramExpr, sortProperty);
                var propType     = sortProperty.PropertyType;
                var delegateType = Expression.GetFuncType(_type, propType);
                var propertyExpr = Expression.Lambda(delegateType, expression1, paramExpr);

                string methodName;
                if (!param.Desc)
                {
                    methodName = sorted ? "ThenBy" : "OrderBy";
                }
                else
                {
                    methodName = sorted ? "ThenByDescending" : "OrderByDescending";
                }

                queryable = typeof(Queryable).GetMethods().Single(
                    method => method.Name == methodName &&
                    method.IsGenericMethodDefinition &&
                    method.GetGenericArguments().Length == 2 &&
                    method.GetParameters().Length == 2)
                            .MakeGenericMethod(_type, propType)
                            .Invoke(null, new object[] { queryable, propertyExpr }) as IOrderedQueryable <T>;

                sorted = true;
            }

            //Linq to entities needs a sort to implement skip
            if (!sorted)
            {
                var firstProp    = Expression.Property(paramExpr, _propertyMap.First().Value.Property);
                var propType     = _propertyMap.First().Value.Property.PropertyType;
                var delegateType = Expression.GetFuncType(_type, propType);
                var propertyExpr = Expression.Lambda(delegateType, firstProp, paramExpr);

                queryable = typeof(Queryable).GetMethods().Single(method =>
                                                                  method.Name == "OrderBy" &&
                                                                  method.IsGenericMethodDefinition &&
                                                                  method.GetGenericArguments().Length == 2 &&
                                                                  method.GetParameters().Length == 2)
                            .MakeGenericMethod(_type, propType)
                            .Invoke(null, new object[] { queryable, propertyExpr }) as IOrderedQueryable <T>;
            }

            return(queryable);
        }
        private Expression <Func <T, bool> > GenerateEntityFilter(AdvancedQueryRequest request)
        {
            var paramExpression = Expression.Parameter(_type, "val");
            List <MethodCallExpression> searchProps = new List <MethodCallExpression>();
            var modifier = new ModifyParam(paramExpression);

            foreach (var filtered in request.Filtered)
            {
                if (!_propertyMap.ContainsKey(filtered.Id.ToLower()) || !_propertyMap[filtered.Id.ToLower()].Orderable)
                {
                    continue;
                }

                var propMap = _propertyMap[filtered.Id.ToLower()];
                var prop    = propMap.Property;

                var isString  = prop.PropertyType == typeof(string);
                var hasCustom = _converters.ContainsKey(prop.Name);

                if ((!prop.CanWrite || !propMap.Searchable || (_convertable.All(t => t != prop.PropertyType) && !isString)) && !hasCustom)
                {
                    continue;
                }

                Expression propExp = Expression.Property(paramExpression, prop);

                if (hasCustom)
                {
                    propExp = modifier.Visit(_converters[prop.Name]);
                }
                else if (!isString)
                {
                    var toString = prop.PropertyType.GetMethod("ToString", Type.EmptyTypes);

                    propExp = Expression.Call(propExp, toString);
                }

                var toLower          = Expression.Call(propExp, typeof(string).GetMethod("ToLower", Type.EmptyTypes));
                var searchExpression = Expression.Constant(filtered.Value.ToLower());
                searchProps.Add(Expression.Call(toLower, typeof(string).GetMethod("Contains"), searchExpression));
            }

            var propertyQuery = searchProps.ToArray();

            if (propertyQuery.Length == 0)
            {
                return(x => true);
            }

            Expression compoundExpression = propertyQuery[0];

            for (int i = 1; i < propertyQuery.Length; i++)
            {
                compoundExpression = Expression.Or(compoundExpression, propertyQuery[i]);
            }

            return(Expression.Lambda <Func <T, bool> >(compoundExpression, paramExpression));
        }
 public static Task <AdvancedQueryPage <T> > PageAsync <T>(this IQueryable <T> query, AdvancedQueryRequest request) where T : class
 {
     return(new AdvancedQueryParser <T>().ParseAsync(query, request));
 }
        public async Task <AdvancedQueryPage <T> > ParseAsync(IQueryable <T> queryable, AdvancedQueryRequest request)
        {
            var page = new AdvancedQueryPage <T>();

            var filteredQuery = ApplySort(queryable, request)
                                .Where(GenerateEntityFilter(request));

            var count = await filteredQuery.CountAsync();

            if (request.PageSize == 0)
            {
                page.PageCount = 1;
                page.Data      = filteredQuery.ToList();
                return(page);
            }

            page.PageCount = (int)Math.Ceiling((double)count / request.PageSize);

            page.Data = filteredQuery
                        .Skip(request.PageSize * request.Page)
                        .Take(request.PageSize)
                        .ToList();

            return(page);
        }