diff --git a/benchmark/EFCore.SqlServer.Benchmarks/EFCore.SqlServer.Benchmarks.csproj b/benchmark/EFCore.SqlServer.Benchmarks/EFCore.SqlServer.Benchmarks.csproj index c8e2081f22a..c1884b89fd2 100644 --- a/benchmark/EFCore.SqlServer.Benchmarks/EFCore.SqlServer.Benchmarks.csproj +++ b/benchmark/EFCore.SqlServer.Benchmarks/EFCore.SqlServer.Benchmarks.csproj @@ -2,6 +2,7 @@ net461;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0 + netcoreapp3.0 Microsoft.EntityFrameworkCore.Benchmarks Exe diff --git a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj index e5f227bdcf8..858477ef9c7 100644 --- a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj +++ b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj @@ -2,6 +2,7 @@ net461;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0 + netcoreapp3.0 Microsoft.EntityFrameworkCore.Benchmarks Exe diff --git a/src/EFCore.Abstractions/EFCore.Abstractions.csproj b/src/EFCore.Abstractions/EFCore.Abstractions.csproj index 9eaa6bdf25b..eac661300e4 100644 --- a/src/EFCore.Abstractions/EFCore.Abstractions.csproj +++ b/src/EFCore.Abstractions/EFCore.Abstractions.csproj @@ -2,7 +2,7 @@ Provides abstractions and attributes that are used to configure Entity Framework Core - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.Abstractions Microsoft.EntityFrameworkCore diff --git a/src/EFCore.Cosmos/EFCore.Cosmos.csproj b/src/EFCore.Cosmos/EFCore.Cosmos.csproj index b0a0ad17f0d..44922d8609e 100644 --- a/src/EFCore.Cosmos/EFCore.Cosmos.csproj +++ b/src/EFCore.Cosmos/EFCore.Cosmos.csproj @@ -2,7 +2,7 @@ Azure Cosmos provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.Cosmos Microsoft.EntityFrameworkCore.Cosmos diff --git a/src/EFCore.Cosmos/Query/Expressions/Internal/QueryShaperExpression.cs b/src/EFCore.Cosmos/Query/Expressions/Internal/QueryShaperExpression.cs index 5ee58ebba89..0dd2ed4701f 100644 --- a/src/EFCore.Cosmos/Query/Expressions/Internal/QueryShaperExpression.cs +++ b/src/EFCore.Cosmos/Query/Expressions/Internal/QueryShaperExpression.cs @@ -82,23 +82,28 @@ public AsyncShaperEnumerable( _shaper = shaper; } - public IAsyncEnumerator GetEnumerator() => new AsyncShaperEnumerator(this); + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new AsyncShaperEnumerator(this, cancellationToken); + } private class AsyncShaperEnumerator : IAsyncEnumerator { private readonly IAsyncEnumerator _enumerator; private readonly Func _shaper; - public AsyncShaperEnumerator(AsyncShaperEnumerable enumerable) + public AsyncShaperEnumerator(AsyncShaperEnumerable enumerable, CancellationToken cancellationToken) { - _enumerator = enumerable._innerEnumerable.GetEnumerator(); + _enumerator = enumerable._innerEnumerable.GetAsyncEnumerator(cancellationToken); _shaper = enumerable._shaper; } + public T Current { get; private set; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public async Task MoveNext(CancellationToken cancellationToken) + public async ValueTask MoveNextAsync() { - if (!await _enumerator.MoveNext(cancellationToken)) + if (!await _enumerator.MoveNextAsync()) { Current = default; return false; @@ -108,9 +113,12 @@ public async Task MoveNext(CancellationToken cancellationToken) return true; } - public T Current { get; private set; } + public ValueTask DisposeAsync() + { + _enumerator.DisposeAsync(); - public void Dispose() => _enumerator.Dispose(); + return default; + } } } diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs index a473feb763b..db36cef614a 100644 --- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs @@ -428,8 +428,8 @@ public DocumentAsyncEnumerable( _cosmosSqlQuery = cosmosSqlQuery; } - public IAsyncEnumerator GetEnumerator() => new AsyncEnumerator(this); - + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => new AsyncEnumerator(this, cancellationToken); private class AsyncEnumerator : IAsyncEnumerator { private CosmosResultSetIterator _query; @@ -439,20 +439,23 @@ private class AsyncEnumerator : IAsyncEnumerator private readonly CosmosClientWrapper _cosmosClient; private readonly string _containerId; private readonly CosmosSqlQuery _cosmosSqlQuery; + private readonly CancellationToken _cancellationToken; - public AsyncEnumerator(DocumentAsyncEnumerable documentEnumerable) + public AsyncEnumerator(DocumentAsyncEnumerable documentEnumerable, CancellationToken cancellationToken) { _cosmosClient = documentEnumerable._cosmosClient; _containerId = documentEnumerable._containerId; _cosmosSqlQuery = documentEnumerable._cosmosSqlQuery; + _cancellationToken = cancellationToken; } public JObject Current { get; private set; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public async Task MoveNext(CancellationToken cancellationToken) + public async ValueTask MoveNextAsync() { - cancellationToken.ThrowIfCancellationRequested(); + _cancellationToken.ThrowIfCancellationRequested(); if (_jsonReader == null) { @@ -467,7 +470,7 @@ public async Task MoveNext(CancellationToken cancellationToken) return false; } - _responseStream = (await _query.FetchNextSetAsync(cancellationToken)).Content; + _responseStream = (await _query.FetchNextSetAsync(_cancellationToken)).Content; _reader = new StreamReader(_responseStream); _jsonReader = new JsonTextReader(_reader); @@ -510,10 +513,10 @@ public async Task MoveNext(CancellationToken cancellationToken) _reader = null; _responseStream.Dispose(); _responseStream = null; - return await MoveNext(cancellationToken); + return await MoveNextAsync(); } - public void Dispose() + public ValueTask DisposeAsync() { _jsonReader?.Close(); _jsonReader = null; @@ -521,9 +524,9 @@ public void Dispose() _reader = null; _responseStream?.Dispose(); _responseStream = null; - } - public void Reset() => throw new NotImplementedException(); + return default; + } } } diff --git a/src/EFCore.Design/EFCore.Design.csproj b/src/EFCore.Design/EFCore.Design.csproj index 5c615c3bbf8..8b274c913d0 100644 --- a/src/EFCore.Design/EFCore.Design.csproj +++ b/src/EFCore.Design/EFCore.Design.csproj @@ -2,7 +2,7 @@ Shared design-time components for Entity Framework Core tools. - netstandard2.0 + netstandard2.1 Microsoft.EntityFrameworkCore.Design Microsoft.EntityFrameworkCore true @@ -20,7 +20,7 @@ build - + diff --git a/src/EFCore.InMemory/EFCore.InMemory.csproj b/src/EFCore.InMemory/EFCore.InMemory.csproj index 23f40b2f7f1..d5fa1fe00df 100644 --- a/src/EFCore.InMemory/EFCore.InMemory.csproj +++ b/src/EFCore.InMemory/EFCore.InMemory.csproj @@ -2,7 +2,7 @@ In-memory database provider for Entity Framework Core (to be used for testing purposes). - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.InMemory Microsoft.EntityFrameworkCore.InMemory diff --git a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs index d386fe407de..b1df74189f1 100644 --- a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpressionVisitor.cs @@ -232,7 +232,8 @@ public AsyncQueryingEnumerable( _logger = logger; } - public IAsyncEnumerator GetEnumerator() => new AsyncEnumerator(this); + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => new AsyncEnumerator(this, cancellationToken); private sealed class AsyncEnumerator : IAsyncEnumerator { @@ -242,21 +243,23 @@ private sealed class AsyncEnumerator : IAsyncEnumerator private readonly Func, Task> _shaper; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; + private readonly CancellationToken _cancellationToken; - public AsyncEnumerator(AsyncQueryingEnumerable asyncQueryingEnumerable) + public AsyncEnumerator( + AsyncQueryingEnumerable asyncQueryingEnumerable, + CancellationToken cancellationToken) { _queryContext = asyncQueryingEnumerable._queryContext; _innerEnumerable = asyncQueryingEnumerable._innerEnumerable; _shaper = asyncQueryingEnumerable._shaper; _contextType = asyncQueryingEnumerable._contextType; _logger = asyncQueryingEnumerable._logger; + _cancellationToken = cancellationToken; } public T Current { get; private set; } - public void Dispose() => _enumerator?.Dispose(); - - public async Task MoveNext(CancellationToken cancellationToken) + public async ValueTask MoveNextAsync() { try { @@ -280,6 +283,13 @@ public async Task MoveNext(CancellationToken cancellationToken) throw; } } + + public ValueTask DisposeAsync() + { + _enumerator?.Dispose(); + + return default; + } } } diff --git a/src/EFCore.Proxies/EFCore.Proxies.csproj b/src/EFCore.Proxies/EFCore.Proxies.csproj index a2b35c3ee20..d7f9bd0f8bf 100644 --- a/src/EFCore.Proxies/EFCore.Proxies.csproj +++ b/src/EFCore.Proxies/EFCore.Proxies.csproj @@ -2,7 +2,7 @@ Lazy-loading proxies for EF Core. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.Proxies Microsoft.EntityFrameworkCore diff --git a/src/EFCore.Relational/EFCore.Relational.csproj b/src/EFCore.Relational/EFCore.Relational.csproj index e608c8c3804..b97ea3461b7 100644 --- a/src/EFCore.Relational/EFCore.Relational.csproj +++ b/src/EFCore.Relational/EFCore.Relational.csproj @@ -2,7 +2,7 @@ Shared Entity Framework Core components for relational database providers. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.Relational Microsoft.EntityFrameworkCore diff --git a/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs index c5d18b20d33..85f47fb6919 100644 --- a/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs @@ -46,7 +46,8 @@ public AsyncQueryingEnumerable( _logger = logger; } - public IAsyncEnumerator GetEnumerator() => new AsyncEnumerator(this); + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => new AsyncEnumerator(this, cancellationToken); private sealed class AsyncEnumerator : IAsyncEnumerator { @@ -60,8 +61,11 @@ private sealed class AsyncEnumerator : IAsyncEnumerator private readonly IDiagnosticsLogger _logger; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; + private readonly CancellationToken _cancellationToken; - public AsyncEnumerator(AsyncQueryingEnumerable queryingEnumerable) + public AsyncEnumerator( + AsyncQueryingEnumerable queryingEnumerable, + CancellationToken cancellationToken) { _relationalQueryContext = queryingEnumerable._relationalQueryContext; _shaper = queryingEnumerable._shaper; @@ -71,24 +75,18 @@ public AsyncEnumerator(AsyncQueryingEnumerable queryingEnumerable) _logger = queryingEnumerable._logger; _sqlExpressionFactory = queryingEnumerable._sqlExpressionFactory; _parameterNameGeneratorFactory = queryingEnumerable._parameterNameGeneratorFactory; + _cancellationToken = cancellationToken; } public T Current { get; private set; } - public void Dispose() - { - _dataReader?.Dispose(); - _dataReader = null; - _relationalQueryContext.Connection.Close(); - } - - public async Task MoveNext(CancellationToken cancellationToken) + public async ValueTask MoveNextAsync() { try { if (_dataReader == null) { - await _relationalQueryContext.Connection.OpenAsync(cancellationToken); + await _relationalQueryContext.Connection.OpenAsync(_cancellationToken); try { @@ -104,7 +102,7 @@ public async Task MoveNext(CancellationToken cancellationToken) _relationalQueryContext.Connection, _relationalQueryContext.ParameterValues, _relationalQueryContext.CommandLogger, - cancellationToken); + _cancellationToken); _resultCoordinator = new ResultCoordinator(); } @@ -118,7 +116,7 @@ public async Task MoveNext(CancellationToken cancellationToken) } } - var hasNext = _resultCoordinator.HasNext ?? await _dataReader.ReadAsync(cancellationToken); + var hasNext = _resultCoordinator.HasNext ?? await _dataReader.ReadAsync(_cancellationToken); _resultCoordinator.HasNext = null; Current @@ -135,6 +133,15 @@ public async Task MoveNext(CancellationToken cancellationToken) throw; } } + + public ValueTask DisposeAsync() + { + _dataReader?.Dispose(); + _dataReader = null; + _relationalQueryContext.Connection.Close(); + + return default; + } } } } diff --git a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs index bf9bbfce221..ebfbc287d9c 100644 --- a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs @@ -47,7 +47,8 @@ public FromSqlNonComposedAsyncQueryingEnumerable( _logger = logger; } - public IAsyncEnumerator GetEnumerator() => new AsyncEnumerator(this); + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => new AsyncEnumerator(this, cancellationToken); private sealed class AsyncEnumerator : IAsyncEnumerator { @@ -61,8 +62,11 @@ private sealed class AsyncEnumerator : IAsyncEnumerator private readonly IDiagnosticsLogger _logger; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; + private readonly CancellationToken _cancellationToken; - public AsyncEnumerator(FromSqlNonComposedAsyncQueryingEnumerable queryingEnumerable) + public AsyncEnumerator( + FromSqlNonComposedAsyncQueryingEnumerable queryingEnumerable, + CancellationToken cancellationToken) { _relationalQueryContext = queryingEnumerable._relationalQueryContext; _shaper = queryingEnumerable._shaper; @@ -72,19 +76,12 @@ public AsyncEnumerator(FromSqlNonComposedAsyncQueryingEnumerable queryingEnum _logger = queryingEnumerable._logger; _sqlExpressionFactory = queryingEnumerable._sqlExpressionFactory; _parameterNameGeneratorFactory = queryingEnumerable._parameterNameGeneratorFactory; + _cancellationToken = cancellationToken; } public T Current { get; private set; } - - public void Dispose() - { - _dataReader?.Dispose(); - _dataReader = null; - _relationalQueryContext.Connection.Close(); - } - - public async Task MoveNext(CancellationToken cancellationToken) + public async ValueTask MoveNextAsync() { try { @@ -108,7 +105,7 @@ public async Task MoveNext(CancellationToken cancellationToken) _relationalQueryContext.Connection, _relationalQueryContext.ParameterValues, _relationalQueryContext.CommandLogger, - cancellationToken); + _cancellationToken); var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) .Select( @@ -153,7 +150,7 @@ var readerColumn } } - var hasNext = await _dataReader.ReadAsync(cancellationToken); + var hasNext = await _dataReader.ReadAsync(_cancellationToken); Current = hasNext @@ -170,7 +167,14 @@ var readerColumn } } - public void Reset() => throw new NotImplementedException(); + public ValueTask DisposeAsync() + { + _dataReader?.Dispose(); + _dataReader = null; + _relationalQueryContext.Connection.Close(); + + return default; + } } } } diff --git a/src/EFCore.SqlServer.NTS/EFCore.SqlServer.NTS.csproj b/src/EFCore.SqlServer.NTS/EFCore.SqlServer.NTS.csproj index b625680c3e2..5d9c9d58c0b 100644 --- a/src/EFCore.SqlServer.NTS/EFCore.SqlServer.NTS.csproj +++ b/src/EFCore.SqlServer.NTS/EFCore.SqlServer.NTS.csproj @@ -2,7 +2,7 @@ NetTopologySuite support for the Microsoft SQL Server database provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite Microsoft.EntityFrameworkCore.SqlServer diff --git a/src/EFCore.SqlServer/EFCore.SqlServer.csproj b/src/EFCore.SqlServer/EFCore.SqlServer.csproj index 35dfa22de64..73cf7177bb2 100644 --- a/src/EFCore.SqlServer/EFCore.SqlServer.csproj +++ b/src/EFCore.SqlServer/EFCore.SqlServer.csproj @@ -2,7 +2,7 @@ Microsoft SQL Server database provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.SqlServer diff --git a/src/EFCore.Sqlite.Core/EFCore.Sqlite.Core.csproj b/src/EFCore.Sqlite.Core/EFCore.Sqlite.Core.csproj index ec3db282304..f8cec9a9c03 100644 --- a/src/EFCore.Sqlite.Core/EFCore.Sqlite.Core.csproj +++ b/src/EFCore.Sqlite.Core/EFCore.Sqlite.Core.csproj @@ -5,7 +5,7 @@ Microsoft.EntityFrameworkCore.Sqlite.Core Microsoft.EntityFrameworkCore.Sqlite SQLite database provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 true $(PackageTags);SQLite diff --git a/src/EFCore.Sqlite.NTS/EFCore.Sqlite.NTS.csproj b/src/EFCore.Sqlite.NTS/EFCore.Sqlite.NTS.csproj index 85d935382b8..215f5642b92 100644 --- a/src/EFCore.Sqlite.NTS/EFCore.Sqlite.NTS.csproj +++ b/src/EFCore.Sqlite.NTS/EFCore.Sqlite.NTS.csproj @@ -5,7 +5,7 @@ Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite Microsoft.EntityFrameworkCore.Sqlite NetTopologySuite support for the SQLite database provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 true $(PackageTags);SQLite;GIS;NTS;OGC;SpatiaLite diff --git a/src/EFCore.Sqlite/EFCore.Sqlite.csproj b/src/EFCore.Sqlite/EFCore.Sqlite.csproj index 3a5829057f8..0163f33a067 100644 --- a/src/EFCore.Sqlite/EFCore.Sqlite.csproj +++ b/src/EFCore.Sqlite/EFCore.Sqlite.csproj @@ -4,7 +4,7 @@ SQLite database provider for Entity Framework Core. - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore.Sqlite $(PackageTags);SQLite diff --git a/src/EFCore.Sqlite/lib/netstandard2.0/_._ b/src/EFCore.Sqlite/lib/netstandard2.1/_._ similarity index 100% rename from src/EFCore.Sqlite/lib/netstandard2.0/_._ rename to src/EFCore.Sqlite/lib/netstandard2.1/_._ diff --git a/src/EFCore.Tools/lib/netstandard2.0/_._ b/src/EFCore.Tools/lib/netstandard2.1/_._ similarity index 100% rename from src/EFCore.Tools/lib/netstandard2.0/_._ rename to src/EFCore.Tools/lib/netstandard2.1/_._ diff --git a/src/EFCore/EF.CompileAsyncQuery.cs b/src/EFCore/EF.CompileAsyncQuery.cs index 4adc770ac5b..2af88eef194 100644 --- a/src/EFCore/EF.CompileAsyncQuery.cs +++ b/src/EFCore/EF.CompileAsyncQuery.cs @@ -2,6 +2,7 @@ // 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.Linq.Expressions; using System.Threading; @@ -22,7 +23,7 @@ public static partial class EF /// The query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery( + public static Func> CompileAsyncQuery( [NotNull] Expression>> queryExpression) where TContext : DbContext where TResult : class @@ -36,7 +37,7 @@ public static Func> CompileAsyncQueryThe LINQ query expression. /// A delegate that can be invoked to execute the compiled query. [Obsolete("Use DbSet instead")] - public static Func> CompileAsyncQuery( + public static Func> CompileAsyncQuery( [NotNull] Expression>> queryExpression) where TContext : DbContext where TResult : class @@ -49,7 +50,7 @@ public static Func> CompileAsyncQueryThe query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery( + public static Func> CompileAsyncQuery( [NotNull] Expression>> queryExpression) where TContext : DbContext => new CompiledAsyncEnumerableQuery(queryExpression).Execute; @@ -62,7 +63,7 @@ public static Func> CompileAsyncQueryThe query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery( + public static Func> CompileAsyncQuery( [NotNull] Expression>> queryExpression) where TContext : DbContext => new CompiledAsyncEnumerableQuery(queryExpression).Execute; @@ -76,7 +77,7 @@ public static Func> CompileAsyncQuer /// The query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery< + public static Func> CompileAsyncQuery< TContext, TParam1, TParam2, TResult>( [NotNull] Expression>> queryExpression) where TContext : DbContext @@ -92,7 +93,7 @@ public static Func> Compile /// The query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery< + public static Func> CompileAsyncQuery< TContext, TParam1, TParam2, TParam3, TResult>( [NotNull] Expression>> queryExpression) where TContext : DbContext @@ -109,7 +110,7 @@ public static Func /// The query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery< + public static Func> CompileAsyncQuery< TContext, TParam1, TParam2, TParam3, TParam4, TResult>( [NotNull] Expression>> queryExpression) where TContext : DbContext @@ -127,7 +128,7 @@ public static FuncThe query result type. /// The LINQ query expression. /// A delegate that can be invoked to execute the compiled query. - public static Func> CompileAsyncQuery< + public static Func> CompileAsyncQuery< TContext, TParam1, TParam2, TParam3, TParam4, TParam5, TResult>( [NotNull] Expression>> queryExpression) where TContext : DbContext diff --git a/src/EFCore/EFCore.csproj b/src/EFCore/EFCore.csproj index 1216c467c02..da6e7fda382 100644 --- a/src/EFCore/EFCore.csproj +++ b/src/EFCore/EFCore.csproj @@ -7,7 +7,7 @@ Commonly Used Types: Microsoft.EntityFrameworkCore.DbContext Microsoft.EntityFrameworkCore.DbSet - netstandard2.0 + netstandard2.1 3.6 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore diff --git a/src/EFCore/EntityFrameworkEnumerableExtensions.cs b/src/EFCore/EntityFrameworkEnumerableExtensions.cs new file mode 100644 index 00000000000..574dcfee82c --- /dev/null +++ b/src/EFCore/EntityFrameworkEnumerableExtensions.cs @@ -0,0 +1,440 @@ +// 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 c6bc3499a13..7aba45cc4bd 100644 --- a/src/EFCore/EntityFrameworkQueryableExtensions.cs +++ b/src/EFCore/EntityFrameworkQueryableExtensions.cs @@ -2128,7 +2128,7 @@ public static Task ContainsAsync( public static Task> ToListAsync( [NotNull] this IQueryable source, CancellationToken cancellationToken = default) - => source.AsAsyncEnumerable().ToList(cancellationToken); + => source.AsAsyncEnumerable().ToListAsync(cancellationToken); /// /// Asynchronously creates an array from an by enumerating it asynchronously. @@ -2153,7 +2153,7 @@ public static Task> ToListAsync( public static Task ToArrayAsync( [NotNull] this IQueryable source, CancellationToken cancellationToken = default) - => source.AsAsyncEnumerable().ToArray(cancellationToken); + => source.AsAsyncEnumerable().ToArrayAsync(cancellationToken); #endregion @@ -2401,12 +2401,12 @@ public IncludableQueryable(IQueryable queryable) public Type ElementType => _queryable.ElementType; public IQueryProvider Provider => _queryable.Provider; + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => ((IAsyncEnumerable)_queryable).GetAsyncEnumerator(cancellationToken); + public IEnumerator GetEnumerator() => _queryable.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - IAsyncEnumerator IAsyncEnumerable.GetEnumerator() - => ((IAsyncEnumerable)_queryable).GetEnumerator(); } internal static readonly MethodInfo StringIncludeMethodInfo @@ -2686,11 +2686,9 @@ public static async Task LoadAsync( { Check.NotNull(source, nameof(source)); - var asyncEnumerable = source.AsAsyncEnumerable(); - - using (var enumerator = asyncEnumerable.GetEnumerator()) + await using (var enumerator = source.AsAsyncEnumerable().GetAsyncEnumerator(cancellationToken)) { - while (await enumerator.MoveNext(cancellationToken)) + while (await enumerator.MoveNextAsync()) { } } @@ -2734,7 +2732,7 @@ public static Task> ToDictionaryAsync( Check.NotNull(source, nameof(source)); Check.NotNull(keySelector, nameof(keySelector)); - return source.AsAsyncEnumerable().ToDictionary(keySelector, cancellationToken); + return source.AsAsyncEnumerable().ToDictionaryAsync(keySelector, cancellationToken); } /// @@ -2776,7 +2774,7 @@ public static Task> ToDictionaryAsync( Check.NotNull(keySelector, nameof(keySelector)); Check.NotNull(comparer, nameof(comparer)); - return source.AsAsyncEnumerable().ToDictionary(keySelector, comparer, cancellationToken); + return source.AsAsyncEnumerable().ToDictionaryAsync(keySelector, comparer, cancellationToken); } /// @@ -2820,7 +2818,7 @@ public static Task> ToDictionaryAsync @@ -2869,7 +2867,7 @@ public static Task> ToDictionaryAsync> ToLookupAsync( Check.NotNull(source, nameof(source)); Check.NotNull(keySelector, nameof(keySelector)); - return source.AsAsyncEnumerable().ToLookup(keySelector, cancellationToken); + return source.AsAsyncEnumerable().ToLookupAsync(keySelector, cancellationToken); } /// @@ -2952,7 +2950,7 @@ public static Task> ToLookupAsync( Check.NotNull(keySelector, nameof(keySelector)); Check.NotNull(comparer, nameof(comparer)); - return source.AsAsyncEnumerable().ToLookup(keySelector, comparer, cancellationToken); + return source.AsAsyncEnumerable().ToLookupAsync(keySelector, comparer, cancellationToken); } /// @@ -2996,7 +2994,7 @@ public static Task> ToLookupAsync @@ -3045,7 +3043,7 @@ public static Task> ToLookupAsync - public virtual void OnIndexRemoved([NotNull] InternalEntityTypeBuilder entityTypeBuilder, [NotNull] Index index) + public virtual void OnIndexRemoved([NotNull] InternalEntityTypeBuilder entityTypeBuilder, [NotNull] Metadata.Internal.Index index) => _scope.OnIndexRemoved(entityTypeBuilder, index); /// diff --git a/src/EFCore/Query/AsyncEnumerable.cs b/src/EFCore/Query/AsyncEnumerable.cs deleted file mode 100644 index 1427464ee48..00000000000 --- a/src/EFCore/Query/AsyncEnumerable.cs +++ /dev/null @@ -1,270 +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.Query.Internal; -using Microsoft.EntityFrameworkCore.Utilities; - -namespace Microsoft.EntityFrameworkCore.Query -{ - /// - /// Represents an asynchronous sequence produced by executing a compiled query. - /// - /// The result type. - public readonly struct AsyncEnumerable : IAsyncEnumerableAccessor - { - private readonly IAsyncEnumerable _asyncEnumerable; - - /// - /// Creates a new instance of - /// - /// The underlying instance. - public AsyncEnumerable([NotNull] IAsyncEnumerable asyncEnumerable) - { - Check.NotNull(asyncEnumerable, nameof(asyncEnumerable)); - - _asyncEnumerable = asyncEnumerable; - } - - IAsyncEnumerable IAsyncEnumerableAccessor.AsyncEnumerable => _asyncEnumerable; - - /// - /// Asynchronously creates a from this - /// 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. - /// - /// - /// 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 async Task> ToListAsync( - CancellationToken cancellationToken = default) - { - var list = new List(); - - using (var asyncEnumerator = _asyncEnumerable.GetEnumerator()) - { - while (await asyncEnumerator.MoveNext(cancellationToken)) - { - list.Add(asyncEnumerator.Current); - } - } - - return list; - } - - /// - /// Asynchronously creates an array from this . - /// - /// - /// 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. - /// - /// - /// 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 async Task ToArrayAsync( - CancellationToken cancellationToken = default) - => (await ToListAsync(cancellationToken)).ToArray(); - - /// - /// Asynchronously enumerates the query. When using Entity Framework, this causes the results of the query to - /// be loaded into the associated context. This is equivalent to calling ToList - /// and then throwing away the list (without the overhead of actually creating the list). - /// - /// - /// A to observe while waiting for the task to complete. - /// - /// A task that represents the asynchronous operation. - public async Task LoadAsync( - CancellationToken cancellationToken = default) - { - using (var enumerator = _asyncEnumerable.GetEnumerator()) - { - while (await enumerator.MoveNext(cancellationToken)) - { - } - } - } - - /// - /// Creates a from this - /// 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 key returned by . - /// - /// 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 Task> ToDictionaryAsync( - [NotNull] Func keySelector, - CancellationToken cancellationToken = default) - { - Check.NotNull(keySelector, nameof(keySelector)); - - return _asyncEnumerable.ToDictionary(keySelector, cancellationToken); - } - - /// - /// Creates a from this - /// 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 key returned by . - /// - /// 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 Task> ToDictionaryAsync( - [NotNull] Func keySelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(comparer, nameof(comparer)); - - return _asyncEnumerable.ToDictionary(keySelector, comparer, cancellationToken); - } - - /// - /// Creates a from this - /// 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 key returned by . - /// - /// - /// The type of the value returned by . - /// - /// 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 Task> ToDictionaryAsync( - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - CancellationToken cancellationToken = default) - { - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - - return _asyncEnumerable.ToDictionary(keySelector, elementSelector, cancellationToken); - } - - /// - /// Creates a from this - /// 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 key returned by . - /// - /// - /// The type of the value returned by . - /// - /// 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 Task> ToDictionaryAsync( - [NotNull] Func keySelector, - [NotNull] Func elementSelector, - [NotNull] IEqualityComparer comparer, - CancellationToken cancellationToken = default) - { - Check.NotNull(keySelector, nameof(keySelector)); - Check.NotNull(elementSelector, nameof(elementSelector)); - Check.NotNull(comparer, nameof(comparer)); - - return _asyncEnumerable.ToDictionary(keySelector, elementSelector, comparer, cancellationToken); - } - - /// - /// 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 action to perform on each element. - /// - /// A to observe while waiting for the task to complete. - /// - /// A task that represents the asynchronous operation. - public async Task ForEachAsync( - [NotNull] Action action, - CancellationToken cancellationToken = default) - { - Check.NotNull(action, nameof(action)); - - using (var asyncEnumerator = _asyncEnumerable.GetEnumerator()) - { - while (await asyncEnumerator.MoveNext(cancellationToken)) - { - action(asyncEnumerator.Current); - } - } - } - } -} diff --git a/src/EFCore/Query/Internal/CompiledAsyncEnumerableQuery.cs b/src/EFCore/Query/Internal/CompiledAsyncEnumerableQuery.cs index 755c4a6a603..1e4b95c7e19 100644 --- a/src/EFCore/Query/Internal/CompiledAsyncEnumerableQuery.cs +++ b/src/EFCore/Query/Internal/CompiledAsyncEnumerableQuery.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public class CompiledAsyncEnumerableQuery : CompiledQueryBase> + public class CompiledAsyncEnumerableQuery : CompiledQueryBase> where TContext : DbContext { /// @@ -34,7 +34,7 @@ public CompiledAsyncEnumerableQuery([NotNull] LambdaExpression queryExpression) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context) => ExecuteCore(context); @@ -44,7 +44,7 @@ public virtual AsyncEnumerable Execute( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context, [CanBeNull] TParam1 param1) => ExecuteCore(context, param1); @@ -55,7 +55,7 @@ public virtual AsyncEnumerable Execute( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context, [CanBeNull] TParam1 param1, [CanBeNull] TParam2 param2) @@ -67,7 +67,7 @@ public virtual AsyncEnumerable Execute( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context, [CanBeNull] TParam1 param1, [CanBeNull] TParam2 param2, @@ -80,7 +80,7 @@ public virtual AsyncEnumerable Execute( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context, [CanBeNull] TParam1 param1, [CanBeNull] TParam2 param2, @@ -94,7 +94,7 @@ public virtual AsyncEnumerable Execute - public virtual AsyncEnumerable Execute( + public virtual IAsyncEnumerable Execute( [NotNull] TContext context, [CanBeNull] TParam1 param1, [CanBeNull] TParam2 param2, @@ -109,12 +109,10 @@ public virtual AsyncEnumerable Execute - protected override Func> CreateCompiledQuery( + protected override Func> CreateCompiledQuery( IQueryCompiler queryCompiler, Expression expression) { - var compiledQuery = queryCompiler.CreateCompiledAsyncQuery>(expression); - - return qc => new AsyncEnumerable(compiledQuery(qc)); + return queryCompiler.CreateCompiledAsyncQuery>(expression); } } } diff --git a/src/EFCore/Query/Internal/EntityQueryable`.cs b/src/EFCore/Query/Internal/EntityQueryable`.cs index 33fba531782..5bac44da27a 100644 --- a/src/EFCore/Query/Internal/EntityQueryable`.cs +++ b/src/EFCore/Query/Internal/EntityQueryable`.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Linq; using System.Linq.Expressions; +using System.Threading; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Utilities; @@ -107,8 +108,8 @@ IEnumerator IEnumerable.GetEnumerator() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IAsyncEnumerator IAsyncEnumerable.GetEnumerator() - => _queryProvider.ExecuteAsync>(Expression).GetEnumerator(); + public virtual IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + => _queryProvider.ExecuteAsync>(Expression).GetAsyncEnumerator(cancellationToken); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs index b2598c60d17..9ebf557697f 100644 --- a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs @@ -98,16 +98,16 @@ private async static Task SingleAsync( IAsyncEnumerable asyncEnumerable, CancellationToken cancellationToken = default) { - using (var enumerator = asyncEnumerable.GetEnumerator()) + await using (var enumerator = asyncEnumerable.GetAsyncEnumerator(cancellationToken)) { - if (!(await enumerator.MoveNext(cancellationToken))) + if (!(await enumerator.MoveNextAsync())) { throw new InvalidOperationException(); } var result = enumerator.Current; - if (await enumerator.MoveNext(cancellationToken)) + if (await enumerator.MoveNextAsync()) { throw new InvalidOperationException(); } @@ -119,16 +119,16 @@ private async static Task SingleOrDefaultAsync( IAsyncEnumerable asyncEnumerable, CancellationToken cancellationToken = default) { - using (var enumerator = asyncEnumerable.GetEnumerator()) + await using (var enumerator = asyncEnumerable.GetAsyncEnumerator(cancellationToken)) { - if (!(await enumerator.MoveNext())) + if (!(await enumerator.MoveNextAsync())) { return default; } var result = enumerator.Current; - if (await enumerator.MoveNext()) + if (await enumerator.MoveNextAsync()) { throw new InvalidOperationException(); } diff --git a/src/Shared/SharedTypeExtensions.cs b/src/Shared/SharedTypeExtensions.cs index de1e78aadf5..ab839a42dd1 100644 --- a/src/Shared/SharedTypeExtensions.cs +++ b/src/Shared/SharedTypeExtensions.cs @@ -115,13 +115,6 @@ private static bool IsInstantiable(TypeInfo type) && !type.IsInterface && (!type.IsGenericType || !type.IsGenericTypeDefinition); - public static bool IsGrouping(this Type type) => IsGrouping(type.GetTypeInfo()); - - private static bool IsGrouping(TypeInfo type) - => type.IsGenericType - && (type.GetGenericTypeDefinition() == typeof(IGrouping<,>) - || type.GetGenericTypeDefinition() == typeof(IAsyncGrouping<,>)); - public static Type UnwrapEnumType(this Type type) { var isNullable = type.IsNullableType(); diff --git a/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs b/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs index 8213532f07e..dd5446f7348 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/QueryableExtensions.cs @@ -14,19 +14,9 @@ public static class QueryableExtensions public static List ToList(this System.Collections.IEnumerable source) => source.OfType().ToList(); - public static async Task> ToListAsync(this IQueryable source, CancellationToken cancellationToken = default) + public static Task> ToListAsync(this IQueryable source, CancellationToken cancellationToken = default) { - var list = new List(); - - using (var e = ((IQueryable)source).AsAsyncEnumerable().GetEnumerator()) - { - while (await e.MoveNext(cancellationToken).ConfigureAwait(false)) - { - list.Add(e.Current); - } - } - - return list; + return ((IQueryable)source).ToListAsync(cancellationToken); } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AsyncSimpleQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AsyncSimpleQuerySqlServerTest.cs index a1bbac1890f..62a36fc0dfa 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AsyncSimpleQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AsyncSimpleQuerySqlServerTest.cs @@ -161,9 +161,9 @@ public async Task Concurrent_async_queries_when_raw_query() { using (var context = CreateContext()) { - using (var asyncEnumerator = context.Customers.AsAsyncEnumerable().GetEnumerator()) + await using (var asyncEnumerator = context.Customers.AsAsyncEnumerable().GetAsyncEnumerator()) { - while (await asyncEnumerator.MoveNext(default)) + while (await asyncEnumerator.MoveNextAsync()) { if (!context.GetService().IsMultipleActiveResultSetsEnabled) { diff --git a/test/EFCore.Tests/Utilities/TypeExtensionsTest.cs b/test/EFCore.Tests/Utilities/TypeExtensionsTest.cs index 50926c5968f..e5f03e219c2 100644 --- a/test/EFCore.Tests/Utilities/TypeExtensionsTest.cs +++ b/test/EFCore.Tests/Utilities/TypeExtensionsTest.cs @@ -1,10 +1,6 @@ // 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. -#if NETCOREAPP3_0 -// workaround the overlap between System.Interactive.Async and System.Runtime -extern alias reactive; -#endif using System; using System.Collections; using System.Collections.Generic; @@ -25,11 +21,7 @@ public void GetSequenceType_finds_element_type() { Assert.Equal(typeof(int), typeof(IEnumerable).GetSequenceType()); Assert.Equal(typeof(int), typeof(IQueryable).GetSequenceType()); -#if NETCOREAPP3_0 - Assert.Equal(typeof(int), typeof(reactive::System.Collections.Generic.IAsyncEnumerable).GetSequenceType()); -#else Assert.Equal(typeof(int), typeof(IAsyncEnumerable).GetSequenceType()); -#endif Assert.Equal(typeof(int), typeof(List).GetSequenceType()); }