diff --git a/src/EFCore/EntityFrameworkEnumerableExtensions.cs b/src/EFCore/EntityFrameworkEnumerableExtensions.cs deleted file mode 100644 index 574dcfee82c..00000000000 --- a/src/EFCore/EntityFrameworkEnumerableExtensions.cs +++ /dev/null @@ -1,440 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Extensions.Internal; -using Microsoft.EntityFrameworkCore.Utilities; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore -{ - public static class EntityFrameworkEnumerableExtensions - { - #region ToList/Array - - /// - /// Asynchronously creates a from an by enumerating it - /// asynchronously. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// An to create a list from. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains elements from the input sequence. - /// - public static async Task> ToListAsync( - [NotNull] this IAsyncEnumerable source, - CancellationToken cancellationToken = default) - { - var list = new List(); - await foreach (var element in source.WithCancellation(cancellationToken)) - { - list.Add(element); - } - - return list; - } - - /// - /// Asynchronously creates an array from an by enumerating it asynchronously. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// An to create an array from. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains an array that contains elements from the input sequence. - /// - public static async Task ToArrayAsync( - [NotNull] this IAsyncEnumerable source, - CancellationToken cancellationToken = default) - => (await source.ToListAsync(cancellationToken)).ToArray(); - - #endregion - - #region ToDictionary - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToDictionaryAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - CancellationToken cancellationToken = default) - => ToDictionaryAsync(source, keySelector, e => e, comparer: null, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function and a comparer. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToDictionaryAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - => ToDictionaryAsync(source, keySelector, e => e, comparer, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static Task> ToDictionaryAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - CancellationToken cancellationToken = default) - => ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function, a comparer, and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static async Task> ToDictionaryAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - Check.NotNull(comparer, nameof(comparer)); - - var d = new Dictionary(comparer); - await foreach (var element in source.WithCancellation(cancellationToken)) - { - d.Add(keySelector(element), elementSelector(element)); - } - - return d; - } - - #endregion - - #region ToLookup - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToLookupAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - CancellationToken cancellationToken = default) - => ToLookupAsync(source, keySelector, e => e, comparer: null, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function and a comparer. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToLookupAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - => ToLookupAsync(source, keySelector, e => e, comparer, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static Task> ToLookupAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - CancellationToken cancellationToken = default) - => ToLookupAsync(source, keySelector, elementSelector, comparer: null, cancellationToken); - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function, a comparer, and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static Task> ToLookupAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - Check.NotNull(comparer, nameof(comparer)); - - throw new NotImplementedException("ToLookupAsync"); - } - - #endregion - - #region ForEach - - /// - /// Asynchronously enumerates the query results and performs the specified action on each element. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// An to enumerate. - /// - /// The action to perform on each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// A task that represents the asynchronous operation. - public static async Task ForEachAsync( - [NotNull] this IAsyncEnumerable source, - [NotNull] Action action, - CancellationToken cancellationToken = default) - { - Check.NotNull(action, nameof(action)); - - await foreach (var element in source.WithCancellation(cancellationToken)) - { - action(element); - } - } - - #endregion - } -} diff --git a/src/EFCore/EntityFrameworkQueryableExtensions.cs b/src/EFCore/EntityFrameworkQueryableExtensions.cs index 7aba45cc4bd..aa007437677 100644 --- a/src/EFCore/EntityFrameworkQueryableExtensions.cs +++ b/src/EFCore/EntityFrameworkQueryableExtensions.cs @@ -2125,10 +2125,18 @@ public static Task ContainsAsync( /// A task that represents the asynchronous operation. /// The task result contains a that contains elements from the input sequence. /// - public static Task> ToListAsync( + public static async Task> ToListAsync( [NotNull] this IQueryable source, CancellationToken cancellationToken = default) - => source.AsAsyncEnumerable().ToListAsync(cancellationToken); + { + var list = new List(); + await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken)) + { + list.Add(element); + } + + return list; + } /// /// Asynchronously creates an array from an by enumerating it asynchronously. @@ -2150,10 +2158,10 @@ public static Task> ToListAsync( /// A task that represents the asynchronous operation. /// The task result contains an array that contains elements from the input sequence. /// - public static Task ToArrayAsync( + public static async Task ToArrayAsync( [NotNull] this IQueryable source, CancellationToken cancellationToken = default) - => source.AsAsyncEnumerable().ToArrayAsync(cancellationToken); + => (await source.ToListAsync(cancellationToken)).ToArray(); #endregion @@ -2728,12 +2736,7 @@ public static Task> ToDictionaryAsync( [NotNull] this IQueryable source, [NotNull] Func keySelector, CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - - return source.AsAsyncEnumerable().ToDictionaryAsync(keySelector, cancellationToken); - } + => ToDictionaryAsync(source, keySelector, e => e, comparer: null, cancellationToken); /// /// Creates a from an by enumerating it @@ -2769,13 +2772,7 @@ public static Task> ToDictionaryAsync( [NotNull] Func keySelector, [NotNull] IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(comparer, nameof(comparer)); - - return source.AsAsyncEnumerable().ToDictionaryAsync(keySelector, comparer, cancellationToken); - } + => ToDictionaryAsync(source, keySelector, e => e, comparer, cancellationToken); /// /// Creates a from an by enumerating it @@ -2813,13 +2810,7 @@ public static Task> ToDictionaryAsync keySelector, [NotNull] Func elementSelector, CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - - return source.AsAsyncEnumerable().ToDictionaryAsync(keySelector, elementSelector, cancellationToken); - } + => ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken); /// /// Creates a from an by enumerating it @@ -2855,7 +2846,7 @@ public static Task> ToDictionaryAsync that contains values of type /// selected from the input sequence. /// - public static Task> ToDictionaryAsync( + public static async Task> ToDictionaryAsync( [NotNull] this IQueryable source, [NotNull] Func keySelector, [NotNull] Func elementSelector, @@ -2865,185 +2856,14 @@ public static Task> ToDictionaryAsync - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToLookupAsync( - [NotNull] this IQueryable source, - [NotNull] Func keySelector, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - - return source.AsAsyncEnumerable().ToLookupAsync(keySelector, cancellationToken); - } - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function and a comparer. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains selected keys and values. - /// - public static Task> ToLookupAsync( - [NotNull] this IQueryable source, - [NotNull] Func keySelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(comparer, nameof(comparer)); - - return source.AsAsyncEnumerable().ToLookupAsync(keySelector, comparer, cancellationToken); - } - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static Task> ToLookupAsync( - [NotNull] this IQueryable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - - return source.AsAsyncEnumerable().ToLookupAsync(keySelector, elementSelector, cancellationToken); - } - - /// - /// Creates a from an by enumerating it - /// asynchronously - /// according to a specified key selector function, a comparer, and an element selector function. - /// - /// - /// 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. - /// - /// - /// The type of the elements of . - /// - /// - /// The type of the key returned by . - /// - /// - /// The type of the value returned by . - /// - /// - /// An to create a from. - /// - /// A function to extract a key from each element. - /// A transform function to produce a result element value from each element. - /// - /// An to compare keys. - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// - /// A task that represents the asynchronous operation. - /// The task result contains a that contains values of type - /// selected from the input sequence. - /// - public static Task> ToLookupAsync( - [NotNull] this IQueryable source, - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(source, nameof(source)); - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - Check.NotNull(comparer, nameof(comparer)); + var d = new Dictionary(comparer); + await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken)) + { + d.Add(keySelector(element), elementSelector(element)); + } - return source.AsAsyncEnumerable().ToLookupAsync(keySelector, elementSelector, comparer, cancellationToken); + return d; } #endregion @@ -3068,11 +2888,18 @@ public static Task> ToLookupAsync to observe while waiting for the task to complete. /// /// A task that represents the asynchronous operation. - public static Task ForEachAsync( + public static async Task ForEachAsync( [NotNull] this IQueryable source, [NotNull] Action action, CancellationToken cancellationToken = default) - => source.AsAsyncEnumerable().ForEachAsync(action, cancellationToken); + { + Check.NotNull(action, nameof(action)); + + await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken)) + { + action(element); + } + } #endregion diff --git a/src/EFCore/Query/Internal/QueryCompiler.cs b/src/EFCore/Query/Internal/QueryCompiler.cs index 7c717864fa0..71402a6ad49 100644 --- a/src/EFCore/Query/Internal/QueryCompiler.cs +++ b/src/EFCore/Query/Internal/QueryCompiler.cs @@ -105,7 +105,7 @@ public virtual Func CompileQueryCore( IModel model, bool async) { - return database.CompileQuery2(query, async); + return database.CompileQuery(query, async); } /// diff --git a/src/EFCore/Storage/Database.cs b/src/EFCore/Storage/Database.cs index 42af355f022..753397d9bb9 100644 --- a/src/EFCore/Storage/Database.cs +++ b/src/EFCore/Storage/Database.cs @@ -67,7 +67,7 @@ public abstract Task SaveChangesAsync( IList entries, CancellationToken cancellationToken = default); - public virtual Func CompileQuery2(Expression query, bool async) + public virtual Func CompileQuery(Expression query, bool async) { return Dependencies.QueryCompilationContextFactory .Create(async) diff --git a/src/EFCore/Storage/IDatabase.cs b/src/EFCore/Storage/IDatabase.cs index 62518de7c16..eb5727c3385 100644 --- a/src/EFCore/Storage/IDatabase.cs +++ b/src/EFCore/Storage/IDatabase.cs @@ -50,6 +50,6 @@ Task SaveChangesAsync( [NotNull] IList entries, CancellationToken cancellationToken = default); - Func CompileQuery2([NotNull] Expression query, bool async); + Func CompileQuery([NotNull] Expression query, bool async); } } diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 1546e65a3f5..e93957540c5 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -698,11 +698,6 @@ await AssertQuery( await AssertQuery( isAsync, gs => gs.Where(g => ((short)g.Rank & (short)1) == 1)); - - // Issue#15950 - //await AssertQuery( - // isAsync, - // gs => gs.Where(g => ((char)g.Rank & '\x0001') == '\x0001')); } [ConditionalTheory] diff --git a/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs b/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs index dd5446f7348..2194486dfee 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs @@ -18,5 +18,16 @@ public static Task> ToListAsync(this IQueryable source, C { return ((IQueryable)source).ToListAsync(cancellationToken); } + + public static async Task> ToListAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + var list = new List(); + await foreach (var element in source.WithCancellation(cancellationToken)) + { + list.Add(element); + } + + return list; + } } } diff --git a/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs b/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs index 0b6fb4bca6e..c480f8819b3 100644 --- a/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs +++ b/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs @@ -305,13 +305,6 @@ public async Task Extension_methods_throw_on_non_async_source() await SourceNonAsyncEnumerableTest(() => Source().ToDictionaryAsync(e => e, e => e, ReferenceEqualityComparer.Instance)); await SourceNonAsyncEnumerableTest( () => Source().ToDictionaryAsync(e => e, e => e, ReferenceEqualityComparer.Instance, new CancellationToken())); - await SourceNonAsyncEnumerableTest(() => Source().ToLookupAsync(e => e)); - await SourceNonAsyncEnumerableTest(() => Source().ToLookupAsync(e => e, e => e)); - await SourceNonAsyncEnumerableTest(() => Source().ToLookupAsync(e => e, ReferenceEqualityComparer.Instance)); - await SourceNonAsyncEnumerableTest(() => Source().ToLookupAsync(e => e, ReferenceEqualityComparer.Instance)); - await SourceNonAsyncEnumerableTest(() => Source().ToLookupAsync(e => e, e => e, ReferenceEqualityComparer.Instance)); - await SourceNonAsyncEnumerableTest( - () => Source().ToLookupAsync(e => e, e => e, ReferenceEqualityComparer.Instance, new CancellationToken())); await SourceNonAsyncEnumerableTest(() => Source().ToListAsync()); Assert.Equal(