public static MethodCallExpression SubstituteWithWhere(this MethodCallExpression node, bool negate = false) { Check.NotNull(node, nameof(node)); Expression predicate = node.Arguments[1]; if (negate) { LambdaExpression lambdaExpression = node.GetLambda(); UnaryExpression body = Expression.Not(lambdaExpression.Body); predicate = body.WrapInLambda(lambdaExpression.Parameters); } return(Expression.Call(typeof(Queryable), nameof(Queryable.Where), node.Method.GetGenericArguments(), node.Arguments[0], predicate)); }
protected override Expression VisitMethodCall(MethodCallExpression node) { Check.NotNull(node, nameof(node)); if (!node.Method.IsGenericMethod) { return(node); } if (!node.Method.IsSupportedNativelyOrByComposition()) { throw new NotSupportedException($"Method {node.Method.Name} cannot be converter to a valid query."); } MethodInfo genericDefinition = node.Method.GetGenericMethodDefinition(); #region Bool member to constants if (!_isVisitingWhereMethodOrChild && genericDefinition == QueryableMethods.Where) { _isVisitingWhereMethodOrChild = true; Expression whereNode = VisitMethodCall(node); _isVisitingWhereMethodOrChild = false; return(whereNode); } #endregion #region Multi-Where Optimization if (genericDefinition == QueryableMethods.Where) { if (_nextWhereCalls.Count == 0) { _nextWhereCalls.Enqueue(node); Expression tail = Visit(node.Arguments[0]); LambdaExpression currentLambda = node.GetLambda(); Expression conditionExpression = Visit(currentLambda.Body); _nextWhereCalls.Dequeue(); while (_nextWhereCalls.Count > 0) { Expression nextWhereBody = Visit(_nextWhereCalls.Dequeue().GetLambdaBody()); conditionExpression = Expression.And(nextWhereBody, conditionExpression); } Expression conditionLambda = conditionExpression.WrapInLambda(currentLambda.Parameters); return(Expression.Call(typeof(Queryable), nameof(Queryable.Where), node.Method.GetGenericArguments(), tail, conditionLambda)); } _nextWhereCalls.Enqueue(node); return(Visit(node.Arguments[0])); } #endregion #region Min/Max // Min(d => d.Property) == OrderBy(d => d.Property).Take(1).Select(d => d.Property).Min() if (genericDefinition == QueryableMethods.MinWithSelector) { return(node .SubstituteWithQueryableCall(nameof(Queryable.OrderBy)) .WrapInTake(1) .WrapInSelect(node) .WrapInMinMax(QueryableMethods.MinWithoutSelector)); } // Max(d => d.Property) == OrderByDescending(d => d.Property).Take(1).Select(d => d.Property).Max() if (genericDefinition == QueryableMethods.MaxWithSelector) { return(node .SubstituteWithQueryableCall(nameof(Queryable.OrderByDescending)) .WrapInTake(1) .WrapInSelect(node) .WrapInMinMax(QueryableMethods.MaxWithoutSelector)); } #endregion #region Sum/Average // Sum(d => d.Property) == Select(d => d.Property).Sum() if (QueryableMethods.IsSumWithSelector(genericDefinition)) { return(node .SubstituteWithSelect(node) .WrapInAverageSum(node)); } // Average(d => d.Property) == Select(d => d.Property).Average() if (QueryableMethods.IsAverageWithSelector(genericDefinition)) { return(node .SubstituteWithSelect(node) .WrapInAverageSum(node)); } #endregion #region Any/All // Any() => Take(1).Any() if (genericDefinition == QueryableMethods.AnyWithoutPredicate) { return(node .WrapInTake(1) .WrapInMethodWithoutSelector(QueryableMethods.AnyWithoutPredicate)); } // Any(d => condition) == Where(d => condition).Take(1).Any() if (genericDefinition == QueryableMethods.AnyWithPredicate) { return(node .SubstituteWithWhere() .WrapInTake(1) .WrapInMethodWithoutSelector(QueryableMethods.AnyWithoutPredicate)); } // All(d => condition) == Where(d => !condition).Take(1).Any() if (genericDefinition == QueryableMethods.All) { return(node .SubstituteWithWhere(true) .WrapInTake(1) .WrapInMethodWithoutSelector(QueryableMethods.AnyWithoutPredicate)); } #endregion #region Single/SingleOrDefault // Single() == Take(2).Single() if (genericDefinition == QueryableMethods.SingleWithoutPredicate) { return(node .SubstituteWithTake(2) .WrapInMethodWithoutSelector(QueryableMethods.SingleWithoutPredicate)); } // SingleOrDefault() == Take(2).SingleOrDefault() if (genericDefinition == QueryableMethods.SingleOrDefaultWithoutPredicate) { return(node .SubstituteWithTake(2) .WrapInMethodWithoutSelector(QueryableMethods.SingleOrDefaultWithoutPredicate)); } // Single(d => condition) == Where(d => condition).Take(2).Single() if (genericDefinition == QueryableMethods.SingleWithPredicate) { return(node .SubstituteWithWhere() .SubstituteWithTake(2) .WrapInMethodWithoutSelector(QueryableMethods.SingleWithoutPredicate)); } // SingleOrDefault(d => condition) == Where(d => condition).Take(2).SingleOrDefault() if (genericDefinition == QueryableMethods.SingleOrDefaultWithPredicate) { return(node .SubstituteWithWhere() .SubstituteWithTake(2) .WrapInMethodWithoutSelector(QueryableMethods.SingleOrDefaultWithoutPredicate)); } #endregion #region First/FirstOrDefault // First() == Take(1).First() if (genericDefinition == QueryableMethods.FirstWithoutPredicate) { return(node .SubstituteWithTake(1) .WrapInMethodWithoutSelector(QueryableMethods.FirstWithoutPredicate)); } // FirstOrDefault() == Take(1).FirstOrDefault() if (genericDefinition == QueryableMethods.FirstOrDefaultWithoutPredicate) { return(node .SubstituteWithTake(1) .WrapInMethodWithoutSelector(QueryableMethods.FirstOrDefaultWithoutPredicate)); } // First(d => condition) == Where(d => condition).Take(1).First() if (genericDefinition == QueryableMethods.FirstWithPredicate) { return(node .SubstituteWithWhere() .SubstituteWithTake(1) .WrapInMethodWithoutSelector(QueryableMethods.FirstWithoutPredicate)); } // FirstOrDefault(d => condition) == Where(d => condition).Take(1).FirstOrDefault() if (genericDefinition == QueryableMethods.FirstOrDefaultWithPredicate) { return(node .SubstituteWithWhere() .SubstituteWithTake(1) .WrapInMethodWithoutSelector(QueryableMethods.FirstOrDefaultWithoutPredicate)); } #endregion #region Last/LastOrDefault // Last() == Last() // LastOrDefault() == LastOrDefault() if (genericDefinition == QueryableMethods.LastWithoutPredicate || genericDefinition == QueryableMethods.LastOrDefaultWithoutPredicate) { return(node); } // Last(d => condition) == Where(d => condition).Last() if (genericDefinition == QueryableMethods.LastWithPredicate) { return(node .SubstituteWithWhere() .WrapInMethodWithoutSelector(QueryableMethods.LastWithoutPredicate)); } // LastOrDefault(d => condition) == Where(d => condition).LastOrDefault() if (genericDefinition == QueryableMethods.LastOrDefaultWithPredicate) { return(node .SubstituteWithWhere() .WrapInMethodWithoutSelector(QueryableMethods.LastOrDefaultWithoutPredicate)); } #endregion return(base.VisitMethodCall(node)); }