Ejemplo n.º 1
0
        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, miCount2E) || ReflectionTools.MethodEqual(mi, miCount2Q))
                {
                    var source = Visit(m.GetArgument("source"));
                    var predicate = (LambdaExpression)Visit(m.GetArgument("predicate").StripQuotes());

                    MethodInfo mWhere = (query ? miWhereQ : miWhereE).MakeGenericMethod(paramTypes[0]);
                    MethodInfo mCount = (query ? miCountQ : miCountE).MakeGenericMethod(paramTypes[0]);
                    
                    return Expression.Call(mCount, Expression.Call(mWhere, source, predicate)); 
                }

                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))
                {
                    var m2 = m.GetArgument("source") as MethodCallExpression;

                    if(m2 != null)
                    {
                        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); 
        }
Ejemplo n.º 2
0
        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<AggregateFunction>(),
                            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);

                var me = obj as MetaExpression;
                if (me != null && 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(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(IIdentifiable).IsAssignableFrom(m.Object.Type))
                return Visit(Expression.Property(m.Object, piToStringProperty));

            if (m.Object != null)
            {
                var a = this.Visit(m.Object);
                var list = this.VisitExpressionList(m.Arguments);
                return MakeDirtyMeta(m.Type, null, list.PreAnd(a).ToArray());
            }
            else
            {
                var list = this.VisitExpressionList(m.Arguments);
                return MakeDirtyMeta(m.Type, null, list.ToArray());
            }
        }