@ -1,17 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Ordering.BackgroundTasksHost.Configuration | |||||
{ | |||||
public class BackgroundTaskSettings | |||||
{ | |||||
public string ConnectionString { get; set; } | |||||
public string EventBusConnection { get; set; } | |||||
public int GracePeriodTime { get; set; } | |||||
public int CheckUpdateTime { get; set; } | |||||
} | |||||
} |
@ -1,12 +0,0 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
namespace Ordering.BackgroundTasksHost.IntegrationEvents | |||||
{ | |||||
public class GracePeriodConfirmedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public GracePeriodConfirmedIntegrationEvent(int orderId) => | |||||
OrderId = orderId; | |||||
} | |||||
} |
@ -1,32 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<None Remove="appsettings.json" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Content Include="appsettings.json"> | |||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||||
</Content> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||||
<PackageReference Include="Dapper" Version="1.50.4" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Folder Include="Tasks\" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -1,177 +0,0 @@ | |||||
using Autofac.Extensions.DependencyInjection; | |||||
using Autofac; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using Ordering.BackgroundTasksHost.Configuration; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | |||||
using Microsoft.Azure.ServiceBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | |||||
using RabbitMQ.Client; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | |||||
using Ordering.BackgroundTasksHost.Tasks; | |||||
namespace Ordering.BackgroundTasksHost | |||||
{ | |||||
class Program | |||||
{ | |||||
static void Main(string[] args) | |||||
{ | |||||
using (var host = CreateHost(args)) | |||||
{ | |||||
host.Start(); | |||||
host.WaitForShutdown(); | |||||
} | |||||
} | |||||
static IHost CreateHost(string[] args) | |||||
{ | |||||
var host = new HostBuilder() | |||||
.ConfigureAppConfiguration((hostContext, configApp) => | |||||
{ | |||||
configApp.AddEnvironmentVariables(); | |||||
configApp.AddJsonFile("appsettings.json", optional: true); | |||||
configApp.AddJsonFile( | |||||
$"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", | |||||
optional: true); | |||||
configApp.AddCommandLine(args); | |||||
}) | |||||
.ConfigureServices(services => | |||||
{ | |||||
var configuration = services.BuildServiceProvider() | |||||
.GetRequiredService<IConfiguration>(); | |||||
services.AddOptions() | |||||
.Configure<BackgroundTaskSettings>(configuration) | |||||
.RegisterBus(configuration) | |||||
.RegisterHostedServices(); | |||||
}) | |||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) | |||||
.Build(); | |||||
return host; | |||||
} | |||||
} | |||||
class AutofacServiceProviderFactory | |||||
: IServiceProviderFactory<ContainerBuilder> | |||||
{ | |||||
public ContainerBuilder CreateBuilder(IServiceCollection services) | |||||
{ | |||||
var containerBuilder = new ContainerBuilder(); | |||||
containerBuilder.Populate(services); | |||||
return containerBuilder; | |||||
} | |||||
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) | |||||
{ | |||||
return new AutofacServiceProvider(containerBuilder.Build()); | |||||
} | |||||
} | |||||
static class ServiceCollectionExtensions | |||||
{ | |||||
public static IServiceCollection RegisterBus(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IServiceBusPersisterConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); | |||||
var serviceBusConnectionString = configuration["EventBusConnection"]; | |||||
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | |||||
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IRabbitMQPersistentConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | |||||
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); | |||||
}); | |||||
} | |||||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | |||||
{ | |||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||||
{ | |||||
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||||
} | |||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | |||||
}); | |||||
} | |||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection RegisterHostedServices(this IServiceCollection services) | |||||
{ | |||||
services.AddSingleton<IHostedService, GracePeriodManagerService>(); | |||||
return services; | |||||
} | |||||
} | |||||
} |
@ -1,81 +0,0 @@ | |||||
using Microsoft.Extensions.Hosting; | |||||
using System; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.BackgroundTasksHost.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. | |||||
/// <summary> | |||||
/// Base class for implementing a long running <see cref="IHostedService"/>. | |||||
/// 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 | |||||
/// | |||||
/// </summary> | |||||
public abstract class BackgroundService : IHostedService, IDisposable | |||||
{ | |||||
private Task _executingTask; | |||||
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource(); | |||||
/// <summary> | |||||
/// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task that represents | |||||
/// the lifetime of the long running operation(s) being performed. | |||||
/// </summary> | |||||
/// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param> | |||||
/// <returns>A <see cref="Task"/> that represents the long running operations.</returns> | |||||
protected abstract Task ExecuteAsync(CancellationToken stoppingToken); | |||||
/// <summary> | |||||
/// Triggered when the application host is ready to start the service. | |||||
/// </summary> | |||||
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param> | |||||
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; | |||||
} | |||||
/// <summary> | |||||
/// Triggered when the application host is performing a graceful shutdown. | |||||
/// </summary> | |||||
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param> | |||||
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(); | |||||
} | |||||
} | |||||
} |
@ -1,92 +0,0 @@ | |||||
using Dapper; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using Ordering.BackgroundTasksHost.Configuration; | |||||
using Ordering.BackgroundTasksHost.IntegrationEvents; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Data.SqlClient; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.BackgroundTasksHost.Tasks | |||||
{ | |||||
public class GracePeriodManagerService | |||||
: BackgroundService | |||||
{ | |||||
private readonly ILogger<GracePeriodManagerService> _logger; | |||||
private readonly BackgroundTaskSettings _settings; | |||||
private readonly IEventBus _eventBus; | |||||
public GracePeriodManagerService(IOptions<BackgroundTaskSettings> settings, | |||||
IEventBus eventBus, | |||||
ILogger<GracePeriodManagerService> 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<int> GetConfirmedGracePeriodOrders() | |||||
{ | |||||
IEnumerable<int> orderIds = new List<int>(); | |||||
using (var conn = new SqlConnection(_settings.ConnectionString)) | |||||
{ | |||||
try | |||||
{ | |||||
conn.Open(); | |||||
orderIds = conn.Query<int>( | |||||
@"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; | |||||
} | |||||
} | |||||
} |
@ -1,27 +0,0 @@ | |||||
{ | |||||
"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": "BackgroundTasks", | |||||
"GracePeriodTime": "1", | |||||
"CheckUpdateTime": "1000", | |||||
"ApplicationInsights": { | |||||
"InstrumentationKey": "" | |||||
}, | |||||
"AzureServiceBusEnabled": false, | |||||
"EventBusRetryCount": 5, | |||||
"EventBusConnection": "", | |||||
"EventBusUserName": "", | |||||
"EventBusPassword": "" | |||||
} |