public override void ProcessMethodCall(IOqlSyntaxContext callContext, MethodCallExpression methodCall)
        {
            if (methodCall.IsCalled(Skip))
            {
                callContext.CallResult.Offset = (int)methodCall.GetArgument(1).GetValue();
                return;
            }

            if (methodCall.IsCalled(Take))
            {
                callContext.CallResult.Size = (int)methodCall.GetArgument(1).GetValue();
                return;
            }

            if (methodCall.IsCalled(ElementAt))
            {
                callContext.CallResult.Command      = OqlCommandToken.ElementAt;
                callContext.CallResult.ElementIndex = (int)methodCall.GetArgument(1).GetValue();
                return;
            }

            if (methodCall.IsCalled(ElementAtOrDefault))
            {
                callContext.CallResult.ElementIndex = (int)methodCall.GetArgument(1).GetValue();
                callContext.CallResult.Command      = OqlCommandToken.ElementAt | OqlCommandToken.DefaultFlag;
                return;
            }

            ProcessNavigate(callContext, methodCall);
        }
        public override void ProcessMethodCall(IOqlSyntaxContext callContext, MethodCallExpression methodCall)
        {
            if (methodCall.IsCalled(SkipWhile))
            {
                AndAlso(Expression.Not(methodCall.GetArgument(1)));
                return;
            }

            AndAlso(methodCall.GetArgument(1));

            OqlNavigationClause.ProcessNavigate(callContext, methodCall);
        }
Exemple #3
0
        private static void VerifyGetArguments(MethodCallExpression call)
        {
            var args = call.Arguments;

            Assert.Equal(args.Count, call.ArgumentCount);
            AssertExtensions.Throws <ArgumentOutOfRangeException>("index", () => call.GetArgument(-1));
            AssertExtensions.Throws <ArgumentOutOfRangeException>("index", () => call.GetArgument(args.Count));
            for (int i = 0; i != args.Count; ++i)
            {
                Assert.Same(args[i], call.GetArgument(i));
                Assert.Equal(i, ((ConstantExpression)call.GetArgument(i)).Value);
            }
        }
Exemple #4
0
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.GetArgument("ae") == parameter && (node.Method.Name == "Pattern" || node.Method.Name == "TryPattern"))
            {
                return(new AutomationPatternExpression(node.Method.GetGenericArguments()[0], node.Type));
            }

            return(base.VisitMethodCall(node));
        }
Exemple #5
0
        public override void ProcessMethodCall(IOqlSyntaxContext callContext, MethodCallExpression methodCall)
        {
            if (Distinct.Equals(methodCall))
            {
                m_has_distinct = true;
                return;
            }


            m_select_expression = methodCall.GetArgument(1);


            callContext.CallResult.ResultType = methodCall.Type;


            if (m_select_expression.NodeType == ExpressionType.MemberInit || m_select_expression.NodeType == ExpressionType.New)
            {
                return;
            }

            if (methodCall.IsCalled(Min))
            {
                visit_to_aggregate = (x, y) => callContext.AggregateFormatter.FormatMin(x, y);
                return;
            }

            if (methodCall.IsCalled(Max))
            {
                visit_to_aggregate = (x, y) => callContext.AggregateFormatter.FormatMax(x, y);
                return;
            }


            if (methodCall.IsCalled("Average"))
            {
                visit_to_aggregate = (x, y) => callContext.AggregateFormatter.FormatAverage(x, y);
                return;
            }

            if (methodCall.IsCalled("Sum"))
            {
                visit_to_aggregate = (x, y) => callContext.AggregateFormatter.FormatSum(x, y);
                return;
            }


            if (methodCall.IsCalledOr(Count, LongCount))
            {
                callContext.CallResult.Command = OqlCommandToken.Scalar;
            }

            visit_to_aggregate = null;
        }
