Skip to content

Commit

Permalink
IAsyncDisposable plumbing
Browse files Browse the repository at this point in the history
Calls dynamically where things are likely to be `IAsyncDisposable` in the future, but aren't now, or for things that are but don't expose it in the API surface (e.g. IServiceScope).

Fixes #14427
  • Loading branch information
ajcvickers committed Jun 28, 2019
1 parent 9b81ac8 commit d13f1b7
Show file tree
Hide file tree
Showing 36 changed files with 434 additions and 61 deletions.
13 changes: 6 additions & 7 deletions src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
Expand Down Expand Up @@ -507,23 +508,21 @@ public async ValueTask<bool> MoveNextAsync()

_jsonReader.Close();
_jsonReader = null;
_reader.Dispose();
await _reader.DisposeAsyncIfAvailable();
_reader = null;
_responseStream.Dispose();
await _responseStream.DisposeAsync();
_responseStream = null;
return await MoveNextAsync();
}

public ValueTask DisposeAsync()
public async ValueTask DisposeAsync()
{
_jsonReader?.Close();
_jsonReader = null;
_reader?.Dispose();
await _reader.DisposeAsyncIfAvailable();
_reader = null;
_responseStream?.Dispose();
await _responseStream.DisposeAsync();
_responseStream = null;

return default;
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,18 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(
public virtual void ResetState()
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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.
/// </summary>
public virtual ValueTask ResetStateAsync()
{
ResetState();

return default;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
Expand Down Expand Up @@ -255,10 +256,10 @@ public ValueTask<bool> MoveNextAsync()

public ValueTask DisposeAsync()
{
_enumerator?.Dispose();
var enumerator = _enumerator;
_enumerator = null;

return default;
return enumerator.DisposeAsyncIfAvailable();
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,14 @@ public virtual Task RollbackAsync(CancellationToken cancellationToken = default)
public virtual void Dispose()
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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.
/// </summary>
public virtual ValueTask DisposeAsync()
=> default;
}
}
13 changes: 13 additions & 0 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,18 @@ public virtual void EnlistTransaction(Transaction transaction)
public virtual void ResetState()
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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.
/// </summary>
public virtual ValueTask ResetStateAsync()
{
ResetState();

return default;
}
}
}
3 changes: 1 addition & 2 deletions src/EFCore.Relational/Migrations/HistoryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -248,7 +247,7 @@ public virtual async Task<IReadOnlyList<HistoryRow>> GetAppliedMigrationsAsync(
{
var command = Dependencies.RawSqlCommandBuilder.Build(GetAppliedMigrationsSql);

using (var reader = await command.ExecuteReaderAsync(
await using(var reader = await command.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Dependencies.Connection,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -96,7 +97,8 @@ public virtual async Task ExecuteNonQueryAsync(
Check.NotNull(migrationCommands, nameof(migrationCommands));
Check.NotNull(connection, nameof(connection));

using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
try
{
await connection.OpenAsync(cancellationToken);

Expand All @@ -118,7 +120,7 @@ public virtual async Task ExecuteNonQueryAsync(
&& command.TransactionSuppressed)
{
transaction.Commit();
transaction.Dispose();
await transaction.DisposeAsync();
transaction = null;
}

Expand All @@ -129,14 +131,21 @@ public virtual async Task ExecuteNonQueryAsync(
}
finally
{
transaction?.Dispose();
if (transaction != null)
{
await transaction.DisposeAsync();
}
}
}
finally
{
await connection.CloseAsync(cancellationToken);
}
}
finally
{
await transactionScope.DisposeAsyncIfAvailable();
}
}
}
}
10 changes: 8 additions & 2 deletions src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
Expand Down Expand Up @@ -172,8 +173,13 @@ public async ValueTask<bool> MoveNextAsync()

public ValueTask DisposeAsync()
{
_dataReader?.Dispose();
_dataReader = null;
if (_dataReader != null)
{
var dataReader = _dataReader;
_dataReader = null;

return dataReader.DisposeAsync();
}

return default;
}
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Storage/IRelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.EntityFrameworkCore.Storage
/// The implementation does not need to be thread-safe.
/// </para>
/// </summary>
public interface IRelationalConnection : IRelationalTransactionManager, IDisposable
public interface IRelationalConnection : IRelationalTransactionManager, IDisposable, IAsyncDisposable
{
/// <summary>
/// Gets the connection string for the database.
Expand Down
23 changes: 17 additions & 6 deletions src/EFCore.Relational/Storage/RelationalCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Storage
Expand Down Expand Up @@ -122,14 +123,24 @@ public virtual int ExecuteNonQuery(RelationalCommandParameterObject parameterObj
}

private static void CleanupCommand(
DbCommand dbCommand,
DbCommand command,
IRelationalConnection connection)
{
dbCommand.Parameters.Clear();
dbCommand.Dispose();
command.Parameters.Clear();
command.Dispose();
connection.Close();
}

private static async Task CleanupCommandAsync(
DbCommand command,
IRelationalConnection connection,
CancellationToken cancellationToken = default)
{
command.Parameters.Clear();
await command.DisposeAsyncIfAvailable();
await connection.CloseAsync(cancellationToken);
}

/// <summary>
/// Asynchronously executes the command with no results.
/// </summary>
Expand Down Expand Up @@ -203,7 +214,7 @@ await logger.CommandErrorAsync(
}
finally
{
CleanupCommand(command, connection);
await CleanupCommandAsync(command, connection, cancellationToken);
}
}

Expand Down Expand Up @@ -336,7 +347,7 @@ await logger.CommandErrorAsync(
}
finally
{
CleanupCommand(command, connection);
await CleanupCommandAsync(command, connection, cancellationToken);
}
}

Expand Down Expand Up @@ -499,7 +510,7 @@ await logger.CommandErrorAsync(
{
if (!readerOpen)
{
CleanupCommand(command, connection);
await CleanupCommandAsync(command, connection, cancellationToken);
}
}
}
Expand Down
57 changes: 43 additions & 14 deletions src/EFCore.Relational/Storage/RelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -422,6 +423,7 @@ public virtual bool Open(bool errorsExpected = false)
var wasOpened = false;
if (DbConnection.State != ConnectionState.Open)
{
CurrentTransaction?.Dispose();
ClearTransactions(clearAmbient: false);
OpenDbConnection(errorsExpected);
wasOpened = true;
Expand Down Expand Up @@ -455,6 +457,11 @@ public virtual async Task<bool> OpenAsync(CancellationToken cancellationToken, b
var wasOpened = false;
if (DbConnection.State != ConnectionState.Open)
{
if (CurrentTransaction != null)
{
await CurrentTransaction.DisposeAsync();
}

ClearTransactions(clearAmbient: false);
await OpenDbConnectionAsync(errorsExpected, cancellationToken);
wasOpened = true;
Expand All @@ -469,7 +476,6 @@ public virtual async Task<bool> OpenAsync(CancellationToken cancellationToken, b

private void ClearTransactions(bool clearAmbient)
{
CurrentTransaction?.Dispose();
CurrentTransaction = null;
EnlistedTransaction = null;
if (clearAmbient
Expand Down Expand Up @@ -611,6 +617,9 @@ public virtual bool Close()

if (ShouldClose())
{
CurrentTransaction?.Dispose();
ClearTransactions(clearAmbient: false);

if (DbConnection.State != ConnectionState.Closed)
{
var startTime = DateTimeOffset.UtcNow;
Expand Down Expand Up @@ -659,6 +668,12 @@ public virtual async Task<bool> CloseAsync(CancellationToken cancellationToken =

if (ShouldClose())
{
if (CurrentTransaction != null)
{
await CurrentTransaction.DisposeAsync();
}
ClearTransactions(clearAmbient: false);

if (DbConnection.State != ConnectionState.Closed)
{
var startTime = DateTimeOffset.UtcNow;
Expand Down Expand Up @@ -705,19 +720,10 @@ await Dependencies.ConnectionLogger.ConnectionErrorAsync(
}

private bool ShouldClose()
{
if ((_openedCount == 0
|| _openedCount > 0
&& --_openedCount == 0)
&& _openedInternally)
{
ClearTransactions(clearAmbient: false);

return true;
}

return false;
}
=> (_openedCount == 0
|| _openedCount > 0
&& --_openedCount == 0)
&& _openedInternally;

/// <summary>
/// Gets a value indicating whether the multiple active result sets feature is enabled.
Expand All @@ -726,6 +732,8 @@ private bool ShouldClose()

void IResettableService.ResetState() => Dispose();

ValueTask IResettableService.ResetStateAsync() => DisposeAsync();

/// <summary>
/// Gets a semaphore used to serialize access to this connection.
/// </summary>
Expand All @@ -741,6 +749,7 @@ private bool ShouldClose()
/// </summary>
public virtual void Dispose()
{
CurrentTransaction?.Dispose();
ClearTransactions(clearAmbient: true);

if (_connectionOwned
Expand All @@ -751,5 +760,25 @@ public virtual void Dispose()
_openedCount = 0;
}
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public virtual async ValueTask DisposeAsync()
{
if (CurrentTransaction != null)
{
await CurrentTransaction.DisposeAsync();
}
ClearTransactions(clearAmbient: true);

if (_connectionOwned
&& _connection != null)
{
await DbConnection.DisposeAsyncIfAvailable();
_connection = null;
_openedCount = 0;
}
}
}
}
Loading

0 comments on commit d13f1b7

Please sign in to comment.