add Support for Background Services ...
This commit is contained in:
@@ -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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user