using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
public enum BackgroundJobTypes
public enum BackgroundJobStates
public class BackgroundJob
public long Id {get;set;}
public BackgroundJobStates State {get;set;}
public BackgroundJobTypes Type {get;set;}
public void MarkForRetry(Exception ex)
public class DBContext : DbContext
public DbSet<BackgroundJob> BackgroundJobs { get; set; }
public class BackgroundProcessWorker : BackgroundService
private readonly IServiceScopeFactory _serviceScopeFactory;
public BackgroundProcessWorker(IServiceScopeFactory scopeFactory)
_serviceScopeFactory = scopeFactory;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
using var scope = _serviceScopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<DBContext>();
while (!stoppingToken.IsCancellationRequested)
var pendingJobs = dbContext.BackgroundJobs
.Where(e => e.State == BackgroundJobStates.Pending)
.Select(e => new { e.Id, e.Type });
await Parallel.ForEachAsync(pendingJobs, async (pendingJob, stoppingToken) =>
if (pendingJob.Type != BackgroundJobTypes.Computing)
var backgroundJob = await dbContext.BackgroundJobs
.Where(e => e.Id == pendingJob.Id)
.SingleAsync(stoppingToken);
await dbContext.SaveChangesAsync();
backgroundJob.MarkForRetry(e);
throw new ArgumentException("Processing failed", e);