Skip to content

Commit

Permalink
Translate Order & OrderDescending (#34282)
Browse files Browse the repository at this point in the history
Fixes #28634
  • Loading branch information
bricelam authored Aug 13, 2024
1 parent f1fee85 commit 6e71839
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
&& visitedMethodCall.Method.DeclaringType == typeof(Queryable)
&& visitedMethodCall.Method.IsGenericMethod)
{
return TryFlattenGroupJoinSelectMany(visitedMethodCall);
visitedMethodCall = TryNormalizeOrderAndOrderDescending(visitedMethodCall);
visitedMethodCall = TryFlattenGroupJoinSelectMany(visitedMethodCall);

return visitedMethodCall;
}

return visitedExpression;
Expand Down Expand Up @@ -517,6 +520,26 @@ private static bool CanConvertEnumerableToQueryable(Type enumerableType, Type qu
|| enumerableType == typeof(IOrderedEnumerable<>) && queryableType == typeof(IOrderedQueryable<>);
}

private MethodCallExpression TryNormalizeOrderAndOrderDescending(MethodCallExpression methodCallExpression)
{
var genericMethod = methodCallExpression.Method.GetGenericMethodDefinition();
if (genericMethod == QueryableMethods.Order
|| genericMethod == QueryableMethods.OrderDescending)
{
var sourceType = methodCallExpression.Method.GetGenericArguments()[0];
var parameter = Expression.Parameter(sourceType);

return Expression.Call(
genericMethod == QueryableMethods.Order
? QueryableMethods.OrderBy.MakeGenericMethod(sourceType, sourceType)
: QueryableMethods.OrderByDescending.MakeGenericMethod(sourceType, sourceType),
methodCallExpression.Arguments[0],
Expression.Quote(Expression.Lambda(parameter, parameter)));
}

return methodCallExpression;
}

private MethodCallExpression TryFlattenGroupJoinSelectMany(MethodCallExpression methodCallExpression)
{
var genericMethod = methodCallExpression.Method.GetGenericMethodDefinition();
Expand Down
24 changes: 24 additions & 0 deletions src/EFCore/Query/QueryableMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ public static class QueryableMethods
/// </summary>
public static MethodInfo OfType { get; }

/// <summary>
/// The <see cref="MethodInfo" /> for <see cref="Queryable.Order{T}(IQueryable{T})" />
/// </summary>
public static MethodInfo Order { get; }

/// <summary>
/// The <see cref="MethodInfo" /> for <see cref="Queryable.OrderBy{TSource,TKey}(IQueryable{TSource},Expression{Func{TSource,TKey}})" />
/// </summary>
Expand All @@ -253,6 +258,11 @@ public static class QueryableMethods

//public static MethodInfo OrderByDescendingWithComparer { get; }

/// <summary>
/// The <see cref="MethodInfo" /> for <see cref="Queryable.OrderDescending{T}(IQueryable{T})" />
/// </summary>
public static MethodInfo OrderDescending { get; }

/// <summary>
/// The <see cref="MethodInfo" /> for <see cref="Queryable.Reverse{TSource}" />
/// </summary>
Expand Down Expand Up @@ -656,6 +666,13 @@ static QueryableMethods()

OfType = GetMethod(nameof(Queryable.OfType), 1, types => [typeof(IQueryable)]);

Order = GetMethod(
nameof(Queryable.Order), 1,
types =>
[
typeof(IQueryable<>).MakeGenericType(types[0])
]);

OrderBy = GetMethod(
nameof(Queryable.OrderBy), 2,
types =>
Expand All @@ -672,6 +689,13 @@ static QueryableMethods()
typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(types[0], types[1]))
]);

OrderDescending = GetMethod(
nameof(Queryable.OrderDescending), 1,
types =>
[
typeof(IQueryable<>).MakeGenericType(types[0])
]);

Reverse = GetMethod(nameof(Queryable.Reverse), 1, types => [typeof(IQueryable<>).MakeGenericType(types[0])]);

Select = GetMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5055,6 +5055,42 @@ public override Task Static_member_access_gets_parameterized_within_larger_evalu
AssertSql("ReadItem(None, ALFKI)");
});

public override Task Select_Order(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.Select_Order(a);
AssertSql(
"""
SELECT VALUE c["id"]
FROM root c
ORDER BY c["id"]
""");
});

public override Task Select_OrderDescending(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.Select_OrderDescending(a);
AssertSql(
"""
SELECT VALUE c["id"]
FROM root c
ORDER BY c["id"] DESC
""");
});

public override async Task Where_Order_First(bool async)
{
await AssertTranslationFailed(
() => base.Where_Order_First(async));

AssertSql();
}

#region ToPageAsync

[ConditionalFact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ public override Task Collection_navigation_equal_to_null_for_subquery_using_Elem

public override Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(bool async)
=> Task.CompletedTask;

public override Task Where_Order_First(bool async)
// Sequence contains no elements.
=> Assert.ThrowsAsync<InvalidOperationException>(
() => base.Where_Order_First(async));
}
Original file line number Diff line number Diff line change
Expand Up @@ -2496,6 +2496,32 @@ public virtual Task OrderByDescending_ThenByDescending(bool async)
.Select(c => c.City),
assertOrder: true);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_Order(bool async)
=> AssertQuery(
async,
ss => ss.Set<Customer>().Select(c => c.CustomerID).Order(),
ss => ss.Set<Customer>().Select(c => c.CustomerID).Order(StringComparer.Ordinal),
assertOrder: true);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_OrderDescending(bool async)
=> AssertQuery(
async,
ss => ss.Set<Customer>().Select(c => c.CustomerID).OrderDescending(),
ss => ss.Set<Customer>().Select(c => c.CustomerID).OrderDescending(StringComparer.Ordinal),
assertOrder: true);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_Order_First(bool async)
=> AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.Orders.Order().First().OrderID == 10248).Select(c => c.CustomerID),
ss => ss.Set<Customer>().AsEnumerable().Where(c => c.Orders.OrderBy(o => o.OrderID).FirstOrDefault()?.OrderID == 10248).Select(c => c.CustomerID).AsQueryable());

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task OrderBy_ThenBy_Any(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7413,6 +7413,46 @@ FROM [Customers] AS [c]
""");
}

public override async Task Select_Order(bool async)
{
await base.Select_Order(async);

AssertSql(
"""
SELECT [c].[CustomerID]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]
""");
}

public override async Task Select_OrderDescending(bool async)
{
await base.Select_OrderDescending(async);

AssertSql(
"""
SELECT [c].[CustomerID]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID] DESC
""");
}

public override async Task Where_Order_First(bool async)
{
await base.Where_Order_First(async);

AssertSql(
"""
SELECT [c].[CustomerID]
FROM [Customers] AS [c]
WHERE (
SELECT TOP(1) [o].[OrderID]
FROM [Orders] AS [o]
WHERE [c].[CustomerID] = [o].[CustomerID]
ORDER BY [o].[OrderID]) = 10248
""");
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down

0 comments on commit 6e71839

Please sign in to comment.