Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Expose async transaction commit/rollback APIs #20695

Merged
merged 1 commit into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(
/// </summary>
public virtual void CommitTransaction() => throw new NotSupportedException();

/// <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 Task CommitTransactionAsync(CancellationToken cancellationToken = default)
=> throw new NotSupportedException();

/// <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
Expand All @@ -51,6 +60,15 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(
/// </summary>
public virtual void RollbackTransaction() => throw new NotSupportedException();

/// <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 Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
=> throw new NotSupportedException();

/// <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
Expand Down
10 changes: 5 additions & 5 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ public virtual void Commit()
/// 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 void Rollback()
{
}
public virtual Task CommitAsync(CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <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 Task CommitAsync(CancellationToken cancellationToken = default)
=> Task.CompletedTask;
public virtual void Rollback()
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
24 changes: 24 additions & 0 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(
/// </summary>
public virtual void CommitTransaction() => _logger.TransactionIgnoredWarning();

/// <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 Task CommitTransactionAsync(CancellationToken cancellationToken = default)
{
_logger.TransactionIgnoredWarning();
return Task.CompletedTask;
}

/// <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
Expand All @@ -90,6 +102,18 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(
/// </summary>
public virtual void RollbackTransaction() => _logger.TransactionIgnoredWarning();

/// <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 Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
{
_logger.TransactionIgnoredWarning();
return Task.CompletedTask;
}

/// <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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,18 @@ public virtual async Task ExecuteNonQueryAsync(
if (transaction != null
&& command.TransactionSuppressed)
{
transaction.Commit();
await transaction.CommitAsync(cancellationToken);
await transaction.DisposeAsync();
transaction = null;
}

await command.ExecuteNonQueryAsync(connection, cancellationToken: cancellationToken);
}

transaction?.Commit();
if (transaction != null)
{
await transaction.CommitAsync(cancellationToken);
}
}
finally
{
Expand Down
32 changes: 31 additions & 1 deletion src/EFCore.Relational/Storage/RelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ public virtual IDbContextTransaction UseTransaction(DbTransaction transaction)
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <param name="cancellationToken"> A <see cref="CancellationToken" /> to observe while waiting for the task to complete. </param>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
public virtual async Task<IDbContextTransaction> UseTransactionAsync(
DbTransaction transaction,
Expand Down Expand Up @@ -466,6 +466,21 @@ public virtual void CommitTransaction()
CurrentTransaction.Commit();
}

/// <summary>
/// Commits all changes made to the database in the current transaction.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> A Task representing the asynchronous operation. </returns>
public virtual Task CommitTransactionAsync(CancellationToken cancellationToken = default)
{
if (CurrentTransaction == null)
{
throw new InvalidOperationException(RelationalStrings.NoActiveTransaction);
}

return CurrentTransaction.CommitAsync(cancellationToken);
}

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
Expand All @@ -479,6 +494,21 @@ public virtual void RollbackTransaction()
CurrentTransaction.Rollback();
}

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> A Task representing the asynchronous operation. </returns>
public virtual Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
{
if (CurrentTransaction == null)
{
throw new InvalidOperationException(RelationalStrings.NoActiveTransaction);
}

return CurrentTransaction.RollbackAsync(cancellationToken);
}

/// <summary>
/// Opens the connection to the database.
/// </summary>
Expand Down
5 changes: 4 additions & 1 deletion src/EFCore.Relational/Update/Internal/BatchExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ public virtual async Task<int> ExecuteAsync(
rowsAffected += batch.ModificationCommands.Count;
}

startedTransaction?.Commit();
if (startedTransaction != null)
{
await startedTransaction.CommitAsync(cancellationToken);
}
}
finally
{
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore/Infrastructure/DatabaseFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,28 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(CancellationTok
public virtual void CommitTransaction()
=> Dependencies.TransactionManager.CommitTransaction();

/// <summary>
/// Applies the outstanding operations in the current transaction to the database.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> A Task representing the asynchronous operation. </returns>
public virtual Task CommitTransactionAsync(CancellationToken cancellationToken = default)
=> Dependencies.TransactionManager.CommitTransactionAsync(cancellationToken);

/// <summary>
/// Discards the outstanding operations in the current transaction.
/// </summary>
public virtual void RollbackTransaction()
=> Dependencies.TransactionManager.RollbackTransaction();

/// <summary>
/// Applies the outstanding operations in the current transaction to the database.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> A Task representing the asynchronous operation. </returns>
public virtual Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
=> Dependencies.TransactionManager.RollbackTransactionAsync(cancellationToken);

/// <summary>
/// Creates an instance of the configured <see cref="IExecutionStrategy" />.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Storage/ExecutionStrategyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ public static Task<TResult> ExecuteInTransactionAsync<TState, TResult>(
s.CommitFailed = false;
s.Result = await s.Operation(s.State, ct);
s.CommitFailed = true;
transaction.Commit();
await transaction.CommitAsync(cancellationToken);
}

return s.Result;
Expand Down
10 changes: 5 additions & 5 deletions src/EFCore/Storage/IDbContextTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ public interface IDbContextTransaction : IDisposable, IAsyncDisposable
/// </summary>
void Commit();

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
void Rollback();

/// <summary>
/// Commits all changes made to the database in the current transaction asynchronously.
/// </summary>
/// <param name="cancellationToken"> The cancellation token. </param>
/// <returns> A <see cref="Task" /> representing the asynchronous operation. </returns>
Task CommitAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
void Rollback();

/// <summary>
/// Discards all changes made to the database in the current transaction asynchronously.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions src/EFCore/Storage/IDbContextTransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,29 @@ public interface IDbContextTransactionManager : IResettableService
/// </summary>
void CommitTransaction();

/// <summary>
/// Commits all changes made to the database in the current transaction.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>
/// A task that represents the asynchronous operation.
/// </returns>
Task CommitTransactionAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
void RollbackTransaction();

/// <summary>
/// Discards all changes made to the database in the current transaction.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>
/// A task that represents the asynchronous operation.
/// </returns>
Task RollbackTransactionAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Gets the current transaction.
/// </summary>
Expand Down
24 changes: 12 additions & 12 deletions test/EFCore.InMemory.Tests/InMemoryTransactionManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,27 @@ public void CurrentTransaction_returns_null()

[ConditionalFact]
public void Throws_on_BeginTransaction()
{
AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).BeginTransaction());
}
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).BeginTransaction());

[ConditionalFact]
public void Throws_on_BeginTransactionAsync()
{
AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).BeginTransactionAsync().GetAwaiter().GetResult());
}
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).BeginTransactionAsync().GetAwaiter().GetResult());

[ConditionalFact]
public void Throws_on_CommitTransaction()
{
AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).CommitTransaction());
}
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).CommitTransaction());

[ConditionalFact]
public void Throws_on_CommitTransactionAsync()
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).CommitTransactionAsync().GetAwaiter().GetResult());

[ConditionalFact]
public void Throws_on_RollbackTransaction()
{
AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).RollbackTransaction());
}
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).RollbackTransaction());

[ConditionalFact]
public void Throws_on_RollbackTransactionAsync()
=> AssertThrows(() => new InMemoryTransactionManager(CreateLogger()).RollbackTransactionAsync().GetAwaiter().GetResult());

private static void AssertThrows(Action action)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ public virtual async Task RelationalTransaction_can_be_rolled_back(bool autoTran
{
context.Entry(context.Set<TransactionCustomer>().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
await context.SaveChangesAsync();
transaction.Rollback();
await transaction.RollbackAsync();

AssertStoreInitialState();
}
Expand All @@ -877,7 +877,7 @@ public virtual async Task RelationalTransaction_can_be_rolled_back_from_context(
{
context.Entry(context.Set<TransactionCustomer>().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
await context.SaveChangesAsync();
context.Database.RollbackTransaction();
await context.Database.RollbackTransactionAsync();

AssertStoreInitialState();
}
Expand Down
Loading