示例#1
0
        private MethodInfo GetSortMethod(
            SortingDirection sortDirection,
            Type propertyType,
            ref bool preOrdered,
            bool caseSensitive)
        {
            string methodName = sortDirection == SortingDirection.Asc
            ? preOrdered
                ? nameof(Enumerable.ThenBy)
                : nameof(Enumerable.OrderBy)
            : preOrdered
                ? nameof(Enumerable.ThenByDescending)
                : nameof(Enumerable.OrderByDescending);

            preOrdered = true;
            return(LINQUtils.BuildLINQExtensionMethod(
                       methodName,
                       numberOfParameters: !_usesCaseInsensitiveSource &&
                       caseSensitive &&
                       propertyType == typeof(string)
                    ? CASE_SENSITIVE_ORDER_FUNCTION_PARAM_COUNT
                    : CASE_INSENSITIVE_ORDER_FUNCTION_PARAM_COUNT,
                       genericElementTypes: new[] { _setElementType, propertyType }));
        }
        /// <summary>
        /// Applies the given DynamicQueryOptions to the generic IEnumerable instace.
        /// </summary>
        /// <param name="currentSet">Existing IEnumerable instance.</param>
        /// <param name="dynamicQueryOptions">Query options to apply.</param>
        /// <returns>DynamicQueryOptions applied IEnumerable instance,</returns>
        public static IQueryable ApplyFilters(this IQueryable currentSet, DynamicQueryOptions dynamicQueryOptions)
        {
            try
            {
                if (dynamicQueryOptions == null || currentSet == null)
                {
                    return(currentSet);
                }

                Expression exp = null;

                // Create the query parameter
                ParameterExpression param = Expression.Parameter(currentSet.ElementType, currentSet.ElementType.Name.ToLower());
                if (dynamicQueryOptions.Filters != null && dynamicQueryOptions.Filters.Count > 0)
                {
                    // Copy the array since we need to mutate it, we should avoid mutating the real list.
                    List <Filter> dqbFilters = dynamicQueryOptions.Filters.ToList();

                    // Since the expression is null at this point, we should create it with our first filter.
                    exp = BuildFilterExpression(param, dqbFilters.FirstOrDefault(), dynamicQueryOptions.UsesCaseInsensitiveSource);
                    dqbFilters.RemoveAt(0); // Remove the first since it was added already.

                    // Append the rest
                    foreach (Filter item in dqbFilters)
                    {
                        exp = Expression.AndAlso(exp, BuildFilterExpression(param, item, dynamicQueryOptions.UsesCaseInsensitiveSource));
                    }
                }

                if (dynamicQueryOptions.SortOptions != null && dynamicQueryOptions.SortOptions.Count > 0)
                {
                    List <OrderOptionDetails> orderLambdas = new List <OrderOptionDetails>();
                    foreach (SortOption so in dynamicQueryOptions.SortOptions)
                    {
                        Expression paramExpr = ExtractMember(param, so.PropertyName);
                        orderLambdas.Add(new OrderOptionDetails
                        {
                            Direction           = so.SortingDirection,
                            Expression          = Expression.Lambda(paramExpr, param),
                            ParameterExpression = paramExpr,
                            CaseSensitive       = so.CaseSensitive
                        });
                    }

                    var orderVisitor = new OrderFunctionVisitor(
                        currentSet.Expression,
                        orderLambdas,
                        currentSet.ElementType,
                        dynamicQueryOptions.UsesCaseInsensitiveSource,
                        dynamicQueryOptions.IgnorePredefinedOrders);

                    currentSet = currentSet.Provider.CreateQuery(orderVisitor.ApplyOrders());
                }

                if (exp != null)
                {
                    MethodCallExpression whereFilter = Expression.Call(
                        LINQUtils.BuildLINQExtensionMethod(
                            nameof(Enumerable.Where),
                            genericElementTypes: new[] { currentSet.ElementType },
                            enumerableType: typeof(Queryable)),
                        currentSet.Expression,
                        Expression.Quote(Expression.Lambda(exp, param)));

                    currentSet = currentSet.Provider.CreateQuery(whereFilter);
                }

                if (dynamicQueryOptions.PaginationOption != null)
                {
                    if (dynamicQueryOptions.PaginationOption.AssignDataSetCount)
                    {
                        dynamicQueryOptions.PaginationOption.DataSetCount = (int)_countFunction.Invoke(null, new[] { currentSet });
                    }

                    if (dynamicQueryOptions.PaginationOption.Offset > 0)
                    {
                        MethodCallExpression skip = Expression.Call(
                            _skipFunction,
                            currentSet.Expression,
                            Expression.Constant(dynamicQueryOptions.PaginationOption.Offset));

                        currentSet = currentSet.Provider.CreateQuery(skip);
                    }

                    if (dynamicQueryOptions.PaginationOption.Count > 0)
                    {
                        MethodCallExpression take = Expression.Call(
                            _takeFunction,
                            currentSet.Expression,
                            Expression.Constant(dynamicQueryOptions.PaginationOption.Count));

                        currentSet = currentSet.Provider.CreateQuery(take);
                    }
                }

                return(currentSet);
            }
            catch (Exception ex)
            {
                throw new DynamicQueryException("DynamicQueryBuilder has encountered an unhandled exception", string.Empty, ex);
            }
        }
        /// <summary>
        /// Builds a runtime generic dynamic query with the given filters.
        /// </summary>
        /// <param name="param">Created parameter instance or current expression body.</param>
        /// <param name="filter">Filter instance to build.</param>
        /// <param name="usesCaseInsensitiveSource">Flag to detect if the query is going to run on a SQL database.</param>
        /// <returns>Built query expression.</returns>
        internal static Expression BuildFilterExpression(ParameterExpression param, Filter filter, bool usesCaseInsensitiveSource = false)
        {
            Expression parentMember = ExtractMember(param, filter.PropertyName);

            if (parentMember.Type == typeof(string) &&
                filter.CaseSensitive &&
                !usesCaseInsensitiveSource)
            {
                parentMember = Expression.Call(parentMember, _toLowerInvariantMethod);
            }

            string stringFilterValue = filter.Value?.ToString();

            // We are handling In operations seperately which are basically a list of OR=EQUALS operation. We recursively handle this operation.
            if (filter.Operator == FilterOperation.In)
            {
                if (filter.Value == null)
                {
                    throw new DynamicQueryException("You can't pass type null to In. Pass null as a string instead.");
                }

                // Split all data into a list
                List <string> splittedValues = stringFilterValue.Split(PARAMETER_OPTION_DELIMITER).ToList();
                var           equalsFilter   = new Filter
                {
                    Operator      = FilterOperation.Equals,
                    PropertyName  = filter.PropertyName,
                    Value         = splittedValues.First().ToLowerInvariant(),
                    CaseSensitive = filter.CaseSensitive
                };

                // Create the expression with the first value.
                Expression builtInExpression = BuildFilterExpression(param, equalsFilter, usesCaseInsensitiveSource);
                splittedValues.RemoveAt(0); // Remove the first value

                // Create query for every splitted value and append them.
                foreach (var item in splittedValues)
                {
                    equalsFilter.Value = item;
                    builtInExpression  = Expression.Or(builtInExpression, BuildFilterExpression(param, equalsFilter, usesCaseInsensitiveSource));
                }

                return(builtInExpression);
            }

            // We should convert the data into its own type before we do any query building.
            object convertedValue = null;

            if (filter.Operator < FilterOperation.Any)
            {
                convertedValue = stringFilterValue != "null"
                    ? TypeDescriptor.GetConverter(parentMember.Type).ConvertFromInvariantString(
                    usesCaseInsensitiveSource
                       ? stringFilterValue
                        : filter.CaseSensitive
                            ? stringFilterValue?.ToLowerInvariant()
                            : stringFilterValue)
                    : null;
            }

            ConstantExpression constant = Expression.Constant(convertedValue);

            switch (filter.Operator)
            {
            case FilterOperation.Equals:
                return(Expression.Equal(parentMember, constant));

            case FilterOperation.NotEqual:
                return(Expression.NotEqual(parentMember, constant));

            case FilterOperation.Contains:
                return(Expression.Call(parentMember, _stringContainsMethod, constant));

            case FilterOperation.GreaterThan:
                return(Expression.GreaterThan(parentMember, constant));

            case FilterOperation.GreaterThanOrEqual:
                return(Expression.GreaterThanOrEqual(parentMember, constant));

            case FilterOperation.LessThan:
                return(Expression.LessThan(parentMember, constant));

            case FilterOperation.LessThanOrEqual:
                return(Expression.LessThanOrEqual(parentMember, constant));

            case FilterOperation.StartsWith:
                return(Expression.Call(parentMember, _stringStartsWithMethod, constant));

            case FilterOperation.EndsWith:
                return(Expression.Call(parentMember, _stringEndsWithMethod, constant));

            case FilterOperation.Any:
            case FilterOperation.All:
                ParameterExpression memberParam = Expression.Parameter(
                    parentMember.Type.GenericTypeArguments[0],
                    parentMember.Type.GenericTypeArguments[0].Name);

                MethodInfo requestedFunction = LINQUtils.BuildLINQExtensionMethod(
                    filter.Operator.ToString(),
                    genericElementTypes: new[] { memberParam.Type },
                    enumerableType: typeof(Enumerable));

                Expression builtMemberExpression = BuildFilterExpression(memberParam, (filter.Value as DynamicQueryOptions).Filters.First(), usesCaseInsensitiveSource);

                return(Expression.Call(
                           requestedFunction,
                           Expression.PropertyOrField(param, filter.PropertyName),
                           Expression.Lambda(builtMemberExpression, memberParam)));

            default:
                return(null);
            }
        }
        /// <summary>
        /// Applies the given DynamicQueryOptions to the generic IEnumerable instace.
        /// </summary>
        /// <param name="currentSet">Existing IEnumerable instance.</param>
        /// <param name="dynamicQueryOptions">Query options to apply.</param>
        /// <returns>DynamicQueryOptions applied IEnumerable instance,</returns>
        public static IQueryable ApplyFilters(this IQueryable currentSet, DynamicQueryOptions dynamicQueryOptions)
        {
            try
            {
                if (dynamicQueryOptions == null || currentSet == null)
                {
                    return(currentSet);
                }

                Expression exp = null;

                // Create the query parameter (x =>)
                ParameterExpression param = Expression.Parameter(currentSet.ElementType, currentSet.ElementType.Name.ToLower());

                // Check if we have any filters
                if (dynamicQueryOptions.Filters != null && dynamicQueryOptions.Filters.Count > 0)
                {
                    // Lets build the first expression and then iterate the rest and append them to this one
                    exp = BuildFilterExpression(param,
                                                dynamicQueryOptions.Filters.First(),
                                                dynamicQueryOptions.UsesCaseInsensitiveSource);

                    // We start to iterate with the second element here because we have just built the first expression up above
                    for (int i = 1; i < dynamicQueryOptions.Filters.Count; ++i)
                    {
                        // Build the current expression
                        Expression builtExpression = BuildFilterExpression(param,
                                                                           dynamicQueryOptions.Filters[i],
                                                                           dynamicQueryOptions.UsesCaseInsensitiveSource);

                        // Get the previous filter to retrieve the logical operator between the current and the next filter
                        Filter previousFilter = dynamicQueryOptions.Filters.ElementAtOrDefault(i - 1);

                        exp = LINQUtils.BuildLINQLogicalOperatorExpression(previousFilter, exp, builtExpression);
                    }
                }

                if (dynamicQueryOptions.SortOptions != null && dynamicQueryOptions.SortOptions.Count > 0)
                {
                    var orderLambdas = new List <OrderOptionDetails>();
                    foreach (SortOption so in dynamicQueryOptions.SortOptions)
                    {
                        Expression paramExpr = ExtractMember(param, so.PropertyName, false);
                        orderLambdas.Add(new OrderOptionDetails
                        {
                            Direction           = so.SortingDirection,
                            Expression          = Expression.Lambda(paramExpr, param),
                            ParameterExpression = paramExpr,
                            CaseSensitive       = so.CaseSensitive
                        });
                    }

                    var orderVisitor = new OrderFunctionVisitor(
                        currentSet.Expression,
                        orderLambdas,
                        currentSet.ElementType,
                        dynamicQueryOptions.UsesCaseInsensitiveSource,
                        dynamicQueryOptions.IgnorePredefinedOrders);

                    currentSet = currentSet.Provider.CreateQuery(orderVisitor.ApplyOrders());
                }

                if (exp != null)
                {
                    MethodCallExpression whereFilter = Expression.Call(
                        LINQUtils.BuildLINQExtensionMethod(
                            nameof(Enumerable.Where),
                            genericElementTypes: new[] { currentSet.ElementType },
                            enumerableType: typeof(Queryable)),
                        currentSet.Expression,
                        Expression.Quote(Expression.Lambda(exp, param)));

                    currentSet = currentSet.Provider.CreateQuery(whereFilter);
                }

                if (dynamicQueryOptions.PaginationOption != null)
                {
                    if (dynamicQueryOptions.PaginationOption.AssignDataSetCount)
                    {
                        dynamicQueryOptions.PaginationOption.DataSetCount = (int)ExtensionMethods.CountFunction.Invoke(null, new[] { currentSet });
                    }

                    if (dynamicQueryOptions.PaginationOption.Offset > 0)
                    {
                        MethodCallExpression skip = Expression.Call(
                            ExtensionMethods.SkipFunction,
                            currentSet.Expression,
                            Expression.Constant(dynamicQueryOptions.PaginationOption.Offset));

                        currentSet = currentSet.Provider.CreateQuery(skip);
                    }

                    if (dynamicQueryOptions.PaginationOption.Count > 0)
                    {
                        MethodCallExpression take = Expression.Call(
                            ExtensionMethods.TakeFunction,
                            currentSet.Expression,
                            Expression.Constant(dynamicQueryOptions.PaginationOption.Count));

                        currentSet = currentSet.Provider.CreateQuery(take);
                    }
                }

                return(currentSet);
            }
            catch (Exception ex)
            {
                throw new DynamicQueryException("DynamicQueryBuilder has encountered an unhandled exception", string.Empty, ex);
            }
        }