static bool IsAggregateResultWithCustomShaper(MethodInfo method) { if (method.IsGenericMethod) { method = method.GetGenericMethodDefinition(); } return(QueryableMethods.IsAverageWithoutSelector(method) || QueryableMethods.IsAverageWithSelector(method) || method == QueryableMethods.MaxWithoutSelector || method == QueryableMethods.MaxWithSelector || method == QueryableMethods.MinWithoutSelector || method == QueryableMethods.MinWithSelector || QueryableMethods.IsSumWithoutSelector(method) || QueryableMethods.IsSumWithSelector(method)); }
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)); }
protected override Expression VisitMethodCall(MethodCallExpression m) { if (QueryableMethods.IsAverageWithoutSelector(m.Method) || QueryableMethods.IsSumWithoutSelector(m.Method)) { return(Visit(m.Arguments[0])); } MethodInfo?genericDefinition = m.Method.IsGenericMethod ? m.Method.GetGenericMethodDefinition() : null; if (genericDefinition != null && genericDefinition.IsSupportedByComposition()) { return(Visit(m.Arguments[0])); } // Queryable if (genericDefinition == QueryableMethods.Where) { return(VisitWhereMethod(m)); } if (genericDefinition == QueryableMethods.OrderBy || genericDefinition == QueryableMethods.ThenBy) { return(VisitOrderAscendingMethod(m)); } if (genericDefinition == QueryableMethods.OrderByDescending || genericDefinition == QueryableMethods.ThenByDescending) { return(VisitOrderDescendingMethod(m)); } if (genericDefinition == QueryableMethods.Skip) { return(VisitSkipMethod(m)); } if (genericDefinition == QueryableMethods.Take) { return(VisitTakeMethod(m)); } if (genericDefinition == QueryableMethods.Select) { return(VisitSelectMethod(m)); } // Enumerable if (m.Method == SupportedQueryMethods.Contains) { return(VisitContainsMethod(m)); } // IQueryable extensions if (genericDefinition == SupportedQueryMethods.AnyWithPredicate) { return(VisitAnyMethod(m)); } if (genericDefinition == SupportedQueryMethods.All) { return(VisitAllMethod(m)); } if (genericDefinition == SupportedQueryMethods.UseBookmark) { return(VisitUseBookmarkMethod(m)); } if (genericDefinition == SupportedQueryMethods.WithReadQuorum) { return(VisitWithQuorumMethod(m)); } if (genericDefinition == SupportedQueryMethods.WithoutIndexUpdate) { return(VisitWithoutIndexUpdateMethod(m)); } if (genericDefinition == SupportedQueryMethods.FromStable) { return(VisitFromStableMethod(m)); } if (genericDefinition == SupportedQueryMethods.UseIndex) { return(VisitUseIndexMethod(m)); } if (genericDefinition == SupportedQueryMethods.IncludeExecutionStats) { return(VisitIncludeExecutionStatsMethod(m)); } if (genericDefinition == SupportedQueryMethods.IncludeConflicts) { return(VisitIncludeConflictsMethod(m)); } if (genericDefinition == SupportedQueryMethods.Select) { return(VisitSelectFieldMethod(m)); } if (genericDefinition == SupportedQueryMethods.Convert) { return(VisitConvertMethod(m)); } // IEnumerable extensions if (genericDefinition == SupportedQueryMethods.EnumerableContains) { return(VisitEnumerableContains(m)); } // Object extensions if (genericDefinition == SupportedQueryMethods.FieldExists) { return(VisitFieldExistsMethod(m)); } if (genericDefinition == SupportedQueryMethods.IsCouchType) { return(VisitIsCouchTypeMethod(m)); } if (genericDefinition == SupportedQueryMethods.In) { return(VisitInMethod(m)); } // String extensions if (m.Method == SupportedQueryMethods.IsMatch) { return(VisitIsMatchMethod(m)); } // List if (m.Method.Name == nameof(List <object> .Contains) && m.Method.DeclaringType.IsGenericType && m.Method.DeclaringType.GetGenericTypeDefinition() == typeof(List <>)) { return(VisitContainsMethod(m)); } throw new NotSupportedException($"The method '{m.Method.Name}' is not supported"); }
public virtual SqlExpression?Translate( MethodInfo method, EnumerableExpression source, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (method.DeclaringType == typeof(Queryable)) { var methodInfo = method.IsGenericMethod ? method.GetGenericMethodDefinition() : method; switch (methodInfo.Name) { case nameof(Queryable.Average) when(QueryableMethods.IsAverageWithoutSelector(methodInfo) || QueryableMethods.IsAverageWithSelector(methodInfo)) && source.Selector is SqlExpression averageSqlExpression: var averageInputType = averageSqlExpression.Type; if (averageInputType == typeof(int) || averageInputType == typeof(long)) { averageSqlExpression = _sqlExpressionFactory.ApplyDefaultTypeMapping( _sqlExpressionFactory.Convert(averageSqlExpression, typeof(double))); } return(averageInputType == typeof(float) ? _sqlExpressionFactory.Convert( _sqlExpressionFactory.AggregateFunction( "avg", new[] { averageSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, typeof(double)), averageSqlExpression.Type, averageSqlExpression.TypeMapping) : _sqlExpressionFactory.AggregateFunction( "avg", new[] { averageSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, averageSqlExpression.Type, averageSqlExpression.TypeMapping)); // PostgreSQL COUNT() always returns bigint, so we need to downcast to int case nameof(Queryable.Count) when methodInfo == QueryableMethods.CountWithoutPredicate || methodInfo == QueryableMethods.CountWithPredicate: var countSqlExpression = (source.Selector as SqlExpression) ?? _sqlExpressionFactory.Fragment("*"); return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.AggregateFunction( "count", new[] { countSqlExpression }, nullable: false, argumentsPropagateNullability: FalseArrays[1], source, typeof(long)), typeof(int), _typeMappingSource.FindMapping(typeof(int)))); case nameof(Queryable.LongCount) when methodInfo == QueryableMethods.LongCountWithoutPredicate || methodInfo == QueryableMethods.LongCountWithPredicate: var longCountSqlExpression = (source.Selector as SqlExpression) ?? _sqlExpressionFactory.Fragment("*"); return(_sqlExpressionFactory.AggregateFunction( "count", new[] { longCountSqlExpression }, nullable: false, argumentsPropagateNullability: FalseArrays[1], source, typeof(long))); case nameof(Queryable.Max) when(methodInfo == QueryableMethods.MaxWithoutSelector || methodInfo == QueryableMethods.MaxWithSelector) && source.Selector is SqlExpression maxSqlExpression: return(_sqlExpressionFactory.AggregateFunction( "max", new[] { maxSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, maxSqlExpression.Type, maxSqlExpression.TypeMapping)); case nameof(Queryable.Min) when(methodInfo == QueryableMethods.MinWithoutSelector || methodInfo == QueryableMethods.MinWithSelector) && source.Selector is SqlExpression minSqlExpression: return(_sqlExpressionFactory.AggregateFunction( "min", new[] { minSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, minSqlExpression.Type, minSqlExpression.TypeMapping)); // In PostgreSQL SUM() doesn't return the same type as its argument for smallint, int and bigint. // Cast to get the same type. // http://www.postgresql.org/docs/current/static/functions-aggregate.html case nameof(Queryable.Sum) when(QueryableMethods.IsSumWithoutSelector(methodInfo) || QueryableMethods.IsSumWithSelector(methodInfo)) && source.Selector is SqlExpression sumSqlExpression: var sumInputType = sumSqlExpression.Type; // Note that there is no Sum over short in LINQ if (sumInputType == typeof(int)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.AggregateFunction( "sum", new[] { sumSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, typeof(long)), sumInputType, sumSqlExpression.TypeMapping)); } if (sumInputType == typeof(long)) { return(_sqlExpressionFactory.Convert( _sqlExpressionFactory.AggregateFunction( "sum", new[] { sumSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, typeof(decimal)), sumInputType, sumSqlExpression.TypeMapping)); } return(_sqlExpressionFactory.AggregateFunction( "sum", new[] { sumSqlExpression }, nullable: true, argumentsPropagateNullability: FalseArrays[1], source, sumInputType, sumSqlExpression.TypeMapping)); } } return(null); }