DD.WellWorkover.Cloud/AsbCloudInfrastructure/Background/Work.cs

143 lines
4.9 KiB
C#
Raw Normal View History

using AsbCloudApp.Data;
2023-10-08 21:20:28 +05:00
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background;
/// <summary>
/// Класс разовой работы.
/// Разовая работа приоритетнее периодической.
/// </summary>
public abstract class Work : BackgroundWorkDto
{
2023-11-03 17:02:44 +05:00
private CancellationTokenSource? stoppingCts;
private sealed class WorkBase : Work
{
2023-10-08 21:20:28 +05:00
private Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> ActionAsync { get; }
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
/// <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
/// <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);
}
/// <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
{
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);
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;
}
2023-11-03 17:02:44 +05:00
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;
}
/// <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);
}