Skip to content

Commit

Permalink
Added background service
Browse files Browse the repository at this point in the history
  • Loading branch information
Farshad DASHTI authored and Farshad DASHTI committed Oct 2, 2024
1 parent 3d1d0a5 commit e18bd4b
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/build-test-backgroundservice.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: .NET Build and Test for DfE.CoreLibs.BackgroundService

on:
push:
branches:
- main
paths:
- 'src/DfE.CoreLibs.BackgroundService/**'

jobs:
build-and-test:
uses: ./.github/workflows/build-test-template.yml
with:
project_name: DfE.CoreLibs.BackgroundService
project_path: src/DfE.CoreLibs.BackgroundService
sonar_project_key: DFE-Digital_corelibs-backgroundService
secrets:
GITHUB__TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR__TOKEN: ${{ secrets.SONAR_TOKEN }}
15 changes: 15 additions & 0 deletions .github/workflows/pack-backgroundservice.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Build and Push NuGet Package for DfE.CoreLibs.BackgroundService

on:
workflow_run:
workflows: [".NET Build and Test for DfE.CoreLibs.BackgroundService"]
types:
- completed

jobs:
build-and-package:
uses: ./.github/workflows/nuget-package-template.yml
with:
project_name: DfE.CoreLibs.BackgroundService
project_path: src/DfE.CoreLibs.BackgroundService
nuget_package_name: DfE.CoreLibs.BackgroundService
6 changes: 6 additions & 0 deletions DfE.CoreLibs.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DfE.CoreLibs.Caching", "src\DfE.CoreLibs.Caching\DfE.CoreLibs.Caching.csproj", "{D88D58F0-18C4-4C3B-805B-8A483500E73E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DfE.CoreLibs.BackgroundService", "src\DfE.CoreLibs.BackgroundService\DfE.CoreLibs.BackgroundService.csproj", "{C7194A27-7254-49A3-8D0E-9B67708D3D43}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +17,10 @@ Global
{D88D58F0-18C4-4C3B-805B-8A483500E73E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D88D58F0-18C4-4C3B-805B-8A483500E73E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D88D58F0-18C4-4C3B-805B-8A483500E73E}.Release|Any CPU.Build.0 = Release|Any CPU
{C7194A27-7254-49A3-8D0E-9B67708D3D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7194A27-7254-49A3-8D0E-9B67708D3D43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7194A27-7254-49A3-8D0E-9B67708D3D43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7194A27-7254-49A3-8D0E-9B67708D3D43}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="MediatR.Contracts" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MediatR;

namespace DfE.CoreLibs.BackgroundService.Interfaces
{
public interface IBackgroundServiceEvent : INotification
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MediatR;

namespace DfE.CoreLibs.BackgroundService.Interfaces
{
public interface IBackgroundServiceEventHandler<in TEvent> : INotificationHandler<TEvent> where TEvent : IBackgroundServiceEvent
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace DfE.CoreLibs.BackgroundService.Interfaces
{
public interface IBackgroundServiceFactory
{
void EnqueueTask<TResult, TEvent>(Func<Task<TResult>> taskFunc, Func<TResult, TEvent>? eventFactory = null)
where TEvent : IBackgroundServiceEvent;
}
}
16 changes: 16 additions & 0 deletions src/DfE.CoreLibs.BackgroundService/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DfE.CoreLibs.BackgroundService.Interfaces;
using DfE.CoreLibs.BackgroundService.Services;

namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddBackgroundService(this IServiceCollection services)
{
services.AddSingleton<IBackgroundServiceFactory, BackgroundServiceFactory>();
services.AddHostedService<BackgroundServiceFactory>();

return services;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Collections.Concurrent;
using DfE.CoreLibs.BackgroundService.Interfaces;
using MediatR;

namespace DfE.CoreLibs.BackgroundService.Services
{
public class BackgroundServiceFactory(IMediator mediator) : Microsoft.Extensions.Hosting.BackgroundService, IBackgroundServiceFactory
{
private readonly ConcurrentDictionary<Type, ConcurrentQueue<Func<Task>>> _taskQueues = new();
private readonly ConcurrentDictionary<Type, SemaphoreSlim> _semaphores = new();

public void EnqueueTask<TResult, TEvent>(Func<Task<TResult>> taskFunc, Func<TResult, TEvent>? eventFactory = null)
where TEvent : IBackgroundServiceEvent
{
var taskType = taskFunc.GetType();
var queue = _taskQueues.GetOrAdd(taskType, new ConcurrentQueue<Func<Task>>());
_semaphores.GetOrAdd(taskType, new SemaphoreSlim(1, 1));

queue.Enqueue(async () =>
{
var result = await taskFunc();

if (eventFactory != null)
{
var taskCompletedEvent = eventFactory.Invoke(result);
await mediator.Publish(taskCompletedEvent);
}
});

_ = StartProcessingQueue(taskType);
}

private async Task StartProcessingQueue(Type taskType)
{
var queue = _taskQueues[taskType];
var semaphore = _semaphores[taskType];

await semaphore.WaitAsync();

try
{
while (queue.TryDequeue(out var taskToProcess))
{
await taskToProcess();
}
}
finally
{
semaphore.Release();
}
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(1000, stoppingToken);
}
}
}
}

0 comments on commit e18bd4b

Please sign in to comment.