Exemple #6
0
        public static void CheckCallFactoryInstanceN()
        {
            const int N = 4;

            ParameterExpression obj = Expression.Parameter(typeof(MS));

            ConstantExpression[] args = Enumerable.Range(0, N).Select(i => Expression.Constant(i)).ToArray();

            MethodCallExpression expr = Expression.Call(obj, typeof(MS).GetMethod("I" + N), args);

            if (!PlatformDetection.IsNetNative) // .Net Native blocks internal framework reflection.
            {
                Assert.Equal("InstanceMethodCallExpressionN", expr.GetType().Name);
            }

            Assert.Same(obj, expr.Object);

            Assert.Equal(N, expr.ArgumentCount);
            for (var i = 0; i < N; i++)
            {
                Assert.Same(args[i], expr.GetArgument(i));
            }

            Collections.ObjectModel.ReadOnlyCollection <Expression> arguments = expr.Arguments;
            Assert.Same(arguments, expr.Arguments);

            Assert.Equal(N, arguments.Count);
            for (var i = 0; i < N; i++)
            {
                Assert.Same(args[i], arguments[i]);
            }

            MethodCallExpression updated = expr.Update(obj, arguments.ToList());

            Assert.Same(expr, updated);

            var visited = (MethodCallExpression) new NopVisitor().Visit(expr);

            Assert.Same(expr, visited);

            var visitedObj = (MethodCallExpression) new VisitorObj().Visit(expr);

            Assert.NotSame(expr, visitedObj);
            Assert.NotSame(obj, visitedObj.Object);
            Assert.Same(arguments, visitedObj.Arguments);

            var visitedArgs = (MethodCallExpression) new VisitorArgs().Visit(expr);

            Assert.NotSame(expr, visitedArgs);
            Assert.Same(obj, visitedArgs.Object);
            Assert.NotSame(arguments, visitedArgs.Arguments);
        }
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            int        start = 0;
            Expression ob    = node.Object;

            if (node.Method.GetCustomAttribute(typeof(ExtensionAttribute)) != null)
            {
                start = 1;
                ob    = node.GetArgument(0);
            }

            if (ob != null)
            {
                Visit(ob);
                Out('.');
            }
            _criteriaBuilder.Clear();
            Out(node.Method.Name);
            Out('(');
            for (int i = start, n = node.ArgumentCount(); i < n; i++)
            {
                if (i > start)
                {
                    Out(", ");
                }
                Visit(node.GetArgument(i));
            }
            Out(')');
            if (string.Equals(node.Method.Name, "Where", StringComparison.InvariantCultureIgnoreCase))
            {
                _queryCriteriaStr.Add(_criteriaBuilder.ToString());
                _queryCriteriaExp.Add(node.GetArgument(1));
            }
            _criteriaBuilder.Clear();
            return(node);
        }
Exemple #8
0
        protected virtual void VisitOrderBy(IOqlExpressionVisitor visitor, MethodCallExpression methodCall)
        {
            visitor.Visit(methodCall.GetArgument(1));


            if (methodCall.IsCalledOr(OrderBy, ThenBy))
            {
                visitor.Query.AppendAsc();
                return;
            }


            if (methodCall.IsCalledOr(OrderByDescending, ThenByDescending))
            {
                visitor.Query.AppendDesc();
            }
        }
Exemple #9
0
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            try
            {
                string methodName = node.Method.Name.ToLower();

                if (methodName.Equals("select"))
                {
                    /*
                     * The following means this,
                     *
                     *      01. Get arguments of select.
                     *      02. Second argument is the expression that is invoked in select.
                     *      03. If it is a new expression, it means it is possibly multiple project expression.
                     *      04. But just in case look at the number of arguments to see if it is actually projecting
                     *          more than one attributes.
                     */
                    UnaryExpression  unaryExpression  = (UnaryExpression)node.Arguments[1];
                    LambdaExpression lambdaExpression = (LambdaExpression)unaryExpression.Operand;

                    if (lambdaExpression.Body is NewExpression newExpression)
                    {
                        multipleProjectionFlag = newExpression.ArgumentCount() > 1;
                    }
                }
                functions[methodName]++;
            }
            catch (Exception e)
            {
                Logger.Log(e, Microsoft.Extensions.Logging.LogLevel.Warning);
            }

            if (node.Object != null)
            {
                Visit(node.Object);
            }

            for (int i = 0, n = node.ArgumentCount(); i < n; i++)
            {
                Visit(node.GetArgument(i));
            }

            return(node);
        }
        public static bool ExtractDefaultIfEmpty(ref Expression expression)
        {
            MethodCallExpression mce = expression as MethodCallExpression;

            if (mce == null || !mce.Method.IsGenericMethod)
            {
                return(false);
            }

            MethodInfo me = mce.Method.GetGenericMethodDefinition();

            if (!ReflectionTools.MethodEqual(me, miDefaultIfEmptyE) && !ReflectionTools.MethodEqual(me, miDefaultIfEmptyQ))
            {
                return(false);
            }

            expression = mce.GetArgument("source");
            return(true);
        }
