Skip to content

Commit f42f29d

Browse files
committed
Ensure transaction is committed in the correct context, when handling chained or nested commands
1 parent e6d1318 commit f42f29d

2 files changed

Lines changed: 22 additions & 12 deletions

File tree

src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,30 @@ public TransactionBehaviour(OrderingContext dbContext,
2727

2828
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
2929
{
30-
TResponse response = default(TResponse);
30+
var response = default(TResponse);
3131
var typeName = request.GetGenericTypeName();
3232

3333
try
3434
{
35+
if (_dbContext.HasActiveTransaction)
36+
{
37+
return await next();
38+
}
39+
3540
var strategy = _dbContext.Database.CreateExecutionStrategy();
3641

3742
await strategy.ExecuteAsync(async () =>
3843
{
39-
var transaction = await _dbContext.BeginTransactionAsync();
40-
44+
using (var transaction = await _dbContext.BeginTransactionAsync())
4145
using (LogContext.PushProperty("TransactionContext", transaction.TransactionId))
4246
{
4347
_logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request);
4448

4549
response = await next();
4650

47-
await _dbContext.CommitTransactionAsync();
51+
_logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName);
4852

49-
_logger.LogInformation("----- Transaction {TransactionId} committed for {CommandName}", transaction.TransactionId, typeName);
53+
await _dbContext.CommitTransactionAsync(transaction);
5054
}
5155

5256
await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync();
@@ -58,7 +62,6 @@ await strategy.ExecuteAsync(async () =>
5862
{
5963
_logger.LogError(ex, "----- ERROR Handling transaction for {CommandName} ({@Command})", typeName, request);
6064

61-
_dbContext.RollbackTransaction();
6265
throw;
6366
}
6467
}

src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ public class OrderingContext : DbContext, IUnitOfWork
2727
private readonly IMediator _mediator;
2828
private IDbContextTransaction _currentTransaction;
2929

30-
private OrderingContext(DbContextOptions<OrderingContext> options) : base (options) { }
30+
private OrderingContext(DbContextOptions<OrderingContext> options) : base(options) { }
3131

3232
public IDbContextTransaction GetCurrentTransaction => _currentTransaction;
3333

34+
public bool HasActiveTransaction => _currentTransaction != null;
35+
3436
public OrderingContext(DbContextOptions<OrderingContext> options, IMediator mediator) : base(options)
3537
{
3638
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
@@ -47,7 +49,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
4749
modelBuilder.ApplyConfiguration(new OrderItemEntityTypeConfiguration());
4850
modelBuilder.ApplyConfiguration(new CardTypeEntityTypeConfiguration());
4951
modelBuilder.ApplyConfiguration(new OrderStatusEntityTypeConfiguration());
50-
modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration());
52+
modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration());
5153
}
5254

5355
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
@@ -69,17 +71,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
6971

7072
public async Task<IDbContextTransaction> BeginTransactionAsync()
7173
{
72-
_currentTransaction = _currentTransaction ?? await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
74+
if (_currentTransaction != null) return null;
75+
76+
_currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
7377

7478
return _currentTransaction;
7579
}
7680

77-
public async Task CommitTransactionAsync()
81+
public async Task CommitTransactionAsync(IDbContextTransaction transaction)
7882
{
83+
if (transaction == null) throw new ArgumentNullException(nameof(transaction));
84+
if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
85+
7986
try
8087
{
8188
await SaveChangesAsync();
82-
_currentTransaction?.Commit();
89+
transaction.Commit();
8390
}
8491
catch
8592
{
@@ -120,7 +127,7 @@ public OrderingContext CreateDbContext(string[] args)
120127
var optionsBuilder = new DbContextOptionsBuilder<OrderingContext>()
121128
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true");
122129

123-
return new OrderingContext(optionsBuilder.Options,new NoMediator());
130+
return new OrderingContext(optionsBuilder.Options, new NoMediator());
124131
}
125132

126133
class NoMediator : IMediator

0 commit comments

Comments
 (0)