using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Background { /// <summary> /// Сервис для фонового выполнения работы /// </summary> public class BackgroundWorker : BackgroundService { private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10); private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2); private static readonly TimeSpan exceptionHandleTimeout = TimeSpan.FromSeconds(2); private readonly IServiceProvider serviceProvider; private readonly WorkQueue workQueue = new WorkQueue(); public string? CurrentWorkId; public BackgroundWorker(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } /// <summary> /// Добавление задачи в очередь. /// Не периодические задачи будут выполняться вперед. /// </summary> /// <param name="work"></param> /// <exception cref="ArgumentException">Id mast be unique</exception> public void Push(WorkBase work) { workQueue.Push(work); } /// <summary> /// Проверяет наличие работы с указанным Id /// </summary> /// <param name="id"></param> /// <returns></returns> public bool Contains(string id) { return workQueue.Contains(id); } /// <summary> /// Удаление работы по ID /// </summary> /// <param name="id"></param> /// <returns></returns> public bool Delete(string id) { return workQueue.Delete(id); } protected override async Task ExecuteAsync(CancellationToken token) { while (!token.IsCancellationRequested) { var dateStart = DateTime.Now; var work = workQueue.Pop(); if (work is null) { await Task.Delay(executePeriod, token); continue; } CurrentWorkId = work.Id; using var scope = serviceProvider.CreateScope(); try { Trace.TraceInformation($"Backgroud work:\"{work.Id}\" start."); var task = work.ActionAsync(work.Id, scope.ServiceProvider, token); await task.WaitAsync(work.Timeout, token); work.ExecutionTime = DateTime.Now - dateStart; Trace.TraceInformation($"Backgroud work:\"{work.Id}\" done. ExecutionTime: {work.ExecutionTime:hh\\:mm\\:ss\\.fff}"); } catch (Exception exception) { Trace.TraceError($"Backgroud work:\"{work.Id}\" throw exception: {exception.Message}"); if (work.OnErrorAsync is not null) { using var task = Task.Run( async () => await work.OnErrorAsync(work.Id, exception, token), token); await task.WaitAsync(exceptionHandleTimeout, token); } } CurrentWorkId = null; await Task.Delay(minDelay, token); } } } }