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

143 lines
4.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AsbCloudApp.Data;
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
{
private CancellationTokenSource? stoppingCts;
private sealed class WorkBase : Work
{
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);
}
/// <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;
}
/// <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
{
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;
}
/// <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);
}