in Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs [1157:1349]
private static Collection VisitMethodCall(MethodCallExpression inputExpression, TranslationContext context)
{
context.PushMethod(inputExpression);
Type declaringType = inputExpression.Method.DeclaringType;
if ((declaringType != typeof(Queryable)
&& declaringType != typeof(Enumerable) /*LINQ Methods*/
&& declaringType != typeof(CosmosLinqExtensions) /*OrderByRank*/)
|| !inputExpression.Method.IsStatic /*Other extansion method*/)
{
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.OnlyLINQMethodsAreSupported, inputExpression.Method.Name));
}
Type returnType = inputExpression.Method.ReturnType;
Type returnElementType = TypeSystem.GetElementType(returnType);
if (inputExpression.Object != null)
{
throw new DocumentQueryException(ClientResources.ExpectedMethodCallsMethods);
}
Expression inputCollection = inputExpression.Arguments[0]; // all these methods are static extension methods, so argument[0] is the collection
Type inputElementType = TypeSystem.GetElementType(inputCollection.Type);
Collection collection = ExpressionToSql.Translate(inputCollection, context);
context.PushCollection(collection);
Collection result = new Collection(inputExpression.Method.Name);
bool shouldBeOnNewQuery = context.CurrentQuery.ShouldBeOnNewQuery(inputExpression.Method.Name, inputExpression.Arguments.Count);
context.PushSubqueryBinding(shouldBeOnNewQuery);
if (context.LastExpressionIsGroupBy)
{
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, "Group By cannot be followed by other methods"));
}
switch (inputExpression.Method.Name)
{
case LinqMethods.Any:
{
result = new Collection(string.Empty);
if (inputExpression.Arguments.Count == 2)
{
// Any is translated to an SELECT VALUE EXISTS() where Any operation itself is treated as a Where.
SqlWhereClause where = ExpressionToSql.VisitWhere(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddWhereClause(where, context);
}
break;
}
case LinqMethods.Average:
{
SqlSelectClause select = ExpressionToSql.VisitAggregateFunction(inputExpression.Arguments, context, SqlFunctionCallScalarExpression.Names.Avg);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.Count:
{
SqlSelectClause select = ExpressionToSql.VisitCount(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.Distinct:
{
SqlSelectClause select = ExpressionToSql.VisitDistinct(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.FirstOrDefault:
{
if (inputExpression.Arguments.Count == 1)
{
// TOP is not allowed when OFFSET ... LIMIT is present.
if (!context.CurrentQuery.HasOffsetSpec())
{
SqlNumberLiteral sqlNumberLiteral = SqlNumberLiteral.Create(1);
SqlTopSpec topSpec = SqlTopSpec.Create(sqlNumberLiteral);
context.CurrentQuery = context.CurrentQuery.AddTopSpec(topSpec);
}
context.SetClientOperation(ScalarOperationKind.FirstOrDefault);
}
else
{
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.InvalidArgumentsCount, inputExpression.Method.Name, 0, inputExpression.Arguments.Count - 1));
}
break;
}
case LinqMethods.Max:
{
SqlSelectClause select = ExpressionToSql.VisitAggregateFunction(inputExpression.Arguments, context, SqlFunctionCallScalarExpression.Names.Max);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.Min:
{
SqlSelectClause select = ExpressionToSql.VisitAggregateFunction(inputExpression.Arguments, context, SqlFunctionCallScalarExpression.Names.Min);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.GroupBy:
{
context.CurrentQuery = context.PackageCurrentQueryIfNeccessary();
result = ExpressionToSql.VisitGroupBy(returnElementType, inputExpression.Arguments, context);
context.LastExpressionIsGroupBy = true;
break;
}
case LinqMethods.OrderBy:
{
SqlOrderByClause orderBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, false, context);
context.CurrentQuery = context.CurrentQuery.AddOrderByClause(orderBy, context);
break;
}
case LinqMethods.OrderByDescending:
{
SqlOrderByClause orderBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, true, context);
context.CurrentQuery = context.CurrentQuery.AddOrderByClause(orderBy, context);
break;
}
case nameof(CosmosLinqExtensions.OrderByRank):
{
SqlOrderByClause orderBy = ExpressionToSql.VisitOrderByRank(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddOrderByClause(orderBy, context);
break;
}
case LinqMethods.Select:
{
SqlSelectClause select = ExpressionToSql.VisitSelect(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.SelectMany:
{
context.CurrentQuery = context.PackageCurrentQueryIfNeccessary();
result = ExpressionToSql.VisitSelectMany(inputExpression.Arguments, context);
break;
}
case LinqMethods.Skip:
{
SqlOffsetSpec offsetSpec = ExpressionToSql.VisitSkip(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddOffsetSpec(offsetSpec, context);
break;
}
case LinqMethods.Sum:
{
SqlSelectClause select = ExpressionToSql.VisitAggregateFunction(inputExpression.Arguments, context, SqlFunctionCallScalarExpression.Names.Sum);
context.CurrentQuery = context.CurrentQuery.AddSelectClause(select, context);
break;
}
case LinqMethods.Take:
{
if (context.CurrentQuery.HasOffsetSpec())
{
SqlLimitSpec limitSpec = ExpressionToSql.VisitTakeLimit(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddLimitSpec(limitSpec, context);
}
else
{
SqlTopSpec topSpec = ExpressionToSql.VisitTakeTop(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddTopSpec(topSpec);
}
break;
}
case LinqMethods.ThenBy:
{
SqlOrderByClause thenBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, false, context);
context.CurrentQuery = context.CurrentQuery.UpdateOrderByClause(thenBy, context);
break;
}
case LinqMethods.ThenByDescending:
{
SqlOrderByClause thenBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, true, context);
context.CurrentQuery = context.CurrentQuery.UpdateOrderByClause(thenBy, context);
break;
}
case LinqMethods.Where:
{
SqlWhereClause where = ExpressionToSql.VisitWhere(inputExpression.Arguments, context);
context.CurrentQuery = context.CurrentQuery.AddWhereClause(where, context);
break;
}
default:
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.MethodNotSupported, inputExpression.Method.Name));
}
context.PopSubqueryBinding();
context.PopCollection();
context.PopMethod();
return result;
}