Ensure transaction is committed in the correct context, when handling chained or nested commands
This commit is contained in:
parent
e6d1318418
commit
f42f29db03
@ -27,26 +27,30 @@ namespace Ordering.API.Application.Behaviors
|
|||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
|
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
|
||||||
{
|
{
|
||||||
TResponse response = default(TResponse);
|
var response = default(TResponse);
|
||||||
var typeName = request.GetGenericTypeName();
|
var typeName = request.GetGenericTypeName();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_dbContext.HasActiveTransaction)
|
||||||
|
{
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
|
||||||
var strategy = _dbContext.Database.CreateExecutionStrategy();
|
var strategy = _dbContext.Database.CreateExecutionStrategy();
|
||||||
|
|
||||||
await strategy.ExecuteAsync(async () =>
|
await strategy.ExecuteAsync(async () =>
|
||||||
{
|
{
|
||||||
var transaction = await _dbContext.BeginTransactionAsync();
|
using (var transaction = await _dbContext.BeginTransactionAsync())
|
||||||
|
|
||||||
using (LogContext.PushProperty("TransactionContext", transaction.TransactionId))
|
using (LogContext.PushProperty("TransactionContext", transaction.TransactionId))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request);
|
_logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request);
|
||||||
|
|
||||||
response = await next();
|
response = await next();
|
||||||
|
|
||||||
await _dbContext.CommitTransactionAsync();
|
_logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName);
|
||||||
|
|
||||||
_logger.LogInformation("----- Transaction {TransactionId} committed for {CommandName}", transaction.TransactionId, typeName);
|
await _dbContext.CommitTransactionAsync(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync();
|
await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync();
|
||||||
@ -58,7 +62,6 @@ namespace Ordering.API.Application.Behaviors
|
|||||||
{
|
{
|
||||||
_logger.LogError(ex, "----- ERROR Handling transaction for {CommandName} ({@Command})", typeName, request);
|
_logger.LogError(ex, "----- ERROR Handling transaction for {CommandName} ({@Command})", typeName, request);
|
||||||
|
|
||||||
_dbContext.RollbackTransaction();
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
|||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private IDbContextTransaction _currentTransaction;
|
private IDbContextTransaction _currentTransaction;
|
||||||
|
|
||||||
private OrderingContext(DbContextOptions<OrderingContext> options) : base (options) { }
|
private OrderingContext(DbContextOptions<OrderingContext> options) : base(options) { }
|
||||||
|
|
||||||
public IDbContextTransaction GetCurrentTransaction => _currentTransaction;
|
public IDbContextTransaction GetCurrentTransaction => _currentTransaction;
|
||||||
|
|
||||||
|
public bool HasActiveTransaction => _currentTransaction != null;
|
||||||
|
|
||||||
public OrderingContext(DbContextOptions<OrderingContext> options, IMediator mediator) : base(options)
|
public OrderingContext(DbContextOptions<OrderingContext> options, IMediator mediator) : base(options)
|
||||||
{
|
{
|
||||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
@ -69,17 +71,22 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
|||||||
|
|
||||||
public async Task<IDbContextTransaction> BeginTransactionAsync()
|
public async Task<IDbContextTransaction> BeginTransactionAsync()
|
||||||
{
|
{
|
||||||
_currentTransaction = _currentTransaction ?? await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
|
if (_currentTransaction != null) return null;
|
||||||
|
|
||||||
|
_currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
|
||||||
|
|
||||||
return _currentTransaction;
|
return _currentTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CommitTransactionAsync()
|
public async Task CommitTransactionAsync(IDbContextTransaction transaction)
|
||||||
{
|
{
|
||||||
|
if (transaction == null) throw new ArgumentNullException(nameof(transaction));
|
||||||
|
if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SaveChangesAsync();
|
await SaveChangesAsync();
|
||||||
_currentTransaction?.Commit();
|
transaction.Commit();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -120,7 +127,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
|||||||
var optionsBuilder = new DbContextOptionsBuilder<OrderingContext>()
|
var optionsBuilder = new DbContextOptionsBuilder<OrderingContext>()
|
||||||
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true");
|
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true");
|
||||||
|
|
||||||
return new OrderingContext(optionsBuilder.Options,new NoMediator());
|
return new OrderingContext(optionsBuilder.Options, new NoMediator());
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoMediator : IMediator
|
class NoMediator : IMediator
|
||||||
|
Loading…
x
Reference in New Issue
Block a user