/// <summary> /// Executes pipeline behavior: /// /// Create a new transaction if there is not already an active transaction within the database context, /// then await the pipeline result through the <c>RequestHandlerDelegate</c> 'next'. /// When the remainder of the pipeline has finished, publish whatever integration events that may have /// spawned during the transaction through the event bus. /// </summary> public async Task <TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate <TResponse> next) { var response = default(TResponse); try { // Do not start a new transaction if an active one already exists if (_context.HasActiveTransaction) { _logger.LogDebug("Has active transaction. Calling command handler."); response = await next(); _logger.LogDebug("Response: {0}", response); return(response); } // Execution strategy includes retry on failure var strategy = _context.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async() => { Guid transactionId; // Start a new transaction to ensure invariance in the domain using (var transaction = await _context.BeginTransactionAsync()) { _logger.LogDebug("New transaction begun. Calling command handler."); response = await next(); _logger.LogDebug("Response: {0}", response); // Commit transaction to database, if this fails a rollback of all changes recorded during the transaction will be rolled back await _context.CommitTransactionAsync(transaction); transactionId = transaction.TransactionId; } _logger.LogDebug("TransactionId: {0}", transactionId); // Publish integration events to event bus if everything succeeded await _integrationEventService.PublishEventsThroughEventBusAsync(transactionId); }); return(response); } catch (Exception ex) { _logger.LogError(ex, "Something terrible happened in the MediatR Pipeline!"); throw; } }