Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Commit db35a5c

Browse files
author
Ramón Tomás
committed
Initial approach
1 parent 0bf9a91 commit db35a5c

File tree

4 files changed

+113
-11
lines changed

4 files changed

+113
-11
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using MediatR;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
4+
using Microsoft.Extensions.Logging;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Ordering.API.Application.Behaviors
12+
{
13+
public class TransactionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
14+
{
15+
private readonly ILogger<TransactionBehaviour<TRequest, TResponse>> _logger;
16+
private readonly OrderingContext _dbContext;
17+
18+
public TransactionBehaviour(OrderingContext dbContext, ILogger<TransactionBehaviour<TRequest, TResponse>> logger)
19+
{
20+
_dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext));
21+
_logger = logger ?? throw new ArgumentException(nameof(ILogger));
22+
}
23+
24+
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
25+
{
26+
TResponse response = default(TResponse);
27+
28+
try
29+
{
30+
var strategy = _dbContext.Database.CreateExecutionStrategy();
31+
await strategy.ExecuteAsync(async () =>
32+
{
33+
_logger.LogInformation($"Begin transaction {typeof(TRequest).Name}");
34+
35+
await _dbContext.BeginTransactionAsync();
36+
37+
response = await next();
38+
39+
await _dbContext.CommitTransactionAsync();
40+
41+
_logger.LogInformation($"Committed transaction {typeof(TRequest).Name}");
42+
});
43+
44+
return response;
45+
}
46+
catch (Exception)
47+
{
48+
_logger.LogInformation($"Rollback transaction executed {typeof(TRequest).Name}");
49+
50+
_dbContext.RollbackTransaction();
51+
throw;
52+
}
53+
}
54+
}
55+
}

src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.EntityFrameworkCore.Storage;
33
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
44
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
5+
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
56
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
67
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
78
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
@@ -17,12 +18,16 @@ public class OrderingIntegrationEventService : IOrderingIntegrationEventService
1718
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
1819
private readonly IEventBus _eventBus;
1920
private readonly OrderingContext _orderingContext;
21+
private readonly IntegrationEventLogContext _eventLogContext;
2022
private readonly IIntegrationEventLogService _eventLogService;
2123

22-
public OrderingIntegrationEventService(IEventBus eventBus, OrderingContext orderingContext,
23-
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
24+
public OrderingIntegrationEventService(IEventBus eventBus,
25+
OrderingContext orderingContext,
26+
IntegrationEventLogContext eventLogContext,
27+
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
2428
{
2529
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
30+
_eventLogContext = eventLogContext ?? throw new ArgumentNullException(nameof(eventLogContext));
2631
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
2732
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
2833
_eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection());
@@ -37,14 +42,8 @@ public async Task PublishThroughEventBusAsync(IntegrationEvent evt)
3742

3843
private async Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt)
3944
{
40-
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
41-
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
42-
await ResilientTransaction.New(_orderingContext)
43-
.ExecuteAsync(async () => {
44-
// Achieving atomicity between original ordering database operation and the IntegrationEventLog thanks to a local transaction
45-
await _orderingContext.SaveChangesAsync();
46-
await _eventLogService.SaveEventAsync(evt, _orderingContext.Database.CurrentTransaction.GetDbTransaction());
47-
});
45+
await _orderingContext.SaveChangesAsync();
46+
await _eventLogContext.SaveChangesAsync();
4847
}
4948
}
5049
}

src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using FluentValidation;
44
using MediatR;
55
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
6+
using Ordering.API.Application.Behaviors;
67
using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
78
using Ordering.API.Application.Validations;
89
using Ordering.API.Infrastructure.Behaviors;
@@ -53,6 +54,7 @@ protected override void Load(ContainerBuilder builder)
5354

5455
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
5556
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
57+
builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>));
5658

5759
}
5860
}

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using MediatR;
22
using Microsoft.EntityFrameworkCore;
33
using Microsoft.EntityFrameworkCore.Design;
4+
using Microsoft.EntityFrameworkCore.Storage;
45
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
56
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
67
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
78
using Ordering.Infrastructure;
89
using Ordering.Infrastructure.EntityConfigurations;
910
using System;
11+
using System.Data;
1012
using System.Threading;
1113
using System.Threading.Tasks;
1214

@@ -23,6 +25,7 @@ public class OrderingContext : DbContext, IUnitOfWork
2325
public DbSet<OrderStatus> OrderStatus { get; set; }
2426

2527
private readonly IMediator _mediator;
28+
private IDbContextTransaction _currentTransaction;
2629

2730
private OrderingContext(DbContextOptions<OrderingContext> options) : base (options) { }
2831

@@ -60,7 +63,50 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
6063
var result = await base.SaveChangesAsync();
6164

6265
return true;
63-
}
66+
}
67+
68+
public async Task BeginTransactionAsync()
69+
{
70+
_currentTransaction = _currentTransaction ?? await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
71+
}
72+
73+
public async Task CommitTransactionAsync()
74+
{
75+
try
76+
{
77+
await SaveChangesAsync();
78+
_currentTransaction?.Commit();
79+
}
80+
catch
81+
{
82+
RollbackTransaction();
83+
throw;
84+
}
85+
finally
86+
{
87+
if (_currentTransaction != null)
88+
{
89+
_currentTransaction.Dispose();
90+
_currentTransaction = null;
91+
}
92+
}
93+
}
94+
95+
public void RollbackTransaction()
96+
{
97+
try
98+
{
99+
_currentTransaction?.Rollback();
100+
}
101+
finally
102+
{
103+
if (_currentTransaction != null)
104+
{
105+
_currentTransaction.Dispose();
106+
_currentTransaction = null;
107+
}
108+
}
109+
}
64110
}
65111

66112
public class OrderingContextDesignFactory : IDesignTimeDbContextFactory<OrderingContext>

0 commit comments

Comments
 (0)