diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 96e34bcf2..4f2e31123 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -83,6 +83,24 @@ services:
ports:
- "5102:80"
+ ordering.backgroundtasks:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - CheckUpdateTime=30000
+ - GracePeriodTime=1
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5111:80"
+
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
@@ -219,4 +237,5 @@ services:
rabbitmq:
ports:
- "15672:15672"
- - "5672:5672"
\ No newline at end of file
+ - "5672:5672"
+
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 552350251..fac480f2e 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -90,6 +90,24 @@ services:
ports:
- "5102:80"
+ ordering.backgroundtasks:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - CheckUpdateTime=30000
+ - GracePeriodTime=1
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5111:80"
+
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
diff --git a/docker-compose.yml b/docker-compose.yml
index d8b7318ce..b237ad04f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -38,6 +38,15 @@ services:
- sql.data
- rabbitmq
+ ordering.backgroundtasks:
+ image: eshop/ordering.backgroundtasks:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
+ depends_on:
+ - sql.data
+ - rabbitmq
+
marketing.api:
image: eshop/marketing.api:${TAG:-latest}
build:
@@ -106,4 +115,4 @@ services:
image: redis:alpine
rabbitmq:
- image: rabbitmq:3-management-alpine
+ image: rabbitmq:3-management-alpine
\ No newline at end of file
diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln
index e4ddff1c2..d7d3b059c 100644
--- a/eShopOnContainers-ServicesAndWebApps.sln
+++ b/eShopOnContainers-ServicesAndWebApps.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27004.2009
+VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}"
EndProject
@@ -97,6 +97,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebHost", "WebHost", "{1815
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebHost.Customization", "src\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj", "{15F4B3AA-89B6-4A0D-9051-414305974781}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{2FF56999-0266-48B2-ACC1-FEBC482A5105}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -1311,6 +1313,54 @@ Global
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x64.Build.0 = Release|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x86.ActiveCfg = Release|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x86.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x64.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x86.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|ARM.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x64.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x86.Build.0 = Debug|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|ARM.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|ARM.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhone.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x64.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x64.Build.0 = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x86.ActiveCfg = Release|Any CPU
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1357,6 +1407,7 @@ Global
{969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{1815B651-941C-466B-AE33-D1D7EEB8F77F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{15F4B3AA-89B6-4A0D-9051-414305974781} = {1815B651-941C-466B-AE33-D1D7EEB8F77F}
+ {2FF56999-0266-48B2-ACC1-FEBC482A5105} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9}
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
index f2129be69..49a417635 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
@@ -92,9 +92,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
policy.Execute(() =>
{
+ var properties = channel.CreateBasicProperties();
+ properties.DeliveryMode = 2; // persistent
+
channel.BasicPublish(exchange: BROKER_NAME,
routingKey: eventName,
- basicProperties: null,
+ mandatory:true,
+ basicProperties: properties,
body: body);
});
}
@@ -184,6 +188,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
var message = Encoding.UTF8.GetString(ea.Body);
await ProcessEvent(eventName, message);
+
+ channel.BasicAck(ea.DeliveryTag,multiple:false);
};
channel.BasicConsume(queue: _queueName,
diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs
index 205b3c75a..7d11b9d90 100644
--- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs
+++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs
@@ -16,68 +16,68 @@ namespace Ordering.API.Infrastructure.HostedServices
/// https://github.com/aspnet/Hosting/blob/712c992ca827576c05923e6a134ca0bec87af4df/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs
///
///
- public abstract class BackgroundService : IHostedService, IDisposable
- {
- private Task _executingTask;
- private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
+ //public abstract class BackgroundService : IHostedService, IDisposable
+ //{
+ // private Task _executingTask;
+ // private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
- ///
- /// This method is called when the starts. The implementation should return a task that represents
- /// the lifetime of the long running operation(s) being performed.
- ///
- /// Triggered when is called.
- /// A that represents the long running operations.
- protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
+ // ///
+ // /// This method is called when the starts. The implementation should return a task that represents
+ // /// the lifetime of the long running operation(s) being performed.
+ // ///
+ // /// Triggered when is called.
+ // /// A that represents the long running operations.
+ // protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
- ///
- /// Triggered when the application host is ready to start the service.
- ///
- /// Indicates that the start process has been aborted.
- public virtual Task StartAsync(CancellationToken cancellationToken)
- {
- // Store the task we're executing
- _executingTask = ExecuteAsync(_stoppingCts.Token);
+ // ///
+ // /// Triggered when the application host is ready to start the service.
+ // ///
+ // /// Indicates that the start process has been aborted.
+ // public virtual Task StartAsync(CancellationToken cancellationToken)
+ // {
+ // // Store the task we're executing
+ // _executingTask = ExecuteAsync(_stoppingCts.Token);
- // If the task is completed then return it, this will bubble cancellation and failure to the caller
- if (_executingTask.IsCompleted)
- {
- return _executingTask;
- }
+ // // If the task is completed then return it, this will bubble cancellation and failure to the caller
+ // if (_executingTask.IsCompleted)
+ // {
+ // return _executingTask;
+ // }
- // Otherwise it's running
- return Task.CompletedTask;
- }
+ // // Otherwise it's running
+ // return Task.CompletedTask;
+ // }
- ///
- /// Triggered when the application host is performing a graceful shutdown.
- ///
- /// Indicates that the shutdown process should no longer be graceful.
- public virtual async Task StopAsync(CancellationToken cancellationToken)
- {
- // Stop called without start
- if (_executingTask == null)
- {
- return;
- }
+ // ///
+ // /// Triggered when the application host is performing a graceful shutdown.
+ // ///
+ // /// Indicates that the shutdown process should no longer be graceful.
+ // public virtual async Task StopAsync(CancellationToken cancellationToken)
+ // {
+ // // Stop called without start
+ // if (_executingTask == null)
+ // {
+ // return;
+ // }
- try
- {
- // Signal cancellation to the executing method
- _stoppingCts.Cancel();
- }
- finally
- {
- // Wait until the task completes or the stop token triggers
- await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
- }
+ // try
+ // {
+ // // Signal cancellation to the executing method
+ // _stoppingCts.Cancel();
+ // }
+ // finally
+ // {
+ // // Wait until the task completes or the stop token triggers
+ // await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
+ // }
- }
+ // }
- public virtual void Dispose()
- {
- _stoppingCts.Cancel();
- }
- }
+ // public virtual void Dispose()
+ // {
+ // _stoppingCts.Cancel();
+ // }
+ //}
}
diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs
index 52b1b2da7..8117ce38b 100644
--- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs
+++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs
@@ -12,79 +12,79 @@
using System.Threading;
using System.Threading.Tasks;
- public class GracePeriodManagerService : BackgroundService
- {
- private readonly OrderingSettings _settings;
- private readonly ILogger _logger;
- private readonly IEventBus _eventBus;
+ //public class GracePeriodManagerService : BackgroundService
+ //{
+ // private readonly OrderingSettings _settings;
+ // private readonly ILogger _logger;
+ // private readonly IEventBus _eventBus;
- public GracePeriodManagerService(IOptions settings,
- IEventBus eventBus,
- ILogger logger)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
- _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings));
- }
+ // public GracePeriodManagerService(IOptions settings,
+ // IEventBus eventBus,
+ // ILogger logger)
+ // {
+ // _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ // _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
+ // _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings));
+ // }
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- _logger.LogDebug($"GracePeriod background task is starting.");
+ // protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ // {
+ // _logger.LogDebug($"GracePeriod background task is starting.");
- stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriod background task is stopping."));
+ // stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriod background task is stopping."));
- while (!stoppingToken.IsCancellationRequested)
- {
- _logger.LogDebug($"GracePeriod background task is doing background work.");
+ // while (!stoppingToken.IsCancellationRequested)
+ // {
+ // _logger.LogDebug($"GracePeriod background task is doing background work.");
- CheckConfirmedGracePeriodOrders();
+ // CheckConfirmedGracePeriodOrders();
- await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
+ // await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
- continue;
- }
+ // continue;
+ // }
- _logger.LogDebug($"GracePeriod background task is stopping.");
+ // _logger.LogDebug($"GracePeriod background task is stopping.");
- }
-
- private void CheckConfirmedGracePeriodOrders()
- {
- _logger.LogDebug($"Checking confirmed grace period orders");
-
- var orderIds = GetConfirmedGracePeriodOrders();
-
- _logger.LogDebug($"GracePeriod sent a .");
- foreach (var orderId in orderIds)
- {
- var gracePeriodConfirmedEvent = new GracePeriodConfirmedIntegrationEvent(orderId);
- _eventBus.Publish(gracePeriodConfirmedEvent);
- }
- }
-
- private IEnumerable GetConfirmedGracePeriodOrders()
- {
- IEnumerable orderIds = new List();
-
- using (var conn = new SqlConnection(_settings.ConnectionString))
- {
- try
- {
- conn.Open();
- orderIds = conn.Query(
- @"SELECT Id FROM [ordering].[orders]
- WHERE DATEDIFF(minute, [OrderDate], GETDATE()) >= @GracePeriodTime
- AND [OrderStatusId] = 1",
- new { GracePeriodTime = _settings.GracePeriodTime });
- }
- catch (SqlException exception)
- {
- _logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}");
- }
-
- }
-
- return orderIds;
- }
- }
+ // }
+
+ // private void CheckConfirmedGracePeriodOrders()
+ // {
+ // _logger.LogDebug($"Checking confirmed grace period orders");
+
+ // var orderIds = GetConfirmedGracePeriodOrders();
+
+ // _logger.LogDebug($"GracePeriod sent a .");
+ // foreach (var orderId in orderIds)
+ // {
+ // var gracePeriodConfirmedEvent = new GracePeriodConfirmedIntegrationEvent(orderId);
+ // _eventBus.Publish(gracePeriodConfirmedEvent);
+ // }
+ // }
+
+ // private IEnumerable GetConfirmedGracePeriodOrders()
+ // {
+ // IEnumerable orderIds = new List();
+
+ // using (var conn = new SqlConnection(_settings.ConnectionString))
+ // {
+ // try
+ // {
+ // conn.Open();
+ // orderIds = conn.Query(
+ // @"SELECT Id FROM [ordering].[orders]
+ // WHERE DATEDIFF(minute, [OrderDate], GETDATE()) >= @GracePeriodTime
+ // AND [OrderStatusId] = 1",
+ // new { GracePeriodTime = _settings.GracePeriodTime });
+ // }
+ // catch (SqlException exception)
+ // {
+ // _logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}");
+ // }
+
+ // }
+
+ // return orderIds;
+ // }
+ //}
}
diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs
index 0c7272f6b..9fdc026fa 100644
--- a/src/Services/Ordering/Ordering.API/Startup.cs
+++ b/src/Services/Ordering/Ordering.API/Startup.cs
@@ -58,8 +58,8 @@
}).AddControllersAsServices(); //Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
- // Configure GracePeriodManager Hosted Service
- services.AddSingleton();
+ //// Configure GracePeriodManager Hosted Service
+ //services.AddSingleton();
services.AddTransient();
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Configuration/BackgroundTaskSettings.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Configuration/BackgroundTaskSettings.cs
new file mode 100644
index 000000000..2b42f6084
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Configuration/BackgroundTaskSettings.cs
@@ -0,0 +1,13 @@
+namespace Ordering.BackgroundTasks.Configuration
+{
+ public class BackgroundTaskSettings
+ {
+ public string ConnectionString { get; set; }
+
+ public string EventBusConnection { get; set; }
+
+ public int GracePeriodTime { get; set; }
+
+ public int CheckUpdateTime { get; set; }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
new file mode 100644
index 000000000..391d8c17b
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
@@ -0,0 +1,18 @@
+FROM microsoft/aspnetcore:2.0.3 AS base
+WORKDIR /app
+EXPOSE 80
+
+FROM microsoft/aspnetcore-build:2.0 AS build
+WORKDIR /src
+COPY . .
+RUN dotnet restore -nowarn:msb3202,nu1503
+WORKDIR /src/src/Services/Ordering/Ordering.BackgroundTasks
+RUN dotnet build --no-restore -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish --no-restore -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENTRYPOINT ["dotnet", "Ordering.BackgroundTasks.dll"]
\ No newline at end of file
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/IntegrationEvents/GracePeriodConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.BackgroundTasks/IntegrationEvents/GracePeriodConfirmedIntegrationEvent.cs
new file mode 100644
index 000000000..df008ad90
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/IntegrationEvents/GracePeriodConfirmedIntegrationEvent.cs
@@ -0,0 +1,12 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+
+namespace Ordering.BackgroundTasks.IntegrationEvents
+{
+ public class GracePeriodConfirmedIntegrationEvent : IntegrationEvent
+ {
+ public int OrderId { get; }
+
+ public GracePeriodConfirmedIntegrationEvent(int orderId) =>
+ OrderId = orderId;
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj
new file mode 100644
index 000000000..0fe3a5b36
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj
@@ -0,0 +1,31 @@
+
+
+
+ netcoreapp2.0
+ ..\..\..\..\docker-compose.dcproj
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs
new file mode 100644
index 000000000..2ce78136f
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Ordering.BackgroundTasks
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ BuildWebHost(args).Run();
+ }
+
+ public static IWebHost BuildWebHost(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseStartup()
+ .ConfigureLogging((hostingContext, builder) =>
+ {
+ builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ builder.AddDebug();
+ builder.AddConsole();
+ }).Build();
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json b/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json
new file mode 100644
index 000000000..6d60a7d28
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:5161/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Ordering.BackgroundTasks": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:5162/"
+ }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs
new file mode 100644
index 000000000..62215d126
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs
@@ -0,0 +1,158 @@
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Azure.ServiceBus;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.HealthChecks;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Ordering.BackgroundTasks.Configuration;
+using Ordering.BackgroundTasks.Tasks;
+using RabbitMQ.Client;
+using System;
+
+namespace Ordering.BackgroundTasks
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public IServiceProvider ConfigureServices(IServiceCollection services)
+ {
+ //add health check for this service
+ services.AddHealthChecks(checks =>
+ {
+ var minutes = 1;
+
+ if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
+ {
+ minutes = minutesParsed;
+ }
+ checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
+ });
+
+ //configure settings
+
+ services.Configure(Configuration);
+ services.AddOptions();
+
+ //configure background task
+
+ services.AddSingleton();
+
+ //configure event bus related services
+
+ if (Configuration.GetValue("AzureServiceBusEnabled"))
+ {
+ services.AddSingleton(sp =>
+ {
+ var logger = sp.GetRequiredService>();
+
+ var serviceBusConnectionString = Configuration["EventBusConnection"];
+ var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString);
+
+ return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger);
+ });
+ }
+ else
+ {
+ services.AddSingleton(sp =>
+ {
+ var logger = sp.GetRequiredService>();
+
+
+ var factory = new ConnectionFactory()
+ {
+ HostName = Configuration["EventBusConnection"]
+ };
+
+ if (!string.IsNullOrEmpty(Configuration["EventBusUserName"]))
+ {
+ factory.UserName = Configuration["EventBusUserName"];
+ }
+
+ if (!string.IsNullOrEmpty(Configuration["EventBusPassword"]))
+ {
+ factory.Password = Configuration["EventBusPassword"];
+ }
+
+ var retryCount = 5;
+ if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
+ {
+ retryCount = int.Parse(Configuration["EventBusRetryCount"]);
+ }
+
+ return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
+ });
+ }
+
+ RegisterEventBus(services);
+
+ //create autofac based service provider
+ var container = new ContainerBuilder();
+ container.Populate(services);
+
+
+ return new AutofacServiceProvider(container.Build());
+ }
+
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+
+ }
+
+
+ private void RegisterEventBus(IServiceCollection services)
+ {
+ var subscriptionClientName = Configuration["SubscriptionClientName"];
+
+ if (Configuration.GetValue("AzureServiceBusEnabled"))
+ {
+ services.AddSingleton(sp =>
+ {
+ var serviceBusPersisterConnection = sp.GetRequiredService();
+ var iLifetimeScope = sp.GetRequiredService();
+ var logger = sp.GetRequiredService>();
+ var eventBusSubcriptionsManager = sp.GetRequiredService();
+
+ return new EventBusServiceBus(serviceBusPersisterConnection, logger,
+ eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
+ });
+ }
+ else
+ {
+ services.AddSingleton(sp =>
+ {
+ var rabbitMQPersistentConnection = sp.GetRequiredService();
+ var iLifetimeScope = sp.GetRequiredService();
+ var logger = sp.GetRequiredService>();
+ var eventBusSubcriptionsManager = sp.GetRequiredService();
+
+ var retryCount = 5;
+ if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
+ {
+ retryCount = int.Parse(Configuration["EventBusRetryCount"]);
+ }
+
+ return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
+ });
+ }
+
+ services.AddSingleton();
+ }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/Base/BackgroundTask.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/Base/BackgroundTask.cs
new file mode 100644
index 000000000..6611fc3ab
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/Base/BackgroundTask.cs
@@ -0,0 +1,81 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ordering.BackgroundTasks.Tasks.Base
+{
+ // Copyright(c) .NET Foundation.All rights reserved.
+ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+ ///
+ /// Base class for implementing a long running .
+ /// IMPORTANT: This base class is implemented in .NET Core 2.1 - Since this microservice is still in .NET Core 2.0, we're using the class within the project
+ /// When .NET Core 2.1 is released, this class should be removed and you should use the use implemented by the framework
+ /// https://github.com/aspnet/Hosting/blob/712c992ca827576c05923e6a134ca0bec87af4df/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs
+ ///
+ ///
+ public abstract class BackgroundService : IHostedService, IDisposable
+ {
+ private Task _executingTask;
+
+ private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
+
+ ///
+ /// This method is called when the starts. The implementation should return a task that represents
+ /// the lifetime of the long running operation(s) being performed.
+ ///
+ /// Triggered when is called.
+ /// A that represents the long running operations.
+ protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
+
+ ///
+ /// Triggered when the application host is ready to start the service.
+ ///
+ /// Indicates that the start process has been aborted.
+ public virtual Task StartAsync(CancellationToken cancellationToken)
+ {
+ // Store the task we're executing
+ _executingTask = ExecuteAsync(_stoppingCts.Token);
+
+ // If the task is completed then return it, this will bubble cancellation and failure to the caller
+ if (_executingTask.IsCompleted)
+ {
+ return _executingTask;
+ }
+
+ // Otherwise it's running
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Triggered when the application host is performing a graceful shutdown.
+ ///
+ /// Indicates that the shutdown process should no longer be graceful.
+ public virtual async Task StopAsync(CancellationToken cancellationToken)
+ {
+ // Stop called without start
+ if (_executingTask == null)
+ {
+ return;
+ }
+
+ try
+ {
+ // Signal cancellation to the executing method
+ _stoppingCts.Cancel();
+ }
+ finally
+ {
+ // Wait until the task completes or the stop token triggers
+ await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
+ }
+
+ }
+
+ public virtual void Dispose()
+ {
+ _stoppingCts.Cancel();
+ }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs
new file mode 100644
index 000000000..c89b8cbf3
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs
@@ -0,0 +1,92 @@
+using Dapper;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Ordering.BackgroundTasks.Configuration;
+using Ordering.BackgroundTasks.IntegrationEvents;
+using Ordering.BackgroundTasks.Tasks.Base;
+using System;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ordering.BackgroundTasks.Tasks
+{
+ public class GracePeriodManagerService
+ : BackgroundService
+ {
+ private readonly ILogger _logger;
+ private readonly BackgroundTaskSettings _settings;
+ private readonly IEventBus _eventBus;
+
+ public GracePeriodManagerService(IOptions settings,
+ IEventBus eventBus,
+ ILogger logger)
+ {
+ _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings));
+ _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogDebug($"GracePeriodManagerService is starting.");
+
+ stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriodManagerService background task is stopping."));
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ _logger.LogDebug($"GracePeriodManagerService background task is doing background work.");
+
+ CheckConfirmedGracePeriodOrders();
+
+ await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
+ }
+
+ _logger.LogDebug($"GracePeriodManagerService background task is stopping.");
+
+ await Task.CompletedTask;
+ }
+
+ private void CheckConfirmedGracePeriodOrders()
+ {
+ _logger.LogDebug($"Checking confirmed grace period orders");
+
+ var orderIds = GetConfirmedGracePeriodOrders();
+
+ foreach (var orderId in orderIds)
+ {
+ var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId);
+
+ _eventBus.Publish(confirmGracePeriodEvent);
+ }
+ }
+
+ private IEnumerable GetConfirmedGracePeriodOrders()
+ {
+ IEnumerable orderIds = new List();
+
+ using (var conn = new SqlConnection(_settings.ConnectionString))
+ {
+ try
+ {
+ conn.Open();
+ orderIds = conn.Query(
+ @"SELECT Id FROM [ordering].[orders]
+ WHERE DATEDIFF(minute, [OrderDate], GETDATE()) >= @GracePeriodTime
+ AND [OrderStatusId] = 1",
+ new { GracePeriodTime = _settings.GracePeriodTime });
+ }
+ catch (SqlException exception)
+ {
+ _logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}");
+ }
+
+ }
+
+ return orderIds;
+ }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json
new file mode 100644
index 000000000..fa8ce71a9
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ }
+}
diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json
new file mode 100644
index 000000000..546374598
--- /dev/null
+++ b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json
@@ -0,0 +1,27 @@
+{
+ "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;",
+ "Logging": {
+ "IncludeScopes": false,
+ "Debug": {
+ "LogLevel": {
+ "Default": "Debug"
+ }
+ },
+ "Console": {
+ "LogLevel": {
+ "Default": "Debug"
+ }
+ }
+ },
+ "SubscriptionClientName": "Ordering",
+ "GracePeriodTime": "1",
+ "CheckUpdateTime": "1000",
+ "ApplicationInsights": {
+ "InstrumentationKey": ""
+ },
+ "AzureServiceBusEnabled": false,
+ "EventBusRetryCount": 5,
+ "EventBusConnection": "",
+ "EventBusUserName": "",
+ "EventBusPassword": ""
+}
\ No newline at end of file
diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
index d2e742b33..428d97580 100644
--- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
+++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
@@ -95,8 +95,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
{
if (_orderStatusId == OrderStatus.Submitted.Id)
{
- AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id;
+
+ AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
}
}
@@ -104,10 +105,10 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
{
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{
- AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
-
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock.";
+
+ AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
}
}
@@ -115,10 +116,10 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
{
if (_orderStatusId == OrderStatus.StockConfirmed.Id)
{
- AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
-
_orderStatusId = OrderStatus.Paid.Id;
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
+
+ AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
}
}