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

Add ToLookupAsync extension methods for IQueryable #13623

Merged
merged 1 commit into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
176 changes: 176 additions & 0 deletions src/EFCore/EntityFrameworkQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2843,6 +2843,182 @@ public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey,

#endregion

#region ToLookup

/// <summary>
/// Creates a <see cref="ILookup{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
/// asynchronously
/// according to a specified key selector function.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source" />.
/// </typeparam>
/// <typeparam name="TKey">
/// The type of the key returned by <paramref name="keySelector" /> .
/// </typeparam>
/// <param name="source">
/// An <see cref="IQueryable{T}" /> to create a <see cref="ILookup{TKey, TValue}" /> from.
/// </param>
/// <param name="keySelector"> A function to extract a key from each element. </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains a <see cref="ILookup{TKey, TSource}" /> that contains selected keys and values.
/// </returns>
public static Task<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(
[NotNull] this IQueryable<TSource> source,
[NotNull] Func<TSource, TKey> keySelector,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
Check.NotNull(keySelector, nameof(keySelector));

return source.AsAsyncEnumerable().ToLookup(keySelector, cancellationToken);
}

/// <summary>
/// Creates a <see cref="ILookup{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
/// asynchronously
/// according to a specified key selector function and a comparer.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source" />.
/// </typeparam>
/// <typeparam name="TKey">
/// The type of the key returned by <paramref name="keySelector" /> .
/// </typeparam>
/// <param name="source">
/// An <see cref="IQueryable{T}" /> to create a <see cref="ILookup{TKey, TValue}" /> from.
/// </param>
/// <param name="keySelector"> A function to extract a key from each element. </param>
/// <param name="comparer">
/// An <see cref="IEqualityComparer{TKey}" /> to compare keys.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains a <see cref="ILookup{TKey, TSource}" /> that contains selected keys and values.
/// </returns>
public static Task<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(
[NotNull] this IQueryable<TSource> source,
[NotNull] Func<TSource, TKey> keySelector,
[NotNull] IEqualityComparer<TKey> comparer,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
Check.NotNull(keySelector, nameof(keySelector));
Check.NotNull(comparer, nameof(comparer));

return source.AsAsyncEnumerable().ToLookup(keySelector, comparer, cancellationToken);
}

/// <summary>
/// Creates a <see cref="ILookup{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
/// asynchronously
/// according to a specified key selector and an element selector function.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source" />.
/// </typeparam>
/// <typeparam name="TKey">
/// The type of the key returned by <paramref name="keySelector" /> .
/// </typeparam>
/// <typeparam name="TElement">
/// The type of the value returned by <paramref name="elementSelector" />.
/// </typeparam>
/// <param name="source">
/// An <see cref="IQueryable{T}" /> to create a <see cref="ILookup{TKey, TValue}" /> from.
/// </param>
/// <param name="keySelector"> A function to extract a key from each element. </param>
/// <param name="elementSelector"> A transform function to produce a result element value from each element. </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains a <see cref="ILookup{TKey, TElement}" /> that contains values of type
/// <typeparamref name="TElement" /> selected from the input sequence.
/// </returns>
public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(
[NotNull] this IQueryable<TSource> source,
[NotNull] Func<TSource, TKey> keySelector,
[NotNull] Func<TSource, TElement> elementSelector,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
Check.NotNull(keySelector, nameof(keySelector));
Check.NotNull(elementSelector, nameof(elementSelector));

return source.AsAsyncEnumerable().ToLookup(keySelector, elementSelector, cancellationToken);
}

/// <summary>
/// Creates a <see cref="ILookup{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
/// asynchronously
/// according to a specified key selector function, a comparer, and an element selector function.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source" />.
/// </typeparam>
/// <typeparam name="TKey">
/// The type of the key returned by <paramref name="keySelector" /> .
/// </typeparam>
/// <typeparam name="TElement">
/// The type of the value returned by <paramref name="elementSelector" />.
/// </typeparam>
/// <param name="source">
/// An <see cref="IQueryable{T}" /> to create a <see cref="ILookup{TKey, TValue}" /> from.
/// </param>
/// <param name="keySelector"> A function to extract a key from each element. </param>
/// <param name="elementSelector"> A transform function to produce a result element value from each element. </param>
/// <param name="comparer">
/// An <see cref="IEqualityComparer{TKey}" /> to compare keys.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains a <see cref="ILookup{TKey, TElement}" /> that contains values of type
/// <typeparamref name="TElement" /> selected from the input sequence.
/// </returns>
public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(
[NotNull] this IQueryable<TSource> source,
[NotNull] Func<TSource, TKey> keySelector,
[NotNull] Func<TSource, TElement> elementSelector,
[NotNull] IEqualityComparer<TKey> comparer,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
Check.NotNull(keySelector, nameof(keySelector));
Check.NotNull(elementSelector, nameof(elementSelector));
Check.NotNull(comparer, nameof(comparer));

return source.AsAsyncEnumerable().ToLookup(keySelector, elementSelector, comparer, cancellationToken);
}

#endregion

#region ForEach

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ public async Task Extension_methods_throw_on_non_async_source()
await SourceNonAsyncEnumerableTest<int>(() => Source().ToDictionaryAsync(e => e, ReferenceEqualityComparer.Instance));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToDictionaryAsync(e => e, e => e, ReferenceEqualityComparer.Instance));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToDictionaryAsync(e => e, e => e, ReferenceEqualityComparer.Instance, new CancellationToken()));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e, e => e));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e, ReferenceEqualityComparer.Instance));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e, ReferenceEqualityComparer.Instance));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e, e => e, ReferenceEqualityComparer.Instance));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToLookupAsync(e => e, e => e, ReferenceEqualityComparer.Instance, new CancellationToken()));
await SourceNonAsyncEnumerableTest<int>(() => Source().ToListAsync());

Assert.Equal(
Expand Down