using AsbCloudApp.Data;
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background;
///
/// Класс разовой работы.
/// Разовая работа приоритетнее периодической.
///
public abstract class Work : BackgroundWorkDto
{
private CancellationTokenSource? stoppingCts;
private sealed class WorkBase : Work
{
private Func, CancellationToken, Task> ActionAsync { get; }
public WorkBase(string id, Func, CancellationToken, Task> actionAsync)
: base(id)
{
ActionAsync = actionAsync;
}
protected override Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token)
=> ActionAsync(id, services, onProgressCallback, token);
}
///
/// Делегат обработки ошибки.
/// Не должен выполняться долго.
///
public Func? OnErrorAsync { get; set; }
///
/// макс продолжительность обработки исключения
///
public TimeSpan OnErrorHandlerTimeout { get; set; } = TimeSpan.FromSeconds(5);
///
/// Базовая работа
///
///
public Work(string id)
{
Id = id;
}
///
/// Создать работу на основе делегата
///
///
///
///
[Obsolete("Use implement Work class")]
public static Work CreateByDelegate(string id, Func, CancellationToken, Task> actionAsync)
{
return new WorkBase(id, actionAsync);
}
///
/// Запустить работу
///
///
///
/// True - success, False = fail
public async Task Start(IServiceProvider services, CancellationToken token)
{
SetStatusStart();
try
{
stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(token);
var task = Action(Id, services, UpdateStatus, stoppingCts.Token);
await task.WaitAsync(Timeout, stoppingCts.Token);
SetStatusComplete();
return true;
}
catch (Exception exception)
{
var message = FormatExceptionMessage(exception);
SetLastError(message);
if (OnErrorAsync is not null)
{
try
{
var task = Task.Run(
async () => await OnErrorAsync(Id, exception, token),
token);
await task.WaitAsync(OnErrorHandlerTimeout, token);
}
catch (Exception onErrorAsyncException)
{
var message2 = FormatExceptionMessage(onErrorAsyncException);
Trace.TraceError($"Backgroud work:\"{Id}\" OnError handler throws exception: {message2}");
}
}
}
return false;
}
public void Stop()
{
stoppingCts?.Cancel();
}
private static string FormatExceptionMessage(Exception exception)
{
var firstException = FirstException(exception);
var message = new StringBuilder();
if (firstException != exception)
{
message.Append("top exception:");
message.AppendLine(exception.Message);
message.Append("inner exception:");
message.AppendLine(firstException.Message);
}
else
message.AppendLine(firstException.Message);
message.AppendLine(exception.StackTrace);
return message.ToString();
}
private static Exception FirstException(Exception exception)
{
if (exception.InnerException is not null)
return FirstException(exception.InnerException);
return exception;
}
///
/// делегат фоновой работы
///
/// Идентификатор работы
/// Поставщик сервисов
/// on progress callback. String - new state text. double? - optional progress 0-100%
///
///
protected abstract Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token);
}