Обработка исключений с помощью akka.net PipeTo() в задаче

Ссылаясь на документацию Akka.Net, использование PipeTo() предпочтительнее при работе с асинхронными заданиями.

Имея дело с функцией, возвращающей Task<T>, я могу обработать событие отказа без проблем.

Проблема в том, что при работе с функцией, которая не возвращает никакого типа, а только Task, по-прежнему вызывается функция PipeTo, но вместо перегрузки, содержащей дескриптор отказа, она теперь говорит следующее: «Поскольку эта задача имеет никакого результата, получателю будут переданы только исключения.'.

Означает ли это, если у меня есть следующий код:

public class RepositoryComponent : IRepositoryComponent
{
    private SqlSettings _sqlSettings;

    public RepositoryComponent(SqlSettings sqlSettings)
    {
        _sqlSettings = sqlSettings;
    }

    public async Task InsertJobAsync(RetryModel job)
    {
        try
        {
            await... //some logic
        }
        catch { throw; }
    }
}

Мой актер:

public class RepositoryActor : ActorBase
{
    private IRepositoryComponent _repoComponent;
    private ActorSelection _retryActor;

    public RepositoryActor(IRepositoryComponent repoComponent) : base()
    {
        _repoComponent = repoComponent;
    }

    public override void Listening()
    {
        Receive<RepositoryMessages.GenericRequestNoReturn>(x => InvokeRequest(x));
        Receive<RepositoryMessages.GenericRequestWithResponseType>(x => InvokeRequestAndSendResponse(x));
    }

    private void InvokeRequest(RepositoryMessages.GenericRequestNoReturn msg)
    {
        try
        {
            //some logic with msg
            _repoComponent.InsertJobAsync(new Core.Models.RetryModel()).PipeTo(Context.Self);
        }
        catch (Exception ex)
        {
            base.ExceptionHandling(ex);
        }
    }
}

Чтобы перехватывать исключения в указанном выше акторе, мне нужно добавить еще один обработчик Receive для обработки исключений, как, например, ниже:

Receive<Exception>(x => SomeExceptionHandler(x));

Это правильно? Если да, то нет ли необходимости в try {} catch {} вокруг моего блока кода?


person monstertjie_za    schedule 15.02.2018    source источник


Ответы (1)


Если вы вызываете свою асинхронную операцию с помощью PipeTo, она не будет блокировать ваш текущий путь выполнения кода. Это означает, что оператор try/catch никогда не будет выполнен (поскольку успех или неудача задачи, представляющей асинхронную операцию, будут перенаправлены на Self).

Существует 2 способа обработки исключений, которые могут возникнуть во время выполнения асинхронной операции:

  1. Используйте ReceiveAsync<> вместо Receive<> в сочетании с асинхронной лямбдой - это позволит вам просто await выполнить асинхронную операцию. Вы можете использовать try/catch в этом контексте, так как он будет работать так, как ожидалось. Имейте в виду, что это сделает ваш актор нереентерабельным — это означает, что актор не будет обрабатывать никакие сообщения, пока не завершится текущая асинхронная операция.
  2. Оставьте PipeTo и добавьте еще один обработчик Receive<> в свою логику - в случае, если задача, обертывающая асинхронную операцию, завершится неудачно, исключение будет заключено в сообщение и перенаправлено обратно к цели PipeTo (в вашем случае Self). По умолчанию исключения, созданные таким образом, завернуты в сообщение Status.Failure - в этом случае вам нужно добавить обработчик Receive<Status.Failure> в ваш актор. Вы также можете указать собственный конструктор сообщений, используемый для обработки PipeTo сбоев, т.е.: _repoComponent.InsertJobAsync(input).PipeTo(Context.Self, failure: exception => new MyErrorMessage(exception))
person Bartosz Sypytkowski    schedule 16.02.2018
comment
Спасибо за Ваш ответ. У меня сложилось впечатление, что если я использую PipeTo(), где задача не имеет возвращаемого типа, возвращаемое исключение будет обрабатываться в событии получения следующим образом: Receive‹KeyNotFoundException›(...). Спасибо за ваш полезный вклад - person monstertjie_za; 19.02.2018