2024-07-04 11:02:45 +05:00
|
|
|
|
using AsbCloudApp.Data;
|
2023-10-08 21:20:28 +05:00
|
|
|
|
using System;
|
2023-11-02 16:20:48 +05:00
|
|
|
|
using System.Diagnostics;
|
2023-10-12 12:45:19 +05:00
|
|
|
|
using System.Text;
|
2022-12-02 14:45:20 +05:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
namespace AsbCloudInfrastructure.Background;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Класс разовой работы.
|
|
|
|
|
/// Разовая работа приоритетнее периодической.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public abstract class Work : BackgroundWorkDto
|
2022-12-02 14:45:20 +05:00
|
|
|
|
{
|
2023-11-03 17:02:44 +05:00
|
|
|
|
private CancellationTokenSource? stoppingCts;
|
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
private sealed class WorkBase : Work
|
2022-12-02 14:45:20 +05:00
|
|
|
|
{
|
2023-10-08 21:20:28 +05:00
|
|
|
|
private Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> ActionAsync { get; }
|
2023-10-09 13:12:45 +05:00
|
|
|
|
public WorkBase(string id, Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> actionAsync)
|
|
|
|
|
: base(id)
|
|
|
|
|
{
|
|
|
|
|
ActionAsync = actionAsync;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
|
|
|
|
|
=> ActionAsync(id, services, onProgressCallback, token);
|
|
|
|
|
}
|
2023-10-08 19:45:21 +05:00
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Делегат обработки ошибки.
|
|
|
|
|
/// Не должен выполняться долго.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Func<string, Exception, CancellationToken, Task>? OnErrorAsync { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// макс продолжительность обработки исключения
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TimeSpan OnErrorHandlerTimeout { get; set; } = TimeSpan.FromSeconds(5);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Базовая работа
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id"></param>
|
|
|
|
|
public Work(string id)
|
|
|
|
|
{
|
|
|
|
|
Id = id;
|
|
|
|
|
}
|
2023-10-08 19:45:21 +05:00
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Создать работу на основе делегата
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id"></param>
|
|
|
|
|
/// <param name="actionAsync"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[Obsolete("Use implement Work class")]
|
|
|
|
|
public static Work CreateByDelegate(string id, Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> actionAsync)
|
|
|
|
|
{
|
|
|
|
|
return new WorkBase(id, actionAsync);
|
|
|
|
|
}
|
2022-12-02 14:45:20 +05:00
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Запустить работу
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="services"></param>
|
|
|
|
|
/// <param name="token"></param>
|
|
|
|
|
/// <returns>True - success, False = fail</returns>
|
|
|
|
|
public async Task<bool> Start(IServiceProvider services, CancellationToken token)
|
|
|
|
|
{
|
|
|
|
|
SetStatusStart();
|
|
|
|
|
try
|
2022-12-02 14:45:20 +05:00
|
|
|
|
{
|
2023-11-03 17:02:44 +05:00
|
|
|
|
stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(token);
|
|
|
|
|
var task = Action(Id, services, UpdateStatus, stoppingCts.Token);
|
|
|
|
|
await task.WaitAsync(Timeout, stoppingCts.Token);
|
2023-10-09 13:12:45 +05:00
|
|
|
|
SetStatusComplete();
|
|
|
|
|
return true;
|
2022-12-02 14:45:20 +05:00
|
|
|
|
}
|
2023-10-09 13:12:45 +05:00
|
|
|
|
catch (Exception exception)
|
2023-10-08 13:09:09 +05:00
|
|
|
|
{
|
2023-10-12 12:45:19 +05:00
|
|
|
|
var message = FormatExceptionMessage(exception);
|
|
|
|
|
SetLastError(message);
|
2023-10-09 13:12:45 +05:00
|
|
|
|
if (OnErrorAsync is not null)
|
2023-10-08 13:09:09 +05:00
|
|
|
|
{
|
2023-11-02 16:20:48 +05:00
|
|
|
|
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}");
|
|
|
|
|
}
|
2023-10-08 13:09:09 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-09 13:12:45 +05:00
|
|
|
|
return false;
|
2023-10-08 13:09:09 +05:00
|
|
|
|
}
|
2023-10-09 13:12:45 +05:00
|
|
|
|
|
2023-11-03 17:02:44 +05:00
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
stoppingCts?.Cancel();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-12 12:45:19 +05:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 13:12:45 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// делегат фоновой работы
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">Идентификатор работы</param>
|
|
|
|
|
/// <param name="services">Поставщик сервисов</param>
|
|
|
|
|
/// <param name="onProgressCallback">on progress callback. String - new state text. double? - optional progress 0-100%</param>
|
|
|
|
|
/// <param name="token"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected abstract Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token);
|
2022-12-02 14:45:20 +05:00
|
|
|
|
}
|