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