Exemple #11
0
        public override void ProcessMethodCall(IOqlSyntaxContext callContext, MethodCallExpression methodCall)
        {
            Expression x = methodCall.GetArgument(1);

            if (x.NodeType == ExpressionType.MemberInit)
            {
                m_change_set = new MemberInitChangeSet(x as MemberInitExpression);
                return;
            }

            object obj = x.GetValue();

            if (obj is IDataChangeSet)
            {
                m_change_set = obj as IDataChangeSet;
                return;
            }


            m_change_set = new ObjectChangeSet(obj).ChangeAllProperties();
        }
Exemple #12
0
        /* ********************************************************************************************************
        * --------------------------------                VISITORS              -------------------------------- *
        ******************************************************************************************************** */

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            Logger.Log("VisitMethodCall : " + node.ToString(), Microsoft.Extensions.Logging.LogLevel.Trace);

            object evalResult = EvaluateMethodAndCall(node);

            if (evalResult != null)
            {
                PadPossibleStringValueToWhere(evalResult)
                .Append(Space);

                /*
                 * We don't need to visit further nodes.
                 * Doing so will pad the arguments of the
                 * method invoked which shouldn't be done.
                 */
                return(node);
            }

            if (node.Object != null)
            {
                Visit(node.Object);
            }

            for (int i = 0, n = node.ArgumentCount(); i < n; i++)
            {
                Visit(node.GetArgument(i));
            }

            // Add an 'AND' for separating query data criteria.
            if (IsAndAble(node.Method.Name))
            {
                if (whereBuilder.Length > 0)
                {
                    whereBuilder.Append("AND").Append(Space);
                }
            }

            return(node);
        }
Exemple #13
0
        public static void CheckCallFactoryStaticN()
        {
            const int N = 6;

            ConstantExpression[] args = Enumerable.Range(0, N).Select(i => Expression.Constant(i)).ToArray();

            MethodCallExpression expr = Expression.Call(typeof(MS).GetMethod("S" + N), args);

            Assert.Equal("MethodCallExpressionN", expr.GetType().Name);

            Assert.Equal(N, expr.ArgumentCount);
            for (var i = 0; i < N; i++)
            {
                Assert.Same(args[i], expr.GetArgument(i));
            }

            Collections.ObjectModel.ReadOnlyCollection <Expression> arguments = expr.Arguments;
            Assert.Same(arguments, expr.Arguments);

            Assert.Equal(N, arguments.Count);
            for (var i = 0; i < N; i++)
            {
                Assert.Same(args[i], arguments[i]);
            }

            MethodCallExpression updated = expr.Update(null, arguments);

            Assert.Same(expr, updated);

            var visited = (MethodCallExpression) new NopVisitor().Visit(expr);

            Assert.Same(expr, visited);

            var visitedArgs = (MethodCallExpression) new VisitorArgs().Visit(expr);

            Assert.NotSame(expr, visitedArgs);
            Assert.Same(null, visitedArgs.Object);
            Assert.NotSame(arguments, visitedArgs.Arguments);
        }
Exemple #14
0
            protected override Expression VisitMethodCall(MethodCallExpression node)
            {
                Logger.Log("Visiting node : " + node.ToString(), Microsoft.Extensions.Logging.LogLevel.Trace);

                if (node.Object != null)
                {
                    Visit(node.Object);
                }

                for (int i = 0, n = node.ArgumentCount(); i < n; i++)
                {
                    Visit(node.GetArgument(i));
                }

                if (node.Method.Name.Equals("Select"))
                {
                    foreach (var arg in node.Arguments)
                    {
                        selectArguments.Add(arg);
                    }
                }

                return(node);
            }
Exemple #15
0
 /// <summary>
 /// Gets the lambda expression given by the first argument.
 /// </summary>
 /// <param name="expression"></param>
 /// <param name="index"></param>
 /// <returns></returns>
 public static LambdaExpression GetLambdaArgument(this MethodCallExpression expression, int index)
 {
     return(expression.GetArgument(index).UnpackLambda());
 }
