Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Implementation]: Add Order and OrderDescending to Enumerable and Queryable #70525

Merged
merged 19 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ public static partial class Queryable
public static System.Linq.IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static System.Linq.IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static System.Linq.IOrderedQueryable<T> OrderDescending<T>(this System.Linq.IQueryable<T> source) { throw null; }
public static System.Linq.IOrderedQueryable<T> OrderDescending<T>(this System.Linq.IQueryable<T> source, System.Collections.Generic.IComparer<T> comparer) { throw null; }
public static System.Linq.IOrderedQueryable<T> Order<T>(this System.Linq.IQueryable<T> source) { throw null; }
public static System.Linq.IOrderedQueryable<T> Order<T>(this System.Linq.IQueryable<T> source, System.Collections.Generic.IComparer<T> comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Prepend<TSource>(this System.Linq.IQueryable<TSource> source, TSource element) { throw null; }
public static System.Linq.IQueryable<TSource> Reverse<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static System.Linq.IQueryable<TResult> SelectMany<TSource, TResult>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, System.Collections.Generic.IEnumerable<TResult>>> selector) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,36 @@ public static MethodInfo OfType_TResult_1(Type TResult) =>
(s_OfType_TResult_1 ??= new Func<IQueryable, IQueryable<object>>(Queryable.OfType<object>).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TResult);

private static MethodInfo? s_Order_T_1;

public static MethodInfo Order_T_1(Type T) =>
(s_Order_T_1 ??= new Func<IQueryable<object>, IOrderedQueryable<object>>(Queryable.Order).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(T);

private static MethodInfo? s_Order_T_2;

public static MethodInfo Order_T_2(Type T) =>
(s_Order_T_2 ??= new Func<IQueryable<object>, IComparer<object>, IOrderedQueryable<object>>(Queryable.Order).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(T);

private static MethodInfo? s_OrderBy_TSource_TKey_2;

public static MethodInfo OrderBy_TSource_TKey_2(Type TSource, Type TKey) =>
(s_OrderBy_TSource_TKey_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, IOrderedQueryable<object>>(Queryable.OrderBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);

private static MethodInfo? s_OrderDescending_T_1;

public static MethodInfo OrderDescending_T_1(Type T) =>
(s_OrderDescending_T_1 ??= new Func<IQueryable<object>, IOrderedQueryable<object>>(Queryable.OrderDescending).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(T);

private static MethodInfo? s_OrderDescending_T_2;

public static MethodInfo OrderDescending_T_2(Type T) =>
(s_OrderDescending_T_2 ??= new Func<IQueryable<object>, IComparer<object>, IOrderedQueryable<object>>(Queryable.OrderDescending).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(T);

private static MethodInfo? s_OrderBy_TSource_TKey_3;

public static MethodInfo OrderBy_TSource_TKey_3(Type TSource, Type TKey) =>
Expand Down
150 changes: 150 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,81 @@ public static IQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this
CachedReflectionInfo.GroupJoin_TOuter_TInner_TKey_TResult_6(typeof(TOuter), typeof(TInner), typeof(TKey), typeof(TResult)), outer.Expression, GetSourceExpression(inner), Expression.Quote(outerKeySelector), Expression.Quote(innerKeySelector), Expression.Quote(resultSelector), Expression.Constant(comparer, typeof(IEqualityComparer<TKey>))));
}

/// <summary>
/// Sorts the elements of a sequence in ascending order.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence of values to order.</param>
/// <returns>An <see cref="IOrderedEnumerable{TElement}"/> whose elements are sorted.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method has at least one parameter of type <see cref="Expression{TDelegate}"/> whose type argument is one
/// of the <see cref="Func{T,TResult}"/> types.
/// For these parameters, you can pass in a lambda expression and it will be compiled to an <see cref="Expression{TDelegate}"/>.
///
/// The <see cref="Order{T}(IQueryable{T})"/> method generates a <see cref="MethodCallExpression"/> that represents
/// calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/> itself as a constructed generic method.
/// It then passes the <see cref="MethodCallExpression"/> to the <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> method
/// of the <see cref="IQueryProvider"/> represented by the <see cref="IQueryable.Provider"/> property of the <paramref name="source"/>
/// parameter. The result of calling <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> is cast to
/// type <see cref="IOrderedQueryable{T}"/> and returned.
///
/// The query behavior that occurs as a result of executing an expression tree
/// that represents calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/>
/// depends on the implementation of the <paramref name="source"/> parameter.
/// The expected behavior is that it sorts the elements of <paramref name="source"/> by itself.
/// </remarks>
[DynamicDependency("Order`1", typeof(Enumerable))]
public static IOrderedQueryable<T> Order<T>(this IQueryable<T> source)
{
ArgumentNullException.ThrowIfNull(source);

return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
Expression.Call(
null,
CachedReflectionInfo.Order_T_1(typeof(T)),
source.Expression
));
}

/// <summary>
/// Sorts the elements of a sequence in ascending order.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence of values to order.</param>
/// <param name="comparer">An <see cref="IComparer{T}"/> to compare elements.</param>
/// <returns>An <see cref="IOrderedEnumerable{TElement}"/> whose elements are sorted.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method has at least one parameter of type <see cref="Expression{TDelegate}"/> whose type argument is one
/// of the <see cref="Func{T,TResult}"/> types.
/// For these parameters, you can pass in a lambda expression and it will be compiled to an <see cref="Expression{TDelegate}"/>.
///
/// The <see cref="Order{T}(IQueryable{T})"/> method generates a <see cref="MethodCallExpression"/> that represents
/// calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/> itself as a constructed generic method.
/// It then passes the <see cref="MethodCallExpression"/> to the <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> method
/// of the <see cref="IQueryProvider"/> represented by the <see cref="IQueryable.Provider"/> property of the <paramref name="source"/>
/// parameter. The result of calling <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> is cast to
/// type <see cref="IOrderedQueryable{T}"/> and returned.
///
/// The query behavior that occurs as a result of executing an expression tree
/// that represents calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/>
/// depends on the implementation of the <paramref name="source"/> parameter.
/// The expected behavior is that it sorts the elements of <paramref name="source"/> by itself.
/// </remarks>
[DynamicDependency("Order`1", typeof(Enumerable))]
public static IOrderedQueryable<T> Order<T>(this IQueryable<T> source, IComparer<T> comparer)
{
ArgumentNullException.ThrowIfNull(source);

return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
Expression.Call(
null,
CachedReflectionInfo.Order_T_2(typeof(T)),
source.Expression, Expression.Constant(comparer, typeof(IComparer<T>))
));
}

[DynamicDependency("OrderBy`2", typeof(Enumerable))]
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
Expand Down Expand Up @@ -269,6 +344,81 @@ public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<
));
}

/// <summary>
/// Sorts the elements of a sequence in descending order.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence of values to order.</param>
/// <returns>An <see cref="IOrderedEnumerable{TElement}"/> whose elements are sorted.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method has at least one parameter of type <see cref="Expression{TDelegate}"/> whose type argument is one
/// of the <see cref="Func{T,TResult}"/> types.
/// For these parameters, you can pass in a lambda expression and it will be compiled to an <see cref="Expression{TDelegate}"/>.
///
/// The <see cref="Order{T}(IQueryable{T})"/> method generates a <see cref="MethodCallExpression"/> that represents
/// calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/> itself as a constructed generic method.
/// It then passes the <see cref="MethodCallExpression"/> to the <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> method
/// of the <see cref="IQueryProvider"/> represented by the <see cref="IQueryable.Provider"/> property of the <paramref name="source"/>
/// parameter. The result of calling <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> is cast to
/// type <see cref="IOrderedQueryable{T}"/> and returned.
///
/// The query behavior that occurs as a result of executing an expression tree
/// that represents calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/>
/// depends on the implementation of the <paramref name="source"/> parameter.
/// The expected behavior is that it sorts the elements of <paramref name="source"/> by itself.
/// </remarks>
[DynamicDependency("OrderDescending`1", typeof(Enumerable))]
public static IOrderedQueryable<T> OrderDescending<T>(this IQueryable<T> source)
{
ArgumentNullException.ThrowIfNull(source);

return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
Expression.Call(
null,
CachedReflectionInfo.OrderDescending_T_1(typeof(T)),
source.Expression
));
}

/// <summary>
/// Sorts the elements of a sequence in descending order.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence of values to order.</param>
/// <param name="comparer">An <see cref="IComparer{T}"/> to compare elements.</param>
/// <returns>An <see cref="IOrderedEnumerable{TElement}"/> whose elements are sorted.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method has at least one parameter of type <see cref="Expression{TDelegate}"/> whose type argument is one
/// of the <see cref="Func{T,TResult}"/> types.
/// For these parameters, you can pass in a lambda expression and it will be compiled to an <see cref="Expression{TDelegate}"/>.
///
/// The <see cref="Order{T}(IQueryable{T})"/> method generates a <see cref="MethodCallExpression"/> that represents
/// calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/> itself as a constructed generic method.
/// It then passes the <see cref="MethodCallExpression"/> to the <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> method
/// of the <see cref="IQueryProvider"/> represented by the <see cref="IQueryable.Provider"/> property of the <paramref name="source"/>
/// parameter. The result of calling <see cref="IQueryProvider.CreateQuery{TElement}(Expression)"/> is cast to
/// type <see cref="IOrderedQueryable{T}"/> and returned.
///
/// The query behavior that occurs as a result of executing an expression tree
/// that represents calling <see cref="Enumerable.Order{T}(IEnumerable{T})"/>
/// depends on the implementation of the <paramref name="source"/> parameter.
/// The expected behavior is that it sorts the elements of <paramref name="source"/> by itself.
/// </remarks>
[DynamicDependency("OrderDescending`1", typeof(Enumerable))]
public static IOrderedQueryable<T> OrderDescending<T>(this IQueryable<T> source, IComparer<T> comparer)
{
ArgumentNullException.ThrowIfNull(source);

return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
Expression.Call(
null,
CachedReflectionInfo.OrderDescending_T_2(typeof(T)),
source.Expression, Expression.Constant(comparer, typeof(IComparer<T>))
));
}

[DynamicDependency("OrderByDescending`2", typeof(Enumerable))]
public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
Expand Down
57 changes: 57 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/OrderDescendingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Xunit;

namespace System.Linq.Tests
{
public sealed class OrderDescendingTests : EnumerableBasedTests
{
[Fact]
public void FirstAndLastAreDuplicatesCustomComparer()
{
string[] source = { "Prakash", "Alpha", "DAN", "dan", "Prakash" };
string[] expected = { "Prakash", "Prakash", "DAN", "dan", "Alpha" };

Assert.Equal(expected, source.AsQueryable().OrderDescending(StringComparer.OrdinalIgnoreCase));
}

[Fact]
public void FirstAndLastAreDuplicatesNullPassedAsComparer()
{
int[] source = { 5, 1, 3, 2, 5 };
int[] expected = { 5, 5, 3, 2, 1 };

Assert.Equal(expected, source.AsQueryable().OrderDescending(null));
}

[Fact]
public void NullSource()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.OrderDescending());
}

[Fact]
public void NullSourceComparer()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.OrderDescending(Comparer<int>.Default));
}

[Fact]
public void OrderDescending1()
{
var count = (new int[] { 0, 1, 2 }).AsQueryable().OrderDescending().Count();
Assert.Equal(3, count);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact]
public void OrderDescending2()
{
var count = (new int[] { 0, 1, 2 }).AsQueryable().OrderDescending(Comparer<int>.Default).Count();
Assert.Equal(3, count);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
}
}
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
}
57 changes: 57 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/OrderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Xunit;

namespace System.Linq.Tests
{
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
public sealed class OrderTests : EnumerableBasedTests
{
[Fact]
public void FirstAndLastAreDuplicatesCustomComparer()
{
string[] source = { "Prakash", "Alpha", "dan", "DAN", "Prakash" };
string[] expected = { "Alpha", "dan", "DAN", "Prakash", "Prakash" };

Assert.Equal(expected, source.AsQueryable().Order(StringComparer.OrdinalIgnoreCase));
}

[Fact]
public void FirstAndLastAreDuplicatesNullPassedAsComparer()
{
int[] source = { 5, 1, 3, 2, 5 };
int[] expected = { 1, 2, 3, 5, 5 };

Assert.Equal(expected, source.AsQueryable().Order(null));
}

[Fact]
public void NullSource()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Order());
}

[Fact]
public void NullSourceComparer()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Order(Comparer<int>.Default));
}

[Fact]
public void Order1()
{
var count = (new int[] { 0, 1, 2 }).AsQueryable().Order().Count();
Assert.Equal(3, count);
}

[Fact]
public void Order2()
{
var count = (new int[] { 0, 1, 2 }).AsQueryable().Order(Comparer<int>.Default).Count();
Assert.Equal(3, count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
<Compile Include="OfTypeTests.cs" />
<Compile Include="OrderByDescendingTests.cs" />
<Compile Include="OrderByTests.cs" />
<Compile Include="OrderDescendingTests.cs" />
<Compile Include="OrderTests.cs" />
<Compile Include="Queryable.cs" />
<Compile Include="QueryFromExpressionTests.cs" />
<Compile Include="ReverseTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static void CachedReflectionInfoMethodsNoAnnotations()
.Where(m => m.GetParameters().Length > 0);

// If you are adding a new method to this class, ensure the method meets these requirements
Assert.Equal(131, methods.Count());
Assert.Equal(135, methods.Count());
foreach (MethodInfo method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
Expand Down
Loading