diff --git a/src/HelloShop.OrderingService/Behaviors/TransactionBehavior.cs b/src/HelloShop.OrderingService/Behaviors/TransactionBehavior.cs new file mode 100644 index 0000000..14ff297 --- /dev/null +++ b/src/HelloShop.OrderingService/Behaviors/TransactionBehavior.cs @@ -0,0 +1,53 @@ +using HelloShop.OrderingService.Extensions; +using HelloShop.OrderingService.Infrastructure; +using MediatR; +using Microsoft.EntityFrameworkCore; +using System.Data; + +namespace HelloShop.OrderingService.Behaviors +{ + public class TransactionBehavior(OrderingServiceDbContext dbContext, ILoggerFactory loggerFactory) : IPipelineBehavior where TRequest : IRequest + { + private readonly ILogger> _logger = loggerFactory.CreateLogger>(); + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + TResponse? response = default; + + string typeName = request.GetGenericTypeName(); + + try + { + if (dbContext.Database.CurrentTransaction != null) + { + return await next(); + } + + var strategy = dbContext.Database.CreateExecutionStrategy(); + + await strategy.ExecuteAsync(async () => + { + using var transaction = await dbContext.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); + + using (_logger.BeginScope(new List> { new("TransactionContext", transaction.TransactionId) })) + { + _logger.LogInformation("Begin transaction {TransactionId} for {CommandName} {Command}", transaction.TransactionId, typeName, request); + + response = await next(); + + _logger.LogInformation("Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); + } + + await transaction.CommitAsync(); + }); + + return response ?? throw new ApplicationException($"Command {typeName} returned null response"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error Handling transaction for {CommandName} {Request}", typeName, request); + throw; + } + } + } +} diff --git a/src/HelloShop.OrderingService/Commands/Orders/CreateOrderCommandHandler.cs b/src/HelloShop.OrderingService/Commands/Orders/CreateOrderCommandHandler.cs index 1d9c612..dd213fc 100644 --- a/src/HelloShop.OrderingService/Commands/Orders/CreateOrderCommandHandler.cs +++ b/src/HelloShop.OrderingService/Commands/Orders/CreateOrderCommandHandler.cs @@ -1,16 +1,48 @@ // Copyright (c) HelloShop Corporation. All rights reserved. // See the license file in the project root for more information. +using AutoMapper; +using HelloShop.OrderingService.Entities.Buyers; +using HelloShop.OrderingService.Entities.Orders; +using HelloShop.OrderingService.Infrastructure; using HelloShop.OrderingService.Services; using MediatR; +using Microsoft.EntityFrameworkCore; namespace HelloShop.OrderingService.Commands.Orders { - public class CreateOrderCommandHandler : IRequestHandler + public class CreateOrderCommandHandler(OrderingServiceDbContext dbContext, IMapper mapper) : IRequestHandler { - public Task Handle(CreateOrderCommand request, CancellationToken cancellationToken) + public async Task Handle(CreateOrderCommand request, CancellationToken cancellationToken) { - throw new NotImplementedException(); + Address address = mapper.Map
(request); + + IEnumerable orderItems = mapper.Map>(request.OrderItems); + + Buyer? buyer = await dbContext.Set().FindAsync([request.UserId], cancellationToken); + + if (buyer == null) + { + buyer = new() { Id = request.UserId, Name = request.UserName }; + await dbContext.AddAsync(buyer, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + } + + PaymentMethod? paymentMethod = await dbContext.Set().SingleOrDefaultAsync(pm => pm.BuyerId == buyer.Id && pm.CardNumber == request.CardNumber, cancellationToken); + + if (paymentMethod == null) + { + paymentMethod = new() { BuyerId = buyer.Id, Alias = request.CardAlias, CardNumber = request.CardNumber, SecurityNumber = request.CardSecurityNumber, CardHolderName = request.CardHolderName, Expiration = request.CardExpiration }; + await dbContext.AddAsync(paymentMethod, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + } + + Order order = new(buyer.Id, address, orderItems) { PaymentMethodId = paymentMethod.Id }; + + await dbContext.AddAsync(order, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + + return await Task.FromResult(true); } } diff --git a/src/HelloShop.OrderingService/Extensions/Extensions.cs b/src/HelloShop.OrderingService/Extensions/Extensions.cs index ed4b93c..2492695 100644 --- a/src/HelloShop.OrderingService/Extensions/Extensions.cs +++ b/src/HelloShop.OrderingService/Extensions/Extensions.cs @@ -29,6 +29,7 @@ namespace HelloShop.OrderingService.Extensions options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); options.AddBehavior(typeof(LoggingBehavior<,>)); options.AddBehavior(typeof(ValidatorBehavior<,>)); + options.AddBehavior(typeof(TransactionBehavior<,>)); }); builder.Services.AddModelMapper().AddModelValidator();