Exemple #16
0
 /// <summary>
 /// Gets the lambda expression given by the first argument.
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <typeparam name="TResult"></typeparam>
 /// <param name="expression"></param>
 /// <param name="index"></param>
 /// <returns></returns>
 public static Expression <Func <T, TResult> > GetLambdaArgument <T, TResult>(this MethodCallExpression expression, int index)
 {
     return(expression.GetArgument(index).UnpackLambda <T, TResult>());
 }
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.DeclaringType == typeof(Queryable) ||
                m.Method.DeclaringType == typeof(Enumerable) ||
                m.Method.DeclaringType == typeof(EnumerableUniqueExtensions))
            {
                switch (m.Method.Name)
                {
                case "Where":
                    return(this.BindWhere(m.Type, m.GetArgument("source"), m.GetArgument("predicate").StripQuotes()));

                case "Select":
                    return(this.BindSelect(m.Type, m.GetArgument("source"), m.GetArgument("selector").StripQuotes()));

                case "SelectMany":
                    if (m.Arguments.Count == 2)
                    {
                        return(this.BindSelectMany(m.Type, m.GetArgument("source"), m.GetArgument("selector").StripQuotes(), null));
                    }
                    else
                    {
                        return(this.BindSelectMany(m.Type, m.GetArgument("source"), m.GetArgument("collectionSelector").StripQuotes(), m.TryGetArgument("resultSelector").StripQuotes()));
                    }

                case "Join":
                    return(this.BindJoin(
                               m.Type, m.GetArgument("outer"), m.GetArgument("inner"),
                               m.GetArgument("outerKeySelector").StripQuotes(),
                               m.GetArgument("innerKeySelector").StripQuotes(),
                               m.GetArgument("resultSelector").StripQuotes()));

                case "OrderBy":
                    return(this.BindOrderBy(m.Type, m.GetArgument("source"), m.GetArgument("keySelector").StripQuotes(), OrderType.Ascending));

                case "OrderByDescending":
                    return(this.BindOrderBy(m.Type, m.GetArgument("source"), m.GetArgument("keySelector").StripQuotes(), OrderType.Descending));

                case "ThenBy":
                    return(this.BindThenBy(m.GetArgument("source"), m.GetArgument("keySelector").StripQuotes(), OrderType.Ascending));

                case "ThenByDescending":
                    return(this.BindThenBy(m.GetArgument("source"), m.GetArgument("keySelector").StripQuotes(), OrderType.Descending));

                case "GroupBy":
                    return(this.BindGroupBy(m.Type, m.GetArgument("source"),
                                            m.GetArgument("keySelector").StripQuotes(),
                                            m.GetArgument("elementSelector").StripQuotes()));

                case "Count":
                    return(this.BindCount(m.Type, m.GetArgument("source")));

                case "DefaultIfEmpty":
                    return(Visit(m.GetArgument("source")));

                case "Any":
                    return(this.BindAny(m.Type, m.GetArgument("source")));

                case "All":
                    return(this.BindAll(m.Type, m.GetArgument("source"), m.GetArgument("predicate").StripQuotes()));

                case "Contains":
                    return(this.BindContains(m.Type, m.GetArgument("source"), m.TryGetArgument("item") ?? m.GetArgument("value")));

                case "Sum":
                case "Min":
                case "Max":
                case "Average":
                    return(this.BindAggregate(m.Type, m.Method.Name.ToEnum <AggregateSqlFunction>(),
                                              m.GetArgument("source"), m.TryGetArgument("selector").StripQuotes()));

                case "First":
                case "FirstOrDefault":
                case "Single":
                case "SingleOrDefault":
                    return(BindUniqueRow(m.Type, m.Method.Name.ToEnum <UniqueFunction>(),
                                         m.GetArgument("source"), m.TryGetArgument("predicate").StripQuotes()));

                case "FirstEx":
                case "SingleEx":
                case "SingleOrDefaultEx":
                    return(BindUniqueRow(m.Type, m.Method.Name.RemoveEnd(2).ToEnum <UniqueFunction>(),
                                         m.GetArgument("collection"), m.TryGetArgument("predicate").StripQuotes()));

                case "Distinct":
                    return(BindDistinct(m.Type, m.GetArgument("source")));

                case "Take":
                    return(BindTake(m.Type, m.GetArgument("source"), m.GetArgument("count")));

                case "Skip":
                    return(BindSkip(m.Type, m.GetArgument("source"), m.GetArgument("count")));
                }
            }

            if (m.Method.Name == "Mixin" && m.Method.GetParameters().Length == 0)
            {
                var obj = Visit(m.Object);

                if (obj is MetaExpression me && me.Meta is CleanMeta)
                {
                    CleanMeta cm = (CleanMeta)me.Meta;

                    var mixinType = m.Method.GetGenericArguments().Single();

                    return(new MetaExpression(mixinType, new CleanMeta(null, cm.PropertyRoutes.Select(a => a.Add(mixinType)).ToArray())));
                }
            }

            if (m.Method.DeclaringType == typeof(LinqHints) || m.Method.DeclaringType == typeof(LinqHintEntities))
            {
                return(Visit(m.Arguments[0]));
            }

            if (m.Method.DeclaringType == typeof(StringExtensions) && m.Method.Name == nameof(StringExtensions.Etc))
            {
                return(Visit(m.Arguments[0]));
            }

            if (m.Method.DeclaringType == typeof(Lite) && m.Method.Name == "ToLite")
            {
                return(MakeCleanMeta(m.Type, Visit(m.Arguments[0])));
            }

            if (m.Method.DeclaringType == typeof(Math) &&
                (m.Method.Name == "Abs" ||
                 m.Method.Name == "Ceiling" ||
                 m.Method.Name == "Floor" ||
                 m.Method.Name == "Round" ||
                 m.Method.Name == "Truncate"))
            {
                return(MakeCleanMeta(m.Type, Visit(m.Arguments[0])));
            }

            if (m.Method.Name == "ToString" && m.Object != null && typeof(IEntity).IsAssignableFrom(m.Object.Type))
            {
                return(Visit(Expression.Property(m.Object, piToStringProperty)));
            }

            if (m.Object != null)
            {
                var a    = this.Visit(m.Object);
                var list = this.Visit(m.Arguments);
                return(MakeDirtyMeta(m.Type, null, list.PreAnd(a).ToArray()));
            }
            else
            {
                var list = this.Visit(m.Arguments);
                return(MakeDirtyMeta(m.Type, null, list.ToArray()));
            }
        }
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            Type decType = m.Method.DeclaringType;

            if (m.Method.IsGenericMethod && (decType == typeof(Queryable) || decType == typeof(Enumerable)))
            {
                bool query = decType == typeof(Queryable);

                Type[]     paramTypes = m.Method.GetGenericArguments();
                MethodInfo mi         = m.Method.GetGenericMethodDefinition();

                //IE<IGrouping<K, S>> GroupBy<S, K>(this IE<S> source, Func<S, K> keySelector);
                //    GroupBy(col, a=>func(a)) -> GroupBy(col, a=>func(a), a=>a)

                if (ReflectionTools.MethodEqual(mi, miGroupBySE) || ReflectionTools.MethodEqual(mi, miGroupBySQ))
                {
                    var source      = Visit(m.GetArgument("source"));
                    var keySelector = (LambdaExpression)Visit(m.GetArgument("keySelector").StripQuotes());

                    MethodInfo miG = (query ? miGroupByNQ : miGroupByNE)
                                     .MakeGenericMethod(paramTypes[0], paramTypes[1], paramTypes[0]);

                    ParameterExpression p = Expression.Parameter(paramTypes[0], "p" + i++);

                    return(Expression.Call(miG, source, keySelector, Expression.Lambda(p, p)));
                }

                //IE<R> GroupBy<S, K, R>(this IE<S> source, Func<S, K> keySelector, Func<K, IE<S>, R> resultSelector);
                //    GroupBy(col, a=>f1(a), a=>f2(a), (a,B)=>f3(a,B)) -> GroupBy(col, a=>f1(a), a=>f2(a)).Select(g=>=>f3(g.Key,g))

                if (ReflectionTools.MethodEqual(mi, miGroupBySRE) || ReflectionTools.MethodEqual(mi, miGroupBySRQ))
                {
                    var source         = Visit(m.GetArgument("source"));
                    var keySelector    = (LambdaExpression)Visit(m.GetArgument("keySelector").StripQuotes());
                    var resultSelector = (LambdaExpression)Visit(m.GetArgument("resultSelector").StripQuotes());

                    Type groupingType = typeof(IGrouping <,>).MakeGenericType(paramTypes[1], paramTypes[0]);

                    MethodInfo miG = (query ? miGroupByNQ : miGroupByNE)
                                     .MakeGenericMethod(paramTypes[0], paramTypes[1], paramTypes[0]);

                    MethodInfo miS = (query ? miSelectQ : miSelectE)
                                     .MakeGenericMethod(groupingType, paramTypes[2]);

                    ParameterExpression g = Expression.Parameter(groupingType, "g" + i++);

                    LambdaExpression newResult =
                        Expression.Lambda(
                            Replacer.Replace(Replacer.Replace(resultSelector.Body,
                                                              resultSelector.Parameters[0], Expression.MakeMemberAccess(g, groupingType.GetProperty("Key"))),
                                             resultSelector.Parameters[1], g),
                            g);


                    ParameterExpression p = Expression.Parameter(paramTypes[0], "p" + i++);
                    return
                        (Expression.Call(miS,
                                         Expression.Call(miG, source, keySelector, Expression.Lambda(p, p)),
                                         newResult));
                }

                //IE<R> GroupBy<S, K, E, R>(this IE<S> source, Func<S, K> keySelector, Func<S, E> elementSelector, Func<K, IE<E>, R> resultSelector)
                //    GroupBy(col, a=>f1(a), a=>f2(a), (k,B)=>f(k,B)) -> GroupBy(col, a=>f1(a), a=>f2(a)).Select(g=>f3(g.Key,g))


                if (ReflectionTools.MethodEqual(mi, miGroupByNRE) || ReflectionTools.MethodEqual(mi, miGroupByNRQ))
                {
                    var source          = Visit(m.GetArgument("source"));
                    var keySelector     = (LambdaExpression)Visit(m.GetArgument("keySelector").StripQuotes());
                    var elementSelector = (LambdaExpression)Visit(m.GetArgument("elementSelector").StripQuotes());
                    var resultSelector  = (LambdaExpression)Visit(m.GetArgument("resultSelector").StripQuotes());

                    Type groupingType = typeof(IGrouping <,>).MakeGenericType(paramTypes[1], paramTypes[2]);

                    MethodInfo miG = (query ? miGroupByNQ : miGroupByNE)
                                     .MakeGenericMethod(paramTypes[0], paramTypes[1], paramTypes[2]);

                    MethodInfo miS = (query ? miSelectQ : miSelectE)
                                     .MakeGenericMethod(groupingType, paramTypes[3]);

                    ParameterExpression g = Expression.Parameter(groupingType, "g" + i++);

                    LambdaExpression newResult =
                        Expression.Lambda(
                            Replacer.Replace(Replacer.Replace(resultSelector.Body,
                                                              resultSelector.Parameters[0], Expression.MakeMemberAccess(g, groupingType.GetProperty("Key"))),
                                             resultSelector.Parameters[1], g),
                            g);

                    return
                        (Expression.Call(miS,
                                         Expression.Call(miG, source, keySelector, elementSelector),
                                         newResult));
                }

                //IE<R> GroupJoin<O, I, K, R>(this IE<O> outer, IE<I> inner, Func<O, K> outerKeySelector, Func<I, K> innerKeySelector, Func<O, IE<I>, R> resultSelector)
                //    GroupJoin(outer, inner, o=>f1(o), i=>f2
                //(i), (o, gI)=>f3(o,gI)) -->

                //      Join(outer, GroupBy(inner, i=>f2(i), i=>i) , o=>f1(o), g=>g.Key, (o,g)=>f2(o, g))


                if (ReflectionTools.MethodEqual(mi, miGroupJoinE) || ReflectionTools.MethodEqual(mi, miGroupJoinQ))
                {
                    Type tO = paramTypes[0], tI = paramTypes[1], tK = paramTypes[2], tR = paramTypes[3];

                    var outer = Visit(m.GetArgument("outer"));
                    var inner = Visit(m.GetArgument("inner"));

                    bool hasDefaultIfEmpty = ExtractDefaultIfEmpty(ref inner);

                    var outerKeySelector = (LambdaExpression)Visit(m.GetArgument("outerKeySelector").StripQuotes());
                    var innerKeySelector = (LambdaExpression)Visit(m.GetArgument("innerKeySelector").StripQuotes());
                    var resultSelector   = (LambdaExpression)Visit(m.GetArgument("resultSelector").StripQuotes());

                    Type groupingType = typeof(IGrouping <,>).MakeGenericType(tK, tI);

                    MethodInfo miG = (query ? miGroupByNQ : miGroupByNQ)
                                     .MakeGenericMethod(tI, tK, tI);

                    ParameterExpression p     = Expression.Parameter(tI, "p" + i++);
                    Expression          group = Expression.Call(miG, inner, innerKeySelector, Expression.Lambda(p, p));

                    if (hasDefaultIfEmpty)
                    {
                        var method = (query ? miDefaultIfEmptyQ : miDefaultIfEmptyE)
                                     .MakeGenericMethod(groupingType);

                        group = Expression.Call(method, group);
                    }

                    //IQueryable<R> Join<TOuter, TInner, TKey, R>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, R>> resultSelector);

                    MethodInfo mij = (query ? miJoinQ : miJoinE)
                                     .MakeGenericMethod(tO, groupingType, tK, tR);

                    ParameterExpression g         = Expression.Parameter(groupingType, "g" + i++);
                    LambdaExpression    newResult =
                        Expression.Lambda(
                            Replacer.Replace(resultSelector.Body, resultSelector.Parameters[1], g),
                            resultSelector.Parameters[0], g);


                    return
                        (Expression.Call(mij, outer, group, outerKeySelector,
                                         Expression.Lambda(Expression.MakeMemberAccess(g, groupingType.GetProperty("Key")), g),
                                         newResult));
                }

                if (ReflectionTools.MethodEqual(mi, miCastE) || ReflectionTools.MethodEqual(mi, miCastQ))
                {
                    var source = Visit(m.GetArgument("source"));

                    Type elemType = source.Type.ElementType();

                    ParameterExpression pe = Expression.Parameter(elemType);

                    var lambdaCast = Expression.Lambda(Expression.Convert(pe, paramTypes[0]), pe);

                    return(Expression.Call((query ? miSelectQ : miSelectE).MakeGenericMethod(elemType, paramTypes[0]), source, lambdaCast));
                }

                if (ReflectionTools.MethodEqual(mi, miOfTypeE) || ReflectionTools.MethodEqual(mi, miOfTypeQ))
                {
                    var source = Visit(m.GetArgument("source"));

                    Type elemType = source.Type.ElementType();

                    ParameterExpression pe = Expression.Parameter(elemType);

                    var lambdaIs = Expression.Lambda(Expression.TypeIs(pe, paramTypes[0]), pe);

                    var lambdaCast = Expression.Lambda(Expression.Convert(pe, paramTypes[0]), pe);

                    var where = Expression.Call((query ? miWhereQ : miWhereE).MakeGenericMethod(elemType), source, lambdaIs);

                    return(Expression.Call((query ? miSelectQ : miSelectE).MakeGenericMethod(elemType, paramTypes[0]), where, lambdaCast));
                }



                if (mi.Name.Contains("Last"))
                {
                    var source    = Visit(m.GetArgument("source"));
                    var predicate = (LambdaExpression)Visit(m.TryGetArgument("predicate").StripQuotes());

                    Expression reverse = Expression.Call((query ? miReverseQ : miReverseE).MakeGenericMethod(paramTypes[0]), source);

                    if (predicate != null)
                    {
                        reverse = Expression.Call((query ? miWhereQ : miWhereE).MakeGenericMethod(paramTypes[0]), reverse, predicate);
                    }

                    MethodInfo mEqFirst = query ?
                                          mi.Name.Contains("OrDefault") ? miFirstOrDefaultQ : miFirstQ :
                                          mi.Name.Contains("OrDefault") ? miFirstOrDefaultE : miFirstE;

                    return(Expression.Call(mEqFirst.MakeGenericMethod(paramTypes[0]), reverse));
                }


                if (ReflectionTools.MethodEqual(mi, miElementAtE) || ReflectionTools.MethodEqual(mi, miElementAtOrDefaultE) ||
                    ReflectionTools.MethodEqual(mi, miElementAtQ) || ReflectionTools.MethodEqual(mi, miElementAtOrDefaultQ))
                {
                    bool def = ReflectionTools.MethodEqual(mi, miElementAtOrDefaultE) || ReflectionTools.MethodEqual(mi, miElementAtOrDefaultQ);

                    var source = Visit(m.GetArgument("source"));
                    var index  = Visit(m.GetArgument("index"));

                    MethodInfo first = (def ? (query ? miFirstOrDefaultQ : miFirstOrDefaultE) :
                                        (query ? miFirstQ : miFirstE)).MakeGenericMethod(paramTypes[0]);

                    MethodInfo skip = (query ? miSkipQ : miSkipE).MakeGenericMethod(paramTypes[0]);
                    return(Visit(Expression.Call(first, Expression.Call(skip, source, index))));
                }


                if (ReflectionTools.MethodEqual(mi, miSkipE) || ReflectionTools.MethodEqual(mi, miSkipQ))
                {
                    var source = Visit(m.GetArgument("source"));
                    var count  = Visit(m.GetArgument("count"));

                    ParameterExpression pi     = Expression.Parameter(typeof(int), "i");
                    ParameterExpression pa     = Expression.Parameter(paramTypes[0], "a");
                    Expression          lambda = Expression.Lambda(Expression.LessThanOrEqual(count, pi), pa, pi);

                    MethodInfo miWhereIndex = (query ? miWhereIndexQ : miWhereIndexE).MakeGenericMethod(paramTypes[0]);

                    return(Expression.Call(miWhereIndex, source, lambda));
                }


                if (ReflectionTools.MethodEqual(mi, miTakeE) || ReflectionTools.MethodEqual(mi, miTakeQ))
                {
                    if (m.GetArgument("source") is MethodCallExpression m2)
                    {
                        var mi2 = (((MethodCallExpression)m2).Method).GetGenericMethodDefinition();

                        if (ReflectionTools.MethodEqual(mi2, miSkipE) || ReflectionTools.MethodEqual(mi2, miSkipQ))
                        {
                            var source = Visit(m2.GetArgument("source"));
                            var skip   = Visit(m2.GetArgument("count"));
                            var take   = Visit(m.GetArgument("count"));

                            ParameterExpression pi     = Expression.Parameter(typeof(int), "i");
                            ParameterExpression pa     = Expression.Parameter(paramTypes[0], "a");
                            Expression          lambda = Expression.Lambda(
                                Expression.And(
                                    Expression.LessThanOrEqual(skip, pi),
                                    Expression.LessThan(pi, Expression.Add(skip, take))
                                    ), pa, pi);

                            MethodInfo miWhereIndex = (query ? miWhereIndexQ : miWhereIndexE).MakeGenericMethod(paramTypes[0]);

                            return(Expression.Call(miWhereIndex, source, lambda));
                        }
                    }
                }
            }

            if (m.Method.DeclaringType == typeof(Tuple) && m.Method.Name == "Create")
            {
                var types = m.Arguments.Select(e => e.Type).ToArray();
                if (types.Length < 8)
                {
                    return(Expression.New(m.Method.ReturnType.GetConstructor(types), m.Arguments.ToArray()));
                }
                else
                {
                    Type lastType = types[7];
                    types[7] = typeof(Tuple <>).MakeGenericType(lastType);

                    return(Expression.New(m.Method.ReturnType.GetConstructor(types), m.Arguments.Take(7).And(
                                              Expression.New(types[7].GetConstructor(new[] { lastType }), m.Arguments[7])).ToArray()));
                }
            }

            if (m.Method.DeclaringType == typeof(EnumerableExtensions) && m.Method.IsGenericMethod)
            {
                MethodInfo mi = m.Method.GetGenericMethodDefinition();

                if (ReflectionTools.MethodEqual(mi, miToStringSeparator))
                {
                    var type = m.Method.GetGenericArguments().SingleEx();
                    if (type != typeof(string))
                    {
                        var source    = Visit(m.GetArgument("source"));
                        var p         = Expression.Parameter(type);
                        var toString  = Visit(Expression.Lambda(Expression.Call(p, miToString), p));
                        var separator = Visit(m.GetArgument("separator"));

                        return(Expression.Call(miToStringSeparator.MakeGenericMethod(typeof(string)),
                                               Expression.Call(miSelectE.MakeGenericMethod(type, typeof(string)), source, toString),
                                               separator));
                    }
                }
                else if (ReflectionTools.MethodEqual(mi, miToStringSeparatorE) || ReflectionTools.MethodEqual(mi, miToStringSeparatorQ))
                {
                    var  type    = m.Method.GetGenericArguments().SingleEx();
                    bool isQuery = ReflectionTools.MethodEqual(mi, miToStringSeparatorQ);

                    var source    = Visit(m.GetArgument("source"));
                    var toString  = (LambdaExpression)Visit(m.GetArgument("toString").StripQuotes());
                    var separator = Visit(m.GetArgument("separator"));

                    return(Expression.Call(miToStringSeparator.MakeGenericMethod(typeof(string)),
                                           Expression.Call((isQuery ? miSelectQ : miSelectE).MakeGenericMethod(type, typeof(string)), source, toString),
                                           separator));
                }
            }

            return(base.VisitMethodCall(m));
        }