Compare commits

...
1 Commits
Author SHA1 Message Date
saherelm 4697937709 add Support for Background Services ... 2026-08-19 12:33:57 +03:30
4 changed files with 359 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using xCommons.Providers;
namespace xCommons.Extensions
{
public static class BackgroundServiceExtensions
{
/// <summary>
/// Register Background Services ...
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="source"></param>
/// <param name="service"></param>
public static void AddXBackgroundService<TService>(
this IServiceCollection source,
TService service
)
where TService : XBackgroundService
{
//
source.AddSingleton(service);
source.AddHostedService<TService>();
}
}
}
+26
View File
@@ -0,0 +1,26 @@
using System;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace xCommons.Providers
{
/// <summary>
/// Default Background Service ...
/// </summary>
public abstract class XBackgroundService : BackgroundService
{
//
protected readonly IServiceProvider serviceProvider;
protected readonly ILogger<XBackgroundService> logger;
protected XBackgroundService(
IServiceProvider serviceProvider,
ILogger<XBackgroundService> logger
)
{
//
this.logger = logger;
this.serviceProvider = serviceProvider;
}
}
}
+93
View File
@@ -0,0 +1,93 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace xCommons.Providers
{
/// <summary>
/// Resumable Background Task ...
/// </summary>
public abstract class XResumableBackgroundService : XBackgroundService
{
/// <summary>
/// Check is Paused ...
/// </summary>
protected bool paused = false;
protected XResumableBackgroundService(
IServiceProvider serviceProvider,
ILogger<XResumableBackgroundService> logger
) : base(
logger: logger,
serviceProvider: serviceProvider
)
{ }
protected override async Task ExecuteAsync(
CancellationToken stoppingToken = default
)
{
//
if (paused)
{
return;
}
//
await DoWorkAsync(
payload: null,
cancellationToken: stoppingToken
);
}
/// <summary>
/// do all required works here ...
/// </summary>
/// <param name="payload"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public abstract Task DoWorkAsync(
object payload = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Pause Consuming ...
/// </summary>
/// <returns></returns>
public bool Pause()
{
//
var result = !paused;
if (!result)
{
return result;
}
//
paused = true;
result = paused;
return result;
}
/// <summary>
/// Resume Consuming ...
/// </summary>
/// <returns></returns>
public bool Resume()
{
//
var result = paused;
if (!result)
{
return result;
}
//
paused = false;
result = !paused;
return result;
}
}
}
+215
View File
@@ -0,0 +1,215 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using xCommons.Extensions;
using xExceptions.Constants;
namespace xCommons.Providers
{
/// <summary>
/// Do Background Jobs based on Time Intervals ...
/// </summary>
public abstract class XTimeBasedBackgroundService : XBackgroundService
{
//
private bool isWorking = false;
private readonly TimeSpan restartInterval;
private readonly SemaphoreSlim mlock = new(1, 1);
public DateTime? LastRunTime { get; private set; }
private CancellationTokenSource cancellationTokenSource;
protected XTimeBasedBackgroundService(
IServiceProvider serviceProvider,
ILogger<XTimeBasedBackgroundService> logger,
int intervalInMinutes = 10
) : base(
logger: logger,
serviceProvider: serviceProvider
)
{
//
// Validate Interval ...
if (intervalInMinutes <= 0)
{
XException.InvalidArgs.Throw();
}
//
restartInterval = TimeSpan.FromMinutes(intervalInMinutes);
}
/// <summary>
/// Override base action to Implement timing ...
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//
while (!stoppingToken.IsCancellationRequested)
{
try
{
if (!isWorking)
{
//
// Enable Working ...
await mlock.WaitAsync(stoppingToken);
try
{
isWorking = true;
}
finally
{
mlock.Release();
}
//
// Start a new work task ...
cancellationTokenSource = CancellationTokenSource
.CreateLinkedTokenSource(stoppingToken);
//
// Execute the work ...
await ExecuteWorkAsync(cancellationTokenSource.Token);
//
// Disable Working ...
await mlock.WaitAsync(stoppingToken);
try
{
isWorking = false;
}
finally
{
mlock.Release();
}
//
// Wait for the restart interval ...
await Task.Delay(restartInterval, stoppingToken);
}
else
{
//
// Prevent tight error loops ...
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
catch (OperationCanceledException)
{
//
// Disable Working ...
await mlock.WaitAsync(stoppingToken);
try
{
isWorking = false;
}
finally
{
mlock.Release();
}
break;
}
catch (Exception)
{
//
// Disable Working ...
await mlock.WaitAsync(stoppingToken);
try
{
isWorking = false;
}
finally
{
mlock.Release();
}
//
// Prevent tight error loops ...
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
/// <summary>
/// Override Stop Background Worker ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public override async Task StopAsync(CancellationToken cancellationToken)
{
//
// Cancel the current work session
cancellationTokenSource.Cancel();
await base.StopAsync(cancellationToken);
}
/// <summary>
/// Dispose all Objects ...
/// </summary>
public override void Dispose()
{
mlock.Dispose();
cancellationTokenSource.Dispose();
GC.SuppressFinalize(this);
base.Dispose();
}
/// <summary>
/// do all required works here ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public abstract Task DoWorkAsync(
CancellationToken cancellationToken = default
);
/// <summary>
/// Refresh Worker ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task Refresh(
CancellationToken cancellationToken = default
)
{
//
// Cancel current work if running
cancellationTokenSource.Cancel();
//
// Wait a moment for cancellation
await Task.Delay(100, cancellationToken);
//
// Start new work immediately
cancellationTokenSource = CancellationTokenSource
.CreateLinkedTokenSource(cancellationToken);
await ExecuteWorkAsync(cancellationTokenSource.Token);
}
//
#region Private ...
private async Task ExecuteWorkAsync(CancellationToken cancellationToken)
{
//
LastRunTime = DateTime.UtcNow;
try
{
await DoWorkAsync(cancellationToken);
}
catch (OperationCanceledException)
{
}
catch (Exception)
{
}
finally
{
}
}
#endregion
}
}