Compare commits
1
Commits
32dd5dd3c1
...
4697937709
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4697937709 |
@@ -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