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