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));
                            }
예제 #2
0
        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");
        }
예제 #3
0
    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);
    }