public override KeyValuePair <string, Expression>?GetExpression <TSource>(Type sourceType, ParameterExpression rootParameter, IEnumerable <TSource> source)
        {
            // 1. Parse the instruction.
            var fieldNames   = Parameter.Split(',');
            var instructions = fieldNames.Select(s =>
            {
                return(InstructionHelper.ExtractInstruction(s));
            }).Where(s => s.HasValue);

            if (instructions.Count() == 0)
            {
                throw new ArgumentException("the parameter cannot be empty");
            }

            if (instructions.Any(i => !_orderByInstructions.Contains(i.Value.Key)))
            {
                throw new ArgumentException("At least one instruction is not supported");
            }

            var onInstruction    = instructions.FirstOrDefault(i => i.Value.Key == _onInstruction);
            var orderInstruction = instructions.FirstOrDefault(i => i.Value.Key == _orderInstruction);

            if (onInstruction == null)
            {
                throw new ArgumentException("The 'on' instruction must be specified");
            }

            var fnName = "OrderBy";

            if (orderInstruction != null && orderInstruction.Value.Value == "desc")
            {
                fnName = "OrderByDescending";
            }

            // 2. Construct the selector.
            ParameterExpression arg      = Expression.Parameter(sourceType, "x");
            MemberExpression    property = Expression.Property(arg, onInstruction.Value.Value);
            var selector = Expression.Lambda(property, new ParameterExpression[] { arg });

            // 3. Call the order function and return the result.
            var enumarableType = typeof(Queryable);
            var method         = enumarableType.GetMethods()
                                 .Where(m => m.Name == fnName && m.IsGenericMethodDefinition)
                                 .Where(m =>
            {
                var parameters = m.GetParameters().ToList();
                return(parameters.Count == 2);
            }).Single().MakeGenericMethod(sourceType, property.Type);

            if (SubInstruction != null)
            {
                var subExpr = SubInstruction.GetExpression(sourceType, rootParameter, source);
                var call    = Expression.Call(method, subExpr.Value.Value, selector);
                return(new KeyValuePair <string, Expression>(Name, call));
            }

            return(new KeyValuePair <string, Expression>(Name, Expression.Call(method, rootParameter, selector)));
        }
        public override KeyValuePair <string, Expression>?GetExpression <TSource>(Type outerSourceType, ParameterExpression rootParameter, IEnumerable <TSource> source)
        {
            if (outerSourceType == null)
            {
                throw new ArgumentNullException(nameof(outerSourceType));
            }

            // 1. Split the value & extract the field names or requests.
            var splitted     = Parameter.Split(',');
            var instructions = splitted.Select(s => InstructionHelper.ExtractInstruction(s)).Where(s => s.HasValue);

            if (!parameters.Any(p => instructions.Any(i => i.Value.Key == p)))
            {
                throw new ArgumentException("either inner or outer parameter is not specified");
            }

            // 2. Construct the expressions.
            var outer       = instructions.First(i => i.Value.Key == outerKey).Value.Value;
            var inner       = instructions.First(i => i.Value.Key == innerKey).Value.Value;
            var select      = instructions.FirstOrDefault(i => i.Value.Key == selectKey);
            var selectValue = string.Empty;

            if (select != null)
            {
                selectValue = select.Value.Value;
            }

            Type       innerSourceType  = outerSourceType;
            Expression targetExpression = null;

            if (TargetInstruction != null)
            {
                var targetArg = Expression.Parameter(typeof(IQueryable <TSource>), "t");
                targetExpression = TargetInstruction.GetExpression(outerSourceType, targetArg, source).Value.Value;
                innerSourceType  = targetExpression.Type.GetGenericArguments().First();
            }

            Type             tResult     = innerSourceType;
            var              fieldNames  = outer.GetParameters();
            var              outerArg    = Expression.Parameter(outerSourceType, "x");
            var              innerArg    = Expression.Parameter(innerSourceType, "y");
            LambdaExpression outerLambda = null,
                             innerLambda = null;
            Type resultType;

            if (fieldNames.Count() == 1)
            {
                resultType = outerSourceType.GetProperty(fieldNames.First()).PropertyType;
                var outerProperty = Expression.Property(outerArg, fieldNames.First());
                var innerProperty = Expression.Property(innerArg, fieldNames.First());
                outerLambda = Expression.Lambda(outerProperty, new ParameterExpression[] { outerArg });
                innerLambda = Expression.Lambda(innerProperty, new ParameterExpression[] { innerArg });
            }
            else
            {
                var commonAnonType = ReflectionHelper.CreateNewAnonymousType(outerSourceType, fieldNames);
                resultType = commonAnonType.AsType();
                var outerNewExpr = ExpressionBuilder.BuildNew(outer, outerSourceType, commonAnonType, outerArg);
                var innerNewExpr = ExpressionBuilder.BuildNew(inner, innerSourceType, commonAnonType, innerArg);
                outerLambda = Expression.Lambda(outerNewExpr, new ParameterExpression[] { outerArg });
                innerLambda = Expression.Lambda(innerNewExpr, new ParameterExpression[] { innerArg });
            }

            LambdaExpression selectorResult = null;

            if (string.IsNullOrWhiteSpace(selectValue))
            {
                selectorResult = Expression.Lambda(outerArg, new ParameterExpression[] { outerArg, innerArg });
            }
            else
            {
                var selectAttributes = new List <KeyValuePair <string, ICollection <string> > >();
                foreach (var val in selectValue.Split('|'))
                {
                    var values = val.Split('$');
                    if (values.Count() != 2 && values.Count() != 1)
                    {
                        continue;
                    }

                    var key = values.First();
                    var kvp = selectAttributes.FirstOrDefault(s => s.Key == key);
                    if (kvp.IsEmpty())
                    {
                        kvp = new KeyValuePair <string, ICollection <string> >(key, new List <string>());
                        selectAttributes.Add(kvp);
                    }

                    if (values.Count() == 2)
                    {
                        kvp.Value.Add(values.ElementAt(1));
                    }
                }

                if (!selectAttributes.Any(a => selectParameters.Contains(a.Key)))
                {
                    throw new InvalidOperationException("At least one parameter in select is not supported");
                }

                Dictionary <string, Type> mapping = new Dictionary <string, Type>();
                var outerSelect = selectAttributes.FirstOrDefault(a => a.Key == outerKey);
                var innerSelect = selectAttributes.FirstOrDefault(a => a.Key == innerKey);
                AddTypes(mapping, "outer", outerSourceType, outerSelect);
                AddTypes(mapping, "inner", innerSourceType, innerSelect);
                var anonymousType = ReflectionHelper.CreateNewAnonymousType(mapping);
                var parameters    = new List <Expression>();
                var tmp           = GetParameterExpressions(outerArg, outerSelect);
                if (tmp != null)
                {
                    parameters.AddRange(tmp);
                }

                tmp = GetParameterExpressions(innerArg, innerSelect);
                if (tmp != null)
                {
                    parameters.AddRange(tmp);
                }

                var newExpr = Expression.New(anonymousType.DeclaredConstructors.First(), parameters);
                selectorResult = Expression.Lambda(newExpr, new ParameterExpression[] { innerArg, outerArg });
                tResult        = anonymousType.AsType();
            }

            var enumarableType        = typeof(Queryable);
            var method                = enumarableType.GetMethods().Where(m => m.Name == "Join" && m.IsGenericMethodDefinition).Where(m => m.GetParameters().ToList().Count == 5).First();
            var genericMethod         = method.MakeGenericMethod(outerSourceType, innerSourceType, resultType, tResult);
            MethodCallExpression call = null;

            if (targetExpression == null)
            {
                call = Expression.Call(genericMethod, Expression.Constant(source), Expression.Constant(source), outerLambda, innerLambda, selectorResult);
            }
            else
            {
                call = Expression.Call(genericMethod, Expression.Constant(source), targetExpression, Expression.Constant(outerLambda), Expression.Constant(innerLambda), selectorResult);
            }

            return(new KeyValuePair <string, Expression>(Name, call));
        }
        public override KeyValuePair <string, Expression>?GetExpression <TSource>(Type sourceType, ParameterExpression rootParameter, IEnumerable <TSource> source)
        {
            // Callback used to generate the group by expression.
            Func <IEnumerable <string>, Type, Type, Expression, MethodCallExpression> getGroupByInst = (names, eType, qType, groupByArg) =>
            {
                var method = qType.GetMethods()
                             .Where(m => m.Name == "GroupBy" && m.IsGenericMethodDefinition)
                             .Where(m => m.GetParameters().ToList().Count == 2).First();
                ParameterExpression arg           = Expression.Parameter(eType, "x");
                MethodInfo          genericMethod = null;
                LambdaExpression    selector      = null;
                if (names.Count() == 1)
                {
                    var fieldName                = names.First();
                    var propertyInfo             = eType.GetProperty(fieldName);
                    MemberExpression keyProperty = Expression.Property(arg, fieldName);
                    var keySelector              = Expression.Lambda(keyProperty, new ParameterExpression[] { arg });
                    genericMethod = method.MakeGenericMethod(eType, propertyInfo.PropertyType);
                    selector      = Expression.Lambda(keyProperty, new ParameterExpression[] { arg });
                }
                else
                {
                    var sourceTypes = names.Select(n => sourceType.GetProperty(n));
                    var expressions = sourceTypes.Select(s => Expression.Property(arg, s));
                    var anonType    = ReflectionHelper.CreateNewAnonymousType(sourceType, names);
                    var newExpr     = Expression.New(anonType.DeclaredConstructors.First(), expressions);
                    selector      = Expression.Lambda(newExpr, arg);
                    genericMethod = method.MakeGenericMethod(eType, anonType.AsType());
                }

                return(Expression.Call(genericMethod, groupByArg, selector));
            };

            if (sourceType == null)
            {
                throw new ArgumentNullException(nameof(sourceType));
            }

            Type queryableType  = typeof(Queryable),
                 enumerableType = typeof(Enumerable);

            // 1. Split the value & extract the field names or requests.
            var splitted     = Parameter.Split(',');
            var instructions = splitted.Select(s =>
            {
                return(InstructionHelper.ExtractInstruction(s));
            }).Where(s => s.HasValue);

            if (instructions.Count() == 0)
            {
                return(null);
            }
            if (instructions.Count() != splitted.Count())
            {
                throw new ArgumentException("At least one parameter is not an instruction : <operation>(<parameter>)");
            }

            if (instructions.Any(i => !_groupByInstructions.Contains(i.Value.Key)))
            {
                throw new ArgumentException("At least one instruction is not supported");
            }

            var onInstruction = instructions.FirstOrDefault(i => i.Value.Key == _onInstruction);

            if (onInstruction == null)
            {
                throw new ArgumentException("The 'on' instruction must be specified");
            }

            // r
            var        sourceQueryableType   = typeof(IQueryable <>).MakeGenericType(sourceType);
            var        sourceEnumerableType  = typeof(IEnumerable <>).MakeGenericType(sourceType);
            var        orderedEnumerableType = typeof(IOrderedEnumerable <>).MakeGenericType(sourceType);
            Expression finalSelectArg        = null;

            if (SubInstruction == null)
            {
                finalSelectArg = Expression.Constant(source);
            }
            else
            {
                finalSelectArg = Expression.Parameter(sourceQueryableType, "r");
            }

            var fieldNames  = onInstruction.Value.Value.Split(_fieldSeparator);
            var groupByInst = getGroupByInst(fieldNames, sourceType, queryableType, finalSelectArg);
            MethodCallExpression instruction = null;
            var aggregateInstruction         = instructions.FirstOrDefault(i => i.Value.Key == _aggregateInstruction);

            if (aggregateInstruction != null)
            {
                var aggregateInstructionVal = InstructionHelper.ExtractAggregateInstruction(aggregateInstruction.Value.Value);
                if (aggregateInstructionVal == null)
                {
                    throw new ArgumentException("the aggregate instruction is not correct");
                }

                var kvp    = aggregateInstructionVal.Value;
                var method = queryableType.GetMethods()
                             .Where(m => m.Name == "Select" && m.IsGenericMethodDefinition)
                             .Where(m => m.GetParameters().ToList().Count == 2).First()
                             .MakeGenericMethod(new Type[] { sourceQueryableType, sourceQueryableType });

                var propertyName = kvp.Value;
                var propertyInfo = sourceType.GetProperty(propertyName);
                // o
                var orderArg = Expression.Parameter(sourceType, "o");
                // s
                var selectArg = Expression.Parameter(sourceEnumerableType, "s");
                // b
                var selectFirstArg = Expression.Parameter(sourceEnumerableType, "b");
                // o => o.[Value]
                var keyProperty     = Expression.Property(orderArg, propertyName);
                var orderBySelector = Expression.Lambda(keyProperty, new ParameterExpression[] { orderArg });
                // s => s.OrderBy(o => o.[Value])
                MethodCallExpression orderByCall = null;
                if (string.Equals(kvp.Key, "max", StringComparison.CurrentCultureIgnoreCase))
                {
                    orderByCall = Expression.Call(enumerableType, "OrderByDescending", new[] { sourceType, propertyInfo.PropertyType }, selectArg, orderBySelector);
                }
                else
                {
                    orderByCall = Expression.Call(enumerableType, "OrderBy", new[] { sourceType, propertyInfo.PropertyType }, selectArg, orderBySelector);
                }

                var selectBody       = Expression.Lambda(orderByCall, new ParameterExpression[] { selectArg });
                var quotedSelectBody = Expression.Quote(selectBody);
                // z.GroupBy(x => x.FirstName).Select(s => s.OrderBy(o => o.BirthDate))
                var selectRequest = Expression.Call(queryableType, "Select", new[] { sourceEnumerableType, orderedEnumerableType }, groupByInst, quotedSelectBody);
                // b => b.First()
                var selectFirstRequest = Expression.Call(enumerableType, "First", new[] { sourceType }, selectFirstArg);
                var selectFirstLambda  = Expression.Lambda(selectFirstRequest, new ParameterExpression[] { selectFirstArg });
                instruction = Expression.Call(queryableType, "Select", new[] { sourceEnumerableType, sourceType }, selectRequest, selectFirstLambda);
            }
            else
            {
                instruction = groupByInst;
            }

            return(new KeyValuePair <string, Expression>(Name, instruction));
        }