From 9e526c54faedb5c95b75e9d8aa0ca80a022c3ea0 Mon Sep 17 00:00:00 2001 From: "HOME-LAPTOP\\kshkulev" Date: Mon, 27 Oct 2025 18:09:09 +0900 Subject: [PATCH] Add transaction support between services --- .../CampaignManagementService.cs | 7 +++--- ...equiredRegistryElementsManager_15_7_0_1.cs | 16 +++++++++++-- .../CampaignManagers/ManagerBase.cs | 4 ++-- .../CampaignManagers/ManagerFactory.cs | 7 +++--- .../OperationExecutionService.cs | 7 +++--- .../OperationExecutors/ExecutorBase.cs | 4 ++-- .../OperationExecutors/ExecutorFactory.cs | 8 +++---- .../ExportNsiItemExecutor_15_7_0_1.cs | 5 ++-- Hcs.WebApp/Services/HcsServiceBase.cs | 15 ++++++++++++ Hcs.WebApp/Services/HeadquartersService.cs | 22 ++++++++--------- Hcs.WebApp/Services/RegistryService.cs | 24 ++++++++++++++----- 11 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 Hcs.WebApp/Services/HcsServiceBase.cs diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs index 3715bcf..9060609 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs @@ -20,24 +20,23 @@ namespace Hcs.WebApp.BackgroundServices { await InitializeStateAsync(); - using var scope = scopeFactory.CreateScope(); - var headquartersService = scope.ServiceProvider.GetRequiredService(); - while (!stoppingToken.IsCancellationRequested) { while (campaignManagementState.TryDequeueCampaign(out var campaign)) { if (stoppingToken.IsCancellationRequested) return; + using var scope = scopeFactory.CreateScope(); try { - var manager = managerFactory.CreateManager(campaign); + var manager = managerFactory.CreateManager(scope, campaign); await manager.StartAsync(stoppingToken); managers.Add(manager); } catch (Exception e) { + var headquartersService = scope.ServiceProvider.GetRequiredService(); await headquartersService.SetCampaignEndedWithFail(campaign.Id, e.Message); } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportRequiredRegistryElementsManager_15_7_0_1.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportRequiredRegistryElementsManager_15_7_0_1.cs index 59abf00..f3e273e 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportRequiredRegistryElementsManager_15_7_0_1.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportRequiredRegistryElementsManager_15_7_0_1.cs @@ -1,12 +1,24 @@ using Hcs.WebApp.Data.Hcs; +using Hcs.WebApp.Services; namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public class ExportRequiredRegistryElementsManager_15_7_0_1(OperationExecutionState state, IServiceScopeFactory scopeFactory, Campaign campaign) : ManagerBase(state, scopeFactory, campaign) + public class ExportRequiredRegistryElementsManager_15_7_0_1(IServiceScope scope, OperationExecutionState state, Campaign campaign) : ManagerBase(scope, state, campaign) { public override async Task StartAsync(CancellationToken cancellationToken) { - // TODO + var headquartersService = scope.ServiceProvider.GetRequiredService(); + var registryService = scope.ServiceProvider.GetRequiredService(); + using var context = headquartersService.GetNewContext(); + using var transaction = await context.Database.BeginTransactionAsync(cancellationToken); + try + { + // TODO + } + catch + { + throw; + } } } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs index 43f868e..e7efb4f 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs @@ -2,10 +2,10 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public abstract class ManagerBase(OperationExecutionState state, IServiceScopeFactory scopeFactory, Campaign campaign) : IManager + public abstract class ManagerBase(IServiceScope scope, OperationExecutionState state, Campaign campaign) : IManager { + protected readonly IServiceScope scope = scope; protected readonly OperationExecutionState state = state; - protected readonly IServiceScopeFactory scopeFactory = scopeFactory; protected readonly Campaign campaign = campaign; public IManager.ManagerState State { get; } = IManager.ManagerState.Created; diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs index 809fe92..0019a9c 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs @@ -2,17 +2,16 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public class ManagerFactory(OperationExecutionState state, IServiceScopeFactory scopeFactory) + public class ManagerFactory(OperationExecutionState state) { protected readonly OperationExecutionState state = state; - protected readonly IServiceScopeFactory scopeFactory = scopeFactory; - public IManager CreateManager(Campaign campaign) + public IManager CreateManager(IServiceScope scope, Campaign campaign) { switch (campaign.Type) { case Campaign.CampaignType.ExportRequiredRegistryElements_15_7_0_1: - return new ExportRequiredRegistryElementsManager_15_7_0_1(state, scopeFactory, campaign); + return new ExportRequiredRegistryElementsManager_15_7_0_1(scope, state, campaign); } throw new NotImplementedException(); diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs b/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs index 8930cfa..34cbf0f 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs @@ -25,19 +25,18 @@ namespace Hcs.WebApp.BackgroundServices InitializeClient(); - var scope = scopeFactory.CreateScope(); - var headquartersService = scope.ServiceProvider.GetRequiredService(); - while (!stoppingToken.IsCancellationRequested) { while (state.TryDequeueOperation(out var operation)) { if (stoppingToken.IsCancellationRequested) return; + var scope = scopeFactory.CreateScope(); + var headquartersService = scope.ServiceProvider.GetRequiredService(); var messageGuid = string.Empty; try { - var executor = executorFactory.CreateExecutor(client, operation); + var executor = executorFactory.CreateExecutor(scope, client, operation); await headquartersService.SetOperationStarted(operation.Id); messageGuid = await executor.ExecuteAsync(stoppingToken); } diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorBase.cs b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorBase.cs index 6a8c10b..74cb13b 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorBase.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorBase.cs @@ -3,10 +3,10 @@ using Hcs.WebApp.Data.Hcs; namespace Hcs.WebApp.BackgroundServices.OperationExecutors { - public abstract class ExecutorBase(IClient client, IServiceScopeFactory scopeFactory, Operation operation) : IExecutor + public abstract class ExecutorBase(IClient client, IServiceScope scope, Operation operation) : IExecutor { protected readonly IClient client = client; - protected readonly IServiceScopeFactory scopeFactory = scopeFactory; + protected readonly IServiceScope scope = scope; protected readonly Operation operation = operation; public abstract Task ExecuteAsync(CancellationToken cancellationToken); diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs index 697d5a4..2cb13de 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs @@ -4,16 +4,14 @@ using Hcs.WebApp.Data.Hcs; namespace Hcs.WebApp.BackgroundServices.OperationExecutors { - public class ExecutorFactory(IServiceScopeFactory scopeFactory) + public class ExecutorFactory { - protected readonly IServiceScopeFactory scopeFactory = scopeFactory; - - public IExecutor CreateExecutor(IClient client, Operation operation) + public IExecutor CreateExecutor(IServiceScope scope, IClient client, Operation operation) { switch (operation.Type) { case Operation.OperationType.NsiCommon_ExportNsiItem_15_7_0_1: - return new ExportNsiItemExecutor_15_7_0_1(client, scopeFactory, operation); + return new ExportNsiItemExecutor_15_7_0_1(client, scope, operation); } throw new NotImplementedException(); diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutors/NsiCommon/ExportNsiItemExecutor_15_7_0_1.cs b/Hcs.WebApp/BackgroundServices/OperationExecutors/NsiCommon/ExportNsiItemExecutor_15_7_0_1.cs index 253ef78..57a7468 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutors/NsiCommon/ExportNsiItemExecutor_15_7_0_1.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutors/NsiCommon/ExportNsiItemExecutor_15_7_0_1.cs @@ -5,13 +5,12 @@ using Hcs.WebApp.Services; namespace Hcs.WebApp.BackgroundServices.OperationExecutors.NsiCommon { - public class ExportNsiItemExecutor_15_7_0_1(IClient client, IServiceScopeFactory scopeFactory, Operation operation) : ExecutorBase(client, scopeFactory, operation) + public class ExportNsiItemExecutor_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ExecutorBase(client, scope, operation) { public override async Task ExecuteAsync(CancellationToken cancellationToken) { - using var scope = scopeFactory.CreateScope(); var registryService = scope.ServiceProvider.GetRequiredService(); - var registry = await registryService.GetRegistryByOperationId(operation.Id); + var registry = await registryService.GetRegistryByOperationIdAsync(operation.Id); return await client.NsiCommon.RequestExportNsiItemAsync(registry.Number, ListGroup.NSI, cancellationToken); } } diff --git a/Hcs.WebApp/Services/HcsServiceBase.cs b/Hcs.WebApp/Services/HcsServiceBase.cs new file mode 100644 index 0000000..c66e11d --- /dev/null +++ b/Hcs.WebApp/Services/HcsServiceBase.cs @@ -0,0 +1,15 @@ +using Hcs.WebApp.Data.Hcs; +using Microsoft.EntityFrameworkCore; + +namespace Hcs.WebApp.Services +{ + public abstract class HcsServiceBase(IDbContextFactory factory) + { + private readonly IDbContextFactory factory = factory; + + public HcsDbContext GetNewContext() + { + return factory.CreateDbContext(); + } + } +} diff --git a/Hcs.WebApp/Services/HeadquartersService.cs b/Hcs.WebApp/Services/HeadquartersService.cs index a162c62..9f30d5f 100644 --- a/Hcs.WebApp/Services/HeadquartersService.cs +++ b/Hcs.WebApp/Services/HeadquartersService.cs @@ -3,19 +3,17 @@ using Microsoft.EntityFrameworkCore; namespace Hcs.WebApp.Services { - public class HeadquartersService(IDbContextFactory factory) + public class HeadquartersService(IDbContextFactory factory) : HcsServiceBase(factory) { - private readonly IDbContextFactory factory = factory; - public async Task HasActiveCampaignAsync(Campaign.CampaignType type) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); return await context.Campaigns.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue); } public async Task> GetInitiatedCampaignAsync() { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); return await (from campaign in context.Campaigns where !campaign.EndedAt.HasValue select campaign).ToListAsync(); @@ -23,7 +21,7 @@ namespace Hcs.WebApp.Services public async Task> GetInitiatedOperationsAsync() { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); return await (from operation in context.Operations where !operation.EndedAt.HasValue && string.IsNullOrEmpty(operation.MessageGuid) select operation).ToListAsync(); @@ -31,7 +29,7 @@ namespace Hcs.WebApp.Services public async Task InitiateCampaignAsync(Campaign.CampaignType type, string initiatorId) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var campaign = new Campaign() { Type = type, @@ -45,7 +43,7 @@ namespace Hcs.WebApp.Services public async Task InitiateOperationAsync(int campaignId, Operation.OperationType type) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var operation = new Operation() { CampaignId = campaignId, @@ -59,7 +57,7 @@ namespace Hcs.WebApp.Services public async Task SetCampaignEndedWithFail(int campaignId, string failureReason) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var campaign = await context.Campaigns.FirstOrDefaultAsync(x => x.Id == campaignId); if (campaign != null) { @@ -72,7 +70,7 @@ namespace Hcs.WebApp.Services public async Task SetOperationStarted(int operationId) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); if (operation != null) { @@ -84,7 +82,7 @@ namespace Hcs.WebApp.Services public async Task SetOperationEndedWithFail(int operationId, string failureReason) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); if (operation != null) { @@ -97,7 +95,7 @@ namespace Hcs.WebApp.Services public async Task SetOperationMessageGuidAsync(int operationId, string messageGuid) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); if (operation != null) { diff --git a/Hcs.WebApp/Services/RegistryService.cs b/Hcs.WebApp/Services/RegistryService.cs index d081483..ac14101 100644 --- a/Hcs.WebApp/Services/RegistryService.cs +++ b/Hcs.WebApp/Services/RegistryService.cs @@ -3,22 +3,34 @@ using Microsoft.EntityFrameworkCore; namespace Hcs.WebApp.Services { - public class RegistryService(IDbContextFactory factory) + public class RegistryService(IDbContextFactory factory) : HcsServiceBase(factory) { - private readonly IDbContextFactory factory = factory; - public async Task> GetAllRegistriesAsync(bool isCommon) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); return await (from registry in context.Registries where registry.IsCommon == isCommon select registry).ToListAsync(); } - public async Task GetRegistryByOperationId(int operationId) + public async Task GetRegistryByOperationIdAsync(int operationId) { - using var context = factory.CreateDbContext(); + using var context = GetNewContext(); return await context.Registries.SingleAsync(x => x.LastSyncOperationId == operationId); } + + public async Task> GetRegistriesByOperationId(int operationId) + { + using var context = GetNewContext(); + return await (from registry in context.Registries + where registry.LastSyncOperationId == operationId + select registry).ToListAsync(); + } + + public async Task SetOperationIdToAllRegistries(HcsDbContext context, int operationId) + { + await context.Registries.ForEachAsync(x => x.LastSyncOperationId = operationId); + await context.SaveChangesAsync(); + } } }