Refactor transaction and publishing in integration events
This commit is contained in:
parent
d9c004a92d
commit
3c909ff392
@ -0,0 +1,37 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities
|
||||||
|
{
|
||||||
|
public class ResilientTransaction
|
||||||
|
{
|
||||||
|
private DbContext _context;
|
||||||
|
private ResilientTransaction(DbContext context) =>
|
||||||
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||||
|
|
||||||
|
public static ResilientTransaction New (DbContext context) =>
|
||||||
|
new ResilientTransaction(context);
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(Func<Task> action)
|
||||||
|
{
|
||||||
|
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||||
|
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||||
|
var strategy = _context.Database.CreateExecutionStrategy();
|
||||||
|
await strategy.ExecuteAsync(async () =>
|
||||||
|
{
|
||||||
|
using (var transaction = _context.Database.BeginTransaction())
|
||||||
|
{
|
||||||
|
await action();
|
||||||
|
transaction.Commit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,8 @@ using System.Data.Common;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
|
||||||
|
using Catalog.API.IntegrationEvents;
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
||||||
{
|
{
|
||||||
@ -23,15 +25,13 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
|||||||
{
|
{
|
||||||
private readonly CatalogContext _catalogContext;
|
private readonly CatalogContext _catalogContext;
|
||||||
private readonly IOptionsSnapshot<Settings> _settings;
|
private readonly IOptionsSnapshot<Settings> _settings;
|
||||||
private readonly IEventBus _eventBus;
|
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
|
||||||
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
|
|
||||||
|
|
||||||
public CatalogController(CatalogContext Context, IOptionsSnapshot<Settings> settings, IEventBus eventBus, Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
|
public CatalogController(CatalogContext Context, IOptionsSnapshot<Settings> settings, ICatalogIntegrationEventService catalogIntegrationEventService)
|
||||||
{
|
{
|
||||||
_catalogContext = Context;
|
_catalogContext = Context;
|
||||||
|
_catalogIntegrationEventService = catalogIntegrationEventService;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_eventBus = eventBus;
|
|
||||||
_integrationEventLogServiceFactory = integrationEventLogServiceFactory;
|
|
||||||
|
|
||||||
((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
}
|
}
|
||||||
@ -145,51 +145,25 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
|||||||
{
|
{
|
||||||
var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);
|
var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);
|
||||||
if (catalogItem == null) return NotFound();
|
if (catalogItem == null) return NotFound();
|
||||||
|
var raiseProductPriceChangedEvent = catalogItem.Price != productToUpdate.Price;
|
||||||
bool raiseProductPriceChangedEvent = false;
|
var oldPrice = catalogItem.Price;
|
||||||
IntegrationEvent priceChangedEvent = null;
|
|
||||||
|
// Update current product
|
||||||
if (catalogItem.Price != productToUpdate.Price) raiseProductPriceChangedEvent = true;
|
|
||||||
|
|
||||||
if (raiseProductPriceChangedEvent) // Create event if price has changed
|
|
||||||
{
|
|
||||||
var oldPrice = catalogItem.Price;
|
|
||||||
priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update current product
|
|
||||||
catalogItem = productToUpdate;
|
catalogItem = productToUpdate;
|
||||||
|
_catalogContext.CatalogItems.Update(catalogItem);
|
||||||
|
|
||||||
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
if (raiseProductPriceChangedEvent) // Save and publish event if price has changed
|
||||||
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
{
|
||||||
var strategy = _catalogContext.Database.CreateExecutionStrategy();
|
var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
|
||||||
var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection());
|
|
||||||
await strategy.ExecuteAsync(async () =>
|
|
||||||
{
|
|
||||||
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
|
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
|
||||||
using (var transaction = _catalogContext.Database.BeginTransaction())
|
await _catalogIntegrationEventService.SaveEventAsync(priceChangedEvent);
|
||||||
{
|
// Publish to Event Bus only if product price changed
|
||||||
_catalogContext.CatalogItems.Update(catalogItem);
|
await _catalogIntegrationEventService.PublishAsync(priceChangedEvent);
|
||||||
await _catalogContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
//Save to EventLog only if product price changed
|
|
||||||
if (raiseProductPriceChangedEvent)
|
|
||||||
{
|
|
||||||
|
|
||||||
await eventLogService.SaveEventAsync(priceChangedEvent, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.Commit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Publish to Event Bus only if product price changed
|
|
||||||
if (raiseProductPriceChangedEvent)
|
|
||||||
{
|
|
||||||
_eventBus.Publish(priceChangedEvent);
|
|
||||||
await eventLogService.MarkEventAsPublishedAsync(priceChangedEvent);
|
|
||||||
}
|
}
|
||||||
|
else // Save updated product
|
||||||
|
{
|
||||||
|
await _catalogContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
||||||
|
using System;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Catalog.API.IntegrationEvents
|
||||||
|
{
|
||||||
|
public class CatalogIntegrationEventService : ICatalogIntegrationEventService
|
||||||
|
{
|
||||||
|
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
|
||||||
|
private readonly IEventBus _eventBus;
|
||||||
|
private readonly CatalogContext _catalogContext;
|
||||||
|
private readonly IIntegrationEventLogService _eventLogService;
|
||||||
|
|
||||||
|
public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalogContext,
|
||||||
|
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
|
||||||
|
{
|
||||||
|
_catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext));
|
||||||
|
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
|
||||||
|
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
||||||
|
_eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PublishAsync(IntegrationEvent evt)
|
||||||
|
{
|
||||||
|
_eventBus.Publish(evt);
|
||||||
|
await _eventLogService.MarkEventAsPublishedAsync(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveEventAsync(IntegrationEvent evt)
|
||||||
|
{
|
||||||
|
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||||
|
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||||
|
await ResilientTransaction.New(_catalogContext)
|
||||||
|
.ExecuteAsync(async () => {
|
||||||
|
// Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction
|
||||||
|
await _catalogContext.SaveChangesAsync();
|
||||||
|
await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Catalog.API.IntegrationEvents
|
||||||
|
{
|
||||||
|
public interface ICatalogIntegrationEventService
|
||||||
|
{
|
||||||
|
Task SaveEventAsync(IntegrationEvent evt);
|
||||||
|
Task PublishAsync(IntegrationEvent evt);
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using global::Catalog.API.IntegrationEvents;
|
||||||
|
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
@ -97,10 +98,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
|
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
|
||||||
sp => (DbConnection c) => new IntegrationEventLogService(c));
|
sp => (DbConnection c) => new IntegrationEventLogService(c));
|
||||||
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<Settings>>().Value;
|
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<Settings>>().Value;
|
||||||
|
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
|
||||||
services.AddSingleton<IEventBus>(new EventBusRabbitMQ(configuration.EventBusConnection));
|
services.AddSingleton<IEventBus>(new EventBusRabbitMQ(configuration.EventBusConnection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ordering.API.IntegrationEvents;
|
||||||
using Ordering.API.IntegrationEvents.Events;
|
using Ordering.API.IntegrationEvents.Events;
|
||||||
using Ordering.Domain.Events;
|
using Ordering.Domain.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Data.Common;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified
|
namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified
|
||||||
@ -18,17 +13,15 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri
|
|||||||
: IAsyncNotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
|
: IAsyncNotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
private readonly IOrderRepository _orderRepository;
|
||||||
private readonly ILoggerFactory _logger;
|
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||||
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
|
private readonly ILoggerFactory _logger;
|
||||||
private readonly IEventBus _eventBus;
|
|
||||||
|
|
||||||
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
|
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
|
||||||
IOrderRepository orderRepository, ILoggerFactory logger, IEventBus eventBus,
|
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||||
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
|
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||||
{
|
{
|
||||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
|
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||||
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,28 +35,18 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri
|
|||||||
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
|
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
|
||||||
|
|
||||||
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid);
|
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid);
|
||||||
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
|
||||||
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
|
||||||
var orderingContext = _orderRepository.UnitOfWork as OrderingContext;
|
|
||||||
var strategy = orderingContext.Database.CreateExecutionStrategy();
|
|
||||||
|
|
||||||
var eventLogService = _integrationEventLogServiceFactory(orderingContext.Database.GetDbConnection());
|
// Using a local transaction to achieve atomicity between original Ordering database operation and
|
||||||
await strategy.ExecuteAsync(async () =>
|
// the IntegrationEventLog. Only saving event if order has been successfully persisted to db
|
||||||
{
|
await _orderingIntegrationEventService
|
||||||
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
|
.SaveEventAsync(orderStartedIntegrationEvent);
|
||||||
using (var transaction = orderingContext.Database.BeginTransaction())
|
|
||||||
{
|
// Publish ordering integration event and mark it as published
|
||||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
await _orderingIntegrationEventService
|
||||||
await eventLogService.SaveEventAsync(orderStartedIntegrationEvent, orderingContext.Database.CurrentTransaction.GetDbTransaction());
|
.PublishAsync(orderStartedIntegrationEvent);
|
||||||
transaction.Commit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler))
|
_logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler))
|
||||||
.LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }");
|
.LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }");
|
||||||
|
|
||||||
_eventBus.Publish(orderStartedIntegrationEvent);
|
|
||||||
await eventLogService.MarkEventAsPublishedAsync(orderStartedIntegrationEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ordering.API.IntegrationEvents
|
||||||
|
{
|
||||||
|
public interface IOrderingIntegrationEventService
|
||||||
|
{
|
||||||
|
Task SaveEventAsync(IntegrationEvent evt);
|
||||||
|
Task PublishAsync(IntegrationEvent evt);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
||||||
|
using System;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ordering.API.IntegrationEvents
|
||||||
|
{
|
||||||
|
public class OrderingIntegrationEventService : IOrderingIntegrationEventService
|
||||||
|
{
|
||||||
|
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
|
||||||
|
private readonly IEventBus _eventBus;
|
||||||
|
private readonly OrderingContext _orderingContext;
|
||||||
|
private readonly IIntegrationEventLogService _eventLogService;
|
||||||
|
|
||||||
|
public OrderingIntegrationEventService (IEventBus eventBus, OrderingContext orderingContext,
|
||||||
|
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
|
||||||
|
{
|
||||||
|
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
|
||||||
|
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
|
||||||
|
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
||||||
|
_eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PublishAsync(IntegrationEvent evt)
|
||||||
|
{
|
||||||
|
_eventBus.Publish(evt);
|
||||||
|
await _eventLogService.MarkEventAsPublishedAsync(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveEventAsync(IntegrationEvent evt)
|
||||||
|
{
|
||||||
|
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||||
|
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||||
|
await ResilientTransaction.New(_orderingContext)
|
||||||
|
.ExecuteAsync(async () => {
|
||||||
|
// Achieving atomicity between original ordering database operation and the IntegrationEventLog thanks to a local transaction
|
||||||
|
await _orderingContext.SaveChangesAsync();
|
||||||
|
await _eventLogService.SaveEventAsync(evt, _orderingContext.Database.CurrentTransaction.GetDbTransaction());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Extensions.DependencyInjection;
|
using Autofac.Extensions.DependencyInjection;
|
||||||
using global::Ordering.API.Infrastructure.Middlewares;
|
using global::Ordering.API.Infrastructure.Middlewares;
|
||||||
|
using global::Ordering.API.IntegrationEvents;
|
||||||
using Infrastructure;
|
using Infrastructure;
|
||||||
using Infrastructure.Auth;
|
using Infrastructure.Auth;
|
||||||
using Infrastructure.AutofacModules;
|
using Infrastructure.AutofacModules;
|
||||||
@ -101,9 +102,9 @@
|
|||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
services.AddTransient<IIdentityService, IdentityService>();
|
services.AddTransient<IIdentityService, IdentityService>();
|
||||||
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
|
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
|
||||||
sp => (DbConnection c) => new IntegrationEventLogService(c));
|
sp => (DbConnection c) => new IntegrationEventLogService(c));
|
||||||
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
|
||||||
services.AddSingleton<IEventBus>(new EventBusRabbitMQ(Configuration["EventBusConnection"]));
|
services.AddSingleton<IEventBus>(new EventBusRabbitMQ(Configuration["EventBusConnection"]));
|
||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user