215 lines
6.5 KiB
C#
215 lines
6.5 KiB
C#
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
|
|
}
|
|
} |