using System; using System.Diagnostics; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Background { /// /// Класс разовой работы. /// Разовая работа приоритетнее периодической. /// public class WorkBase { /// /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки. /// public string Id { get; } /// /// Делегат работы. /// /// Параметры: /// /// /// string /// Id Идентификатор работы /// /// /// IServiceProvider /// Поставщик сервисов /// /// /// CancellationToken /// Токен отмены задачи /// /// /// /// internal Func ActionAsync { get; } /// /// Делегат обработки ошибки. /// Не должен выполняться долго. /// public Func? OnErrorAsync { get; set; } /// /// максимально допустимое время выполнения работы /// public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); /// /// Продолжительность последнего выполнения /// public TimeSpan? LastExecutionTime { get; private set; } /// /// Текущее время выполнения /// public TimeSpan? CurrentExecutionTime => CurrentStart.HasValue ? DateTime.Now - CurrentStart.Value : null; /// /// Время последнего запуска /// public DateTime? CurrentStart { get; private set; } /// /// Текстовое описание того, что происходит в задаче. /// public string? CurrentStatus { get; private set; } /// /// Время последнего запуска /// public DateTime? CurrentStatusUpdate { get; private set; } /// /// Последняя ошибка /// public string? LastErrorMessage { get; private set; } /// /// Дата последнего запуска /// public DateTime? LastStart { get; private set; } /// /// Дата последней ошибки /// public DateTime? LastError { get; private set; } /// /// Дата завершения последнего выполнения /// public DateTime? LastComplete { get; private set; } /// /// Кол-во завершений /// public int CountComplete { get; private set; } /// /// Кол-во ошибок /// public int CountErrors { get; private set; } private string WorkNameForTrace => $"Backgroud work:\"{Id}\""; public WorkBase(string id, Func actionAsync) { Id = id; ActionAsync = actionAsync; } public async Task Start(IServiceProvider services, CancellationToken token) { CurrentStart = DateTime.Now; LastStart = DateTime.Now; try { SetStatus(" start"); var task = ActionAsync(Id, services, token); await task.WaitAsync(Timeout, token); LastComplete = DateTime.Now; CountComplete++; SetStatus($" {task.Status}. ExecutionTime: {CurrentExecutionTime:hh\\:mm\\:ss\\.fff}"); } catch (Exception exception) { SetError(exception.Message); if (OnErrorAsync is not null) { var task = Task.Run( async () => await OnErrorAsync(Id, exception, token), token); await task.WaitAsync(Timeout, token); } } LastExecutionTime = CurrentExecutionTime; CurrentStart = null; SetStatus(null); } protected void SetStatus(string? newStatus) { CurrentStatus = newStatus; if (newStatus is not null) { CurrentStatusUpdate = DateTime.Now; Trace.TraceInformation($"{WorkNameForTrace} state: {newStatus}"); } else CurrentStatusUpdate = null; } private void SetError(string? errorMessage) { CountErrors++; LastErrorMessage = errorMessage; LastError = DateTime.Now; Trace.TraceError($"{WorkNameForTrace} throw exception[{CountErrors}]: {errorMessage}"); } } }