From b938e638456845f8292fdff25991d8bf848c93eb Mon Sep 17 00:00:00 2001 From: Denis Date: Mon, 4 Apr 2022 12:28:46 +0700 Subject: [PATCH] Commitable transaction instead of DbTransaction --- .../CommunicationDataAccessModule.cs | 5 +++-- .../Services/ConnectionFactory.cs | 15 +++++++++++---- .../Services/IConnectionFactory.cs | 4 +++- .../OrderDataAccessModule.cs | 5 +++-- ModularMonolith/Shop.Tests.Unit/WorkflowTests.cs | 2 +- ModularMonolith/Shop.Web/Startup.cs | 2 +- ...s => CommitableTransactionPipelineBehavior.cs} | 13 +++++++++---- 7 files changed, 31 insertions(+), 15 deletions(-) rename ModularMonolith/Shop.Web/Utils/{DbTransactionPipelineBehavior.cs => CommitableTransactionPipelineBehavior.cs} (63%) diff --git a/ModularMonolith/Communication/Shop.Communication.DataAccess.MsSql/CommunicationDataAccessModule.cs b/ModularMonolith/Communication/Shop.Communication.DataAccess.MsSql/CommunicationDataAccessModule.cs index 3b53335..7ba2cae 100644 --- a/ModularMonolith/Communication/Shop.Communication.DataAccess.MsSql/CommunicationDataAccessModule.cs +++ b/ModularMonolith/Communication/Shop.Communication.DataAccess.MsSql/CommunicationDataAccessModule.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System.Transactions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Shop.Communication.DataAccess.Interfaces; using Shop.Framework.UseCases.Interfaces.Services; @@ -21,7 +22,7 @@ public override void Load(IServiceCollection services) var context = sp.GetRequiredService(); var connectionFactory = sp.GetRequiredService(); - context.Database.UseTransaction(connectionFactory.GetTransaction()); + context.Database.EnlistTransaction(connectionFactory.GetTransaction()); return context; }); diff --git a/ModularMonolith/Framework/Shop.Framework.Implementation/Services/ConnectionFactory.cs b/ModularMonolith/Framework/Shop.Framework.Implementation/Services/ConnectionFactory.cs index 02feaf1..dbb837b 100644 --- a/ModularMonolith/Framework/Shop.Framework.Implementation/Services/ConnectionFactory.cs +++ b/ModularMonolith/Framework/Shop.Framework.Implementation/Services/ConnectionFactory.cs @@ -1,4 +1,6 @@ -using System.Data.Common; +using System; +using System.Data.Common; +using System.Transactions; using Microsoft.Data.SqlClient; using Shop.Framework.UseCases.Interfaces.Services; @@ -12,7 +14,7 @@ public ConnectionFactory(string connectionString) } private DbConnection _connection; - private DbTransaction _transaction; + private Transaction _transaction; private readonly string _connectionString; public DbConnection GetConnection() @@ -25,9 +27,14 @@ public DbConnection GetConnection() return _connection; } - public DbTransaction GetTransaction() + public Transaction GetTransaction() { - return _transaction ??= GetConnection().BeginTransaction(); + return _transaction ?? throw new InvalidOperationException("Transaction not initialized"); + } + + public void SetTransaction(Transaction transaction) + { + _transaction = transaction; } public bool IsConnectionOpened => _connection != null; diff --git a/ModularMonolith/Framework/Shop.Framework.Interfaces/Services/IConnectionFactory.cs b/ModularMonolith/Framework/Shop.Framework.Interfaces/Services/IConnectionFactory.cs index c43503f..f4fe375 100644 --- a/ModularMonolith/Framework/Shop.Framework.Interfaces/Services/IConnectionFactory.cs +++ b/ModularMonolith/Framework/Shop.Framework.Interfaces/Services/IConnectionFactory.cs @@ -1,12 +1,14 @@ using System; using System.Data.Common; +using System.Transactions; namespace Shop.Framework.UseCases.Interfaces.Services { public interface IConnectionFactory : IDisposable { DbConnection GetConnection(); - DbTransaction GetTransaction(); + Transaction GetTransaction(); + void SetTransaction(Transaction transaction); bool IsConnectionOpened { get; } bool IsTransactionStarted { get; } } diff --git a/ModularMonolith/Order/Shop.Order.DataAccess.MsSql/OrderDataAccessModule.cs b/ModularMonolith/Order/Shop.Order.DataAccess.MsSql/OrderDataAccessModule.cs index d44fbf4..7f9a668 100644 --- a/ModularMonolith/Order/Shop.Order.DataAccess.MsSql/OrderDataAccessModule.cs +++ b/ModularMonolith/Order/Shop.Order.DataAccess.MsSql/OrderDataAccessModule.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System.Transactions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Shop.Framework.UseCases.Interfaces.Services; using Shop.Order.DataAccess.Interfaces; @@ -21,7 +22,7 @@ public override void Load(IServiceCollection services) var context = sp.GetRequiredService(); var connectionFactory = sp.GetRequiredService(); - context.Database.UseTransaction(connectionFactory.GetTransaction()); + context.Database.EnlistTransaction(connectionFactory.GetTransaction()); return context; }); diff --git a/ModularMonolith/Shop.Tests.Unit/WorkflowTests.cs b/ModularMonolith/Shop.Tests.Unit/WorkflowTests.cs index cc0c4c4..96d1e47 100644 --- a/ModularMonolith/Shop.Tests.Unit/WorkflowTests.cs +++ b/ModularMonolith/Shop.Tests.Unit/WorkflowTests.cs @@ -140,7 +140,7 @@ private ServiceCollection CreateServiceProvider(IConfiguration configuration) services.AddMediatR(assemblies); services.AddAutoMapper(assemblies); - services.AddScoped(typeof(IPipelineBehavior<,>), typeof(DbTransactionPipelineBehavior<,>)); + services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CommitableTransactionPipelineBehavior<,>)); services.RegisterModule(configuration); services.RegisterModule(configuration); diff --git a/ModularMonolith/Shop.Web/Startup.cs b/ModularMonolith/Shop.Web/Startup.cs index 40e71dd..a363848 100644 --- a/ModularMonolith/Shop.Web/Startup.cs +++ b/ModularMonolith/Shop.Web/Startup.cs @@ -53,7 +53,7 @@ public void ConfigureServices(IServiceCollection services) services.RegisterModule(Configuration); services.RegisterModule(Configuration); - services.AddScoped(typeof(IPipelineBehavior<,>), typeof(DbTransactionPipelineBehavior<,>)); + services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CommitableTransactionPipelineBehavior<,>)); var location = Assembly.GetExecutingAssembly().Location; var assemblies = Directory.EnumerateFiles(Path.GetDirectoryName(location), "Shop*UseCases.dll") diff --git a/ModularMonolith/Shop.Web/Utils/DbTransactionPipelineBehavior.cs b/ModularMonolith/Shop.Web/Utils/CommitableTransactionPipelineBehavior.cs similarity index 63% rename from ModularMonolith/Shop.Web/Utils/DbTransactionPipelineBehavior.cs rename to ModularMonolith/Shop.Web/Utils/CommitableTransactionPipelineBehavior.cs index 3f92d7d..aaaeb07 100644 --- a/ModularMonolith/Shop.Web/Utils/DbTransactionPipelineBehavior.cs +++ b/ModularMonolith/Shop.Web/Utils/CommitableTransactionPipelineBehavior.cs @@ -1,17 +1,18 @@ using System.Threading; using System.Threading.Tasks; +using System.Transactions; using MediatR; using Shop.Framework.UseCases.Interfaces.Services; using Shop.Framework.UseCases.Interfaces.Transactions; namespace Shop.Web.Utils { - public class DbTransactionPipelineBehavior : IPipelineBehavior + public class CommitableTransactionPipelineBehavior : IPipelineBehavior where TRequest : IRequest, ITransactionalRequest { private readonly IConnectionFactory _connectionFactory; - public DbTransactionPipelineBehavior(IConnectionFactory connectionFactory) + public CommitableTransactionPipelineBehavior(IConnectionFactory connectionFactory) { _connectionFactory = connectionFactory; } @@ -24,11 +25,15 @@ public async Task Handle(TRequest request, CancellationToken cancella } await using var connection = _connectionFactory.GetConnection(); - await using var transaction = _connectionFactory.GetTransaction(); + + using var transaction = new CommittableTransaction( + new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }); + + _connectionFactory.SetTransaction(transaction); var result = await next(); - await transaction.CommitAsync(cancellationToken); + transaction.Commit(); return result; }