Skip to content

Commit

Permalink
Add CurrentTransaction API to context.Database
Browse files Browse the repository at this point in the history
Issue #5552
  • Loading branch information
ajcvickers committed Jun 30, 2016
1 parent c76ba2b commit f576112
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public virtual Task<IDbContextTransaction> BeginTransactionAsync(

public virtual void RollbackTransaction() => LogWarning();

public virtual IDbContextTransaction CurrentTransaction => null;

private void LogWarning()
{
_logger.LogWarning(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ public interface IRelationalConnection : IRelationalTransactionManager, IDisposa
/// </summary>
DbConnection DbConnection { get; }

/// <summary>
/// Gets the current transaction.
/// </summary>
IDbContextTransaction CurrentTransaction { get; }

/// <summary>
/// Gets the timeout for executing a command against the database.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected RelationalConnection([NotNull] IDbContextOptions options, [NotNull] IL
/// <summary>
/// Gets the current transaction.
/// </summary>
public virtual IDbContextTransaction CurrentTransaction { get; [param: NotNull] protected set; }
public virtual IDbContextTransaction CurrentTransaction { get; [param: CanBeNull] protected set; }

/// <summary>
/// Gets the timeout for executing a command against the database.
Expand Down
23 changes: 21 additions & 2 deletions src/Microsoft.EntityFrameworkCore/Infrastructure/DatabaseFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,25 @@ public virtual void CommitTransaction()
public virtual void RollbackTransaction()
=> TransactionManager.RollbackTransaction();

/// <summary>
/// <para>
/// Gets the current <see cref="IDbContextTransaction" /> being used by the context, or null
/// if no transaction is in use.
/// </para>
/// <para>
/// This property will be null unless one of the 'BeginTransaction' or 'UseTransaction' methods has
/// been called, some of which are available as extension methods installed by EF providers.
/// No attempt is made to obtain a transaction from the current DbConnection or similar.
/// </para>
/// <para>
/// For relational databases, the underlying DbTransaction can be obtained using the
/// 'Microsoft.EntityFrameworkCore.Storage.GetDbTransaction'extension method
/// on the returned <see cref="IDbContextTransaction" />.
/// </para>
/// </summary>
public virtual IDbContextTransaction CurrentTransaction
=> TransactionManager.CurrentTransaction;

/// <summary>
/// <para>
/// Gets the scoped <see cref="IServiceProvider" /> being used to resolve services.
Expand All @@ -145,10 +164,10 @@ public virtual void RollbackTransaction()
/// </summary>
IServiceProvider IInfrastructure<IServiceProvider>.Instance => ((IInfrastructure<IServiceProvider>)_context).Instance;

private IDbContextTransactionManager TransactionManager
private IDbContextTransactionManager TransactionManager
=> _transactionManager ?? (_transactionManager = this.GetService<IDbContextTransactionManager>());

private IDatabaseCreator DatabaseCreator
private IDatabaseCreator DatabaseCreator
=> _databaseCreator ?? (_databaseCreator = this.GetService<IDatabaseCreator>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,10 @@ public interface IDbContextTransactionManager
/// Discards all changes made to the database in the current transaction.
/// </summary>
void RollbackTransaction();

/// <summary>
/// Gets the current transaction.
/// </summary>
IDbContextTransaction CurrentTransaction { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.Extensions.Logging;
using Xunit;
Expand All @@ -13,6 +12,17 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Tests
{
public class InMemoryTransactionManagerTest
{
[Fact]
public void CurrentTransaction_returns_null()
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseInMemoryDatabase();

var transactionManager = new InMemoryTransactionManager(new FakeLogger());

Assert.Null(transactionManager.CurrentTransaction);
}

[Fact]
public void Throws_on_BeginTransaction()
{
Expand All @@ -24,7 +34,7 @@ public void Throws_on_BeginTransaction()
Assert.Equal(
InMemoryStrings.TransactionsNotSupported,
Assert.Throws<InvalidOperationException>(
() => transactionManager.BeginTransaction()).Message);
() => transactionManager.BeginTransaction()).Message);
}

[Fact]
Expand All @@ -38,7 +48,7 @@ public async Task Throws_on_BeginTransactionAsync()
Assert.Equal(
InMemoryStrings.TransactionsNotSupported,
(await Assert.ThrowsAsync<InvalidOperationException>(
async () => await transactionManager.BeginTransactionAsync())).Message);
async () => await transactionManager.BeginTransactionAsync())).Message);
}

[Fact]
Expand All @@ -52,7 +62,7 @@ public void Throws_on_CommitTransaction()
Assert.Equal(
InMemoryStrings.TransactionsNotSupported,
Assert.Throws<InvalidOperationException>(
() => transactionManager.CommitTransaction()).Message);
() => transactionManager.CommitTransaction()).Message);
}

[Fact]
Expand All @@ -66,7 +76,7 @@ public void Throws_on_RollbackTransaction()
Assert.Equal(
InMemoryStrings.TransactionsNotSupported,
Assert.Throws<InvalidOperationException>(
() => transactionManager.RollbackTransaction()).Message);
() => transactionManager.RollbackTransaction()).Message);
}

private class FakeLogger : ILogger<InMemoryTransactionManager>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,12 @@ public void Connection_is_opened_and_closed_by_using_transaction()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

var transaction = connection.BeginTransaction();

Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -485,6 +489,8 @@ public void Connection_is_opened_and_closed_by_using_transaction()

transaction.Dispose();

Assert.Null(connection.CurrentTransaction);

Assert.Equal(1, dbConnection.OpenCount);
Assert.Equal(1, dbConnection.CloseCount);
}
Expand All @@ -498,8 +504,12 @@ public async Task Connection_is_opened_and_closed_by_using_transaction_async()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

var transaction = await connection.BeginTransactionAsync();

Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -512,6 +522,8 @@ public async Task Connection_is_opened_and_closed_by_using_transaction_async()

transaction.Dispose();

Assert.Null(connection.CurrentTransaction);

Assert.Equal(1, dbConnection.OpenCount);
Assert.Equal(1, dbConnection.CloseCount);
}
Expand All @@ -525,8 +537,12 @@ public void Transaction_can_begin_with_isolation_level()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

using (var transaction = connection.BeginTransaction(IsolationLevel.Chaos))
{
Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -535,6 +551,8 @@ public void Transaction_can_begin_with_isolation_level()

Assert.Equal(IsolationLevel.Chaos, dbTransaction.IsolationLevel);
}

Assert.Null(connection.CurrentTransaction);
}
}

Expand All @@ -546,8 +564,12 @@ public async Task Transaction_can_begin_with_isolation_level_async()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

using (var transaction = await connection.BeginTransactionAsync(IsolationLevel.Chaos))
{
Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -556,6 +578,8 @@ public async Task Transaction_can_begin_with_isolation_level_async()

Assert.Equal(IsolationLevel.Chaos, dbTransaction.IsolationLevel);
}

Assert.Null(connection.CurrentTransaction);
}
}

Expand All @@ -568,8 +592,12 @@ public void Current_transaction_is_disposed_when_connection_is_disposed()

Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

var transaction = connection.BeginTransaction();

Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -592,10 +620,14 @@ public void Can_use_existing_transaction()
using (var connection = new FakeRelationalConnection(
CreateOptions(new FakeRelationalOptionsExtension { Connection = dbConnection })))
{
Assert.Null(connection.CurrentTransaction);

using (connection.UseTransaction(dbTransaction))
{
Assert.Equal(dbTransaction, connection.CurrentTransaction.GetDbTransaction());
}

Assert.Null(connection.CurrentTransaction);
}
}

Expand All @@ -607,8 +639,12 @@ public void Commit_calls_commit_on_DbTransaction()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

using (var transaction = connection.BeginTransaction())
{
Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -619,6 +655,8 @@ public void Commit_calls_commit_on_DbTransaction()

Assert.Equal(1, dbTransaction.CommitCount);
}

Assert.Null(connection.CurrentTransaction);
}
}

Expand All @@ -630,8 +668,12 @@ public void Rollback_calls_rollback_on_DbTransaction()
{
Assert.Equal(0, connection.DbConnections.Count);

Assert.Null(connection.CurrentTransaction);

using (var transaction = connection.BeginTransaction())
{
Assert.Same(transaction, connection.CurrentTransaction);

Assert.Equal(1, connection.DbConnections.Count);
var dbConnection = connection.DbConnections[0];

Expand All @@ -642,6 +684,8 @@ public void Rollback_calls_rollback_on_DbTransaction()

Assert.Equal(1, dbTransaction.RollbackCount);
}

Assert.Null(connection.CurrentTransaction);
}
}

Expand Down
14 changes: 14 additions & 0 deletions test/Microsoft.EntityFrameworkCore.Tests/DatabaseFacadeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ public void Can_roll_back_transaction()
transactionManagerMock.Verify(m => m.RollbackTransaction(), Times.Once);
}

[Fact]
public void Can_get_current_transaction()
{
var transactionManagerMock = new Mock<IDbContextTransactionManager>();
var transaction = Mock.Of<IDbContextTransaction>();

transactionManagerMock.Setup(m => m.CurrentTransaction).Returns(transaction);

var context = TestHelpers.Instance.CreateContext(
new ServiceCollection().AddSingleton(transactionManagerMock.Object));

Assert.Same(transaction, context.Database.CurrentTransaction);
}

[Fact]
public void Cannot_use_DatabaseFacade_after_dispose()
{
Expand Down

0 comments on commit f576112

Please sign in to comment.