diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerTransactionTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerTransactionTests.cs
index c9638e557cf2..1911a91ae320 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerTransactionTests.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerTransactionTests.cs
@@ -16,6 +16,7 @@
using Google.Cloud.Spanner.V1.Internal.Logging;
using Google.Cloud.Spanner.V1.Tests;
using System;
+using System.Threading.Tasks;
using Xunit;
namespace Google.Cloud.Spanner.Data.Tests;
@@ -47,6 +48,54 @@ public void MaxCommitDelay_DefaultsToNull()
Assert.Null(transaction.TransactionOptions.MaxCommitDelay);
}
+ [Fact]
+ public void Connection_IsNullAfterDispose()
+ {
+ SpannerClient spannerClientMock = SpannerClientHelpers.CreateMockClient(Logger.DefaultLogger);
+ spannerClientMock.SetupCreateSessionAsync();
+ SpannerConnection connection = SpannerCommandTests.BuildSpannerConnection(spannerClientMock);
+ SpannerTransaction transaction = connection.BeginTransaction();
+
+ Assert.Same(connection, transaction.Connection);
+
+ transaction.Dispose();
+
+ Assert.Null(transaction.Connection);
+ }
+
+ [Fact]
+ public async Task Connection_IsNullAfterCommit()
+ {
+ SpannerClient spannerClientMock = SpannerClientHelpers.CreateMockClient(Logger.DefaultLogger);
+ spannerClientMock.SetupCreateSessionAsync().SetupExecuteStreamingSql().SetupCommitAsync();
+ SpannerConnection connection = SpannerCommandTests.BuildSpannerConnection(spannerClientMock);
+ SpannerTransaction transaction = connection.BeginTransaction();
+
+ using (var command = connection.CreateSelectCommand("SELECT 1"))
+ {
+ command.Transaction = transaction;
+ using var reader = await command.ExecuteReaderAsync();
+ Assert.True(reader.HasRows);
+ }
+
+ await transaction.CommitAsync();
+
+ Assert.Null(transaction.Connection);
+ }
+
+ [Fact]
+ public async Task Connection_IsNullAfterRollback()
+ {
+ SpannerClient spannerClientMock = SpannerClientHelpers.CreateMockClient(Logger.DefaultLogger);
+ spannerClientMock.SetupCreateSessionAsync().SetupRollbackAsync();
+ SpannerConnection connection = SpannerCommandTests.BuildSpannerConnection(spannerClientMock);
+ SpannerTransaction transaction = connection.BeginTransaction();
+
+ await transaction.RollbackAsync();
+
+ Assert.Null(transaction.Connection);
+ }
+
[Theory, MemberData(nameof(ValidMaxCommitDelayValues))]
public void MaxCommitDelay_Valid(TimeSpan? maxCommitDelay)
{
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerTransaction.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerTransaction.cs
index e47a12fdc9c4..76c3320a87e7 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerTransaction.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerTransaction.cs
@@ -64,6 +64,7 @@ public sealed class SpannerTransaction : SpannerTransactionBase, ISpannerTransac
private readonly bool _isRetriable = false;
private int _disposed = 0;
private int _commited = 0;
+ private int _completed = 0;
// Flag indicating whether the transaction has executed at least one statement.
// The TransactionTag may no longer be set once the transaction has executed one
@@ -153,7 +154,7 @@ public int CommitTimeout
public TimestampBound TimestampBound => _creationOptions.TimestampBound ?? _creationOptions.TransactionId?.TimestampBound;
///
- protected override DbConnection DbConnection => SpannerConnection;
+ protected override DbConnection DbConnection => _disposed == 0 && _completed == 0 ? SpannerConnection : null;
///
/// associated with this transaction.
@@ -448,6 +449,7 @@ Task> ISpannerTransaction.ExecuteBatchDmlAsync(ExecuteBatchDml
settings => settings.CommitSettings, TransactionOptions.EffectiveCommitTimeout(SpannerConnection), cancellationToken);
var response = await _session.CommitAsync(request, callSettings).ConfigureAwait(false);
Interlocked.Exchange(ref _commited, 1);
+ Interlocked.Exchange(ref _completed, 1);
// We dispose of the SpannerTransaction to inmediately release the session to the pool when possible.
Dispose();
if (response.CommitTimestamp == null)
@@ -503,6 +505,7 @@ public override async Task RollbackAsync(CancellationToken cancellationToken = d
await ExecuteHelper.WithErrorTranslationAndProfiling(
() => _session.RollbackAsync(new RollbackRequest(), callSettings),
"SpannerTransaction.Rollback", SpannerConnection.Logger).ConfigureAwait(false);
+ Interlocked.Exchange(ref _completed, 1);
Dispose();
}