Skip to content

Commit

Permalink
Use existing transaction identifier in RelationalConnection.UseTransa…
Browse files Browse the repository at this point in the history
…ction (#20844)

* Use existing transaction identifier when UseTransaction of the RelationalConnection i called. #20828

* Add M prefix to XML comment to fix CS0419

* Implement interfaces in tests. Add M prefix to XML comment to fix CS0419

* Add [NotNull] attribute
  • Loading branch information
Marusyk committed May 18, 2020
1 parent 350c4cb commit 9e78d7e
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 25 deletions.
12 changes: 6 additions & 6 deletions src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,17 @@ public virtual Task<DbTransaction> TransactionStartedAsync(

/// <summary>
/// <para>
/// Called immediately after <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" /> is called.
/// Called immediately after <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" /> is called.
/// </para>
/// </summary>
/// <param name="connection"> The connection. </param>
/// <param name="eventData"> Contextual information about connection and transaction. </param>
/// <param name="result">
/// The <see cref="DbTransaction" /> that was passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" />.
/// The <see cref="DbTransaction" /> that was passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The value that will be used as the effective value passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" />
/// The value that will be used as the effective value passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" />
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
Expand All @@ -142,19 +142,19 @@ public virtual DbTransaction TransactionUsed(

/// <summary>
/// <para>
/// Called immediately after <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" /> is called.
/// Called immediately after <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" /> is called.
/// </para>
/// </summary>
/// <param name="connection"> The connection. </param>
/// <param name="eventData"> Contextual information about connection and transaction. </param>
/// <param name="result">
/// The <see cref="DbTransaction" /> that was passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" />.
/// The <see cref="DbTransaction" /> that was passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <param name="cancellationToken"> The cancellation token. </param>
/// <returns>
/// A <see cref="Task" /> containing the value that will be used as the effective value passed
/// to <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" />
/// to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" />
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in, often using <see cref="Task.FromResult{TResult}" />
/// </returns>
Expand Down
12 changes: 6 additions & 6 deletions src/EFCore.Relational/Diagnostics/IDbTransactionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@ Task<DbTransaction> TransactionStartedAsync(

/// <summary>
/// <para>
/// Called immediately after <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" /> is called.
/// Called immediately after <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" /> is called.
/// </para>
/// </summary>
/// <param name="connection"> The connection. </param>
/// <param name="eventData"> Contextual information about connection and transaction. </param>
/// <param name="result">
/// The <see cref="DbTransaction" /> that was passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" />.
/// The <see cref="DbTransaction" /> that was passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The value that will be used as the effective value passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransaction" />
/// The value that will be used as the effective value passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransaction" />
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
Expand All @@ -158,19 +158,19 @@ DbTransaction TransactionUsed(

/// <summary>
/// <para>
/// Called immediately after <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" /> is called.
/// Called immediately after <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" /> is called.
/// </para>
/// </summary>
/// <param name="connection"> The connection. </param>
/// <param name="eventData"> Contextual information about connection and transaction. </param>
/// <param name="result">
/// The <see cref="DbTransaction" /> that was passed to <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" />.
/// The <see cref="DbTransaction" /> that was passed to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <param name="cancellationToken"> The cancellation token. </param>
/// <returns>
/// A <see cref="Task" /> containing the value that will be used as the effective value passed
/// to <see cref="RelationalDatabaseFacadeExtensions.UseTransactionAsync" />
/// to <see cref="M:RelationalDatabaseFacadeExtensions.UseTransactionAsync" />
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in, often using <see cref="Task.FromResult{TResult}" />
/// </returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public static Task MigrateAsync(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -151,7 +151,7 @@ public static int ExecuteSqlRaw(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -182,7 +182,7 @@ public static int ExecuteSqlInterpolated(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -244,7 +244,7 @@ public static int ExecuteSqlRaw(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -279,7 +279,7 @@ public static Task<int> ExecuteSqlInterpolatedAsync(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -310,7 +310,7 @@ public static Task<int> ExecuteSqlRawAsync(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -350,7 +350,7 @@ public static Task<int> ExecuteSqlRawAsync(
/// </para>
/// <para>
/// Note that this method does not start a transaction. To use this method with
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="UseTransaction" />.
/// a transaction, first call <see cref="BeginTransaction" /> or <see cref="M:UseTransaction" />.
/// </para>
/// <para>
/// Note that the current <see cref="ExecutionStrategy" /> is not used by this method
Expand Down Expand Up @@ -542,6 +542,17 @@ public static Task<IDbContextTransaction> BeginTransactionAsync(
/// <returns> A <see cref="IDbContextTransaction" /> that encapsulates the given transaction. </returns>
public static IDbContextTransaction UseTransaction(
[NotNull] this DatabaseFacade databaseFacade, [CanBeNull] DbTransaction transaction)
=> databaseFacade.UseTransaction(transaction, Guid.NewGuid());

/// <summary>
/// Sets the <see cref="DbTransaction" /> to be used by database operations on the <see cref="DbContext" />.
/// </summary>
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
/// <param name="transaction"> The <see cref="DbTransaction" /> to use. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </param>
/// <returns> A <see cref="IDbContextTransaction" /> that encapsulates the given transaction. </returns>
public static IDbContextTransaction UseTransaction(
[NotNull] this DatabaseFacade databaseFacade, [CanBeNull] DbTransaction transaction, Guid transactionId)
{
var transactionManager = GetTransactionManager(databaseFacade);

Expand All @@ -550,7 +561,7 @@ public static IDbContextTransaction UseTransaction(
throw new InvalidOperationException(RelationalStrings.RelationalNotInUse);
}

return relationalTransactionManager.UseTransaction(transaction);
return relationalTransactionManager.UseTransaction(transaction, transactionId);
}

/// <summary>
Expand All @@ -564,6 +575,21 @@ public static Task<IDbContextTransaction> UseTransactionAsync(
[NotNull] this DatabaseFacade databaseFacade,
[CanBeNull] DbTransaction transaction,
CancellationToken cancellationToken = default)
=> databaseFacade.UseTransactionAsync(transaction, Guid.NewGuid(), cancellationToken);

/// <summary>
/// Sets the <see cref="DbTransaction" /> to be used by database operations on the <see cref="DbContext" />.
/// </summary>
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
/// <param name="transaction"> The <see cref="DbTransaction" /> to use. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </param>
/// <param name="cancellationToken">A token to observe while waiting for the task to complete.</param>
/// <returns> A <see cref="Task" /> containing the <see cref="IDbContextTransaction" /> for the given transaction. </returns>
public static Task<IDbContextTransaction> UseTransactionAsync(
[NotNull] this DatabaseFacade databaseFacade,
[CanBeNull] DbTransaction transaction,
Guid transactionId,
CancellationToken cancellationToken = default)
{
var transactionManager = GetTransactionManager(databaseFacade);

Expand All @@ -572,7 +598,7 @@ public static Task<IDbContextTransaction> UseTransactionAsync(
throw new InvalidOperationException(RelationalStrings.RelationalNotInUse);
}

return relationalTransactionManager.UseTransactionAsync(transaction, cancellationToken);
return relationalTransactionManager.UseTransactionAsync(transaction, transactionId, cancellationToken);
}

/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions src/EFCore.Relational/Storage/IRelationalTransactionManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Data;
using System.Data.Common;
using System.Threading;
Expand Down Expand Up @@ -53,10 +54,30 @@ public interface IRelationalTransactionManager : IDbContextTransactionManager
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </param>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
IDbContextTransaction UseTransaction([CanBeNull] DbTransaction transaction, Guid transactionId);

/// <summary>
/// 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>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
Task<IDbContextTransaction> UseTransactionAsync(
[CanBeNull] DbTransaction transaction,
CancellationToken cancellationToken = default);

/// <summary>
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </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>
Task<IDbContextTransaction> UseTransactionAsync(
[CanBeNull] DbTransaction transaction,
Guid transactionId,
CancellationToken cancellationToken = default);
}
}
27 changes: 23 additions & 4 deletions src/EFCore.Relational/Storage/RelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,22 @@ private IDbContextTransaction CreateRelationalTransaction(DbTransaction transact
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
public virtual IDbContextTransaction UseTransaction(DbTransaction transaction)
=> UseTransaction(transaction, Guid.NewGuid());

/// <summary>
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </param>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
public virtual IDbContextTransaction UseTransaction(DbTransaction transaction, Guid transactionId)
{
if (ShouldUseTransaction(transaction))
{
Open();

var transactionId = Guid.NewGuid();

transaction = Dependencies.TransactionLogger.TransactionUsed(
this,
// ReSharper disable once AssignNullToNotNullAttribute
Expand All @@ -412,16 +420,27 @@ public virtual IDbContextTransaction UseTransaction(DbTransaction transaction)
/// <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>
/// <returns> An instance of <see cref="IDbTransaction" /> that wraps the provided transaction. </returns>
public virtual Task<IDbContextTransaction> UseTransactionAsync(
DbTransaction transaction,
CancellationToken cancellationToken = default)
=> UseTransactionAsync(transaction, Guid.NewGuid(), cancellationToken);

/// <summary>
/// Specifies an existing <see cref="DbTransaction" /> to be used for database operations.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
/// <param name="transactionId"> The unique identifier for the transaction. </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,
Guid transactionId,
CancellationToken cancellationToken = default)
{
if (ShouldUseTransaction(transaction))
{
await OpenAsync(cancellationToken);

var transactionId = Guid.NewGuid();

transaction = await Dependencies.TransactionLogger.TransactionUsedAsync(
this,
// ReSharper disable once AssignNullToNotNullAttribute
Expand Down
Loading

0 comments on commit 9e78d7e

Please sign in to comment.