RX.Net: используйте повторную попытку, но регистрируйте любое исключение

Я новичок в RX и занимаюсь обработкой ошибок и использованием Retry; У меня есть следующее (да, я знаю, что это не «настоящий» модульный тест, но он дает мне место для возни!!), и мне было интересно, как мне сохранить повторную попытку, но иметь возможность регистрировать любое исключение?

    [Test]
    public void Test()
    {
        var scheduler = new TestScheduler();

        var source = scheduler.CreateHotObservable(
            new Recorded<Notification<long>>(10000000, Notification.CreateOnNext(0L)),
            new Recorded<Notification<long>>(20000000, Notification.CreateOnNext(1L)),
            new Recorded<Notification<long>>(30000000, Notification.CreateOnNext(2L)),
            new Recorded<Notification<long>>(30000001, Notification.CreateOnError<long>(new Exception("Fail"))),
            new Recorded<Notification<long>>(40000000, Notification.CreateOnNext(3L)),
            new Recorded<Notification<long>>(40000000, Notification.CreateOnCompleted<long>())
        );

        source.Retry().Subscribe(
            l => Console.WriteLine($"OnNext {l}"), 
            exception => Console.WriteLine(exception.ToString()), // Would be logging this in production
            () => Console.WriteLine("OnCompleted"));

       scheduler.Start(
            () => source,
            0,
            TimeSpan.FromSeconds(1).Ticks,
            TimeSpan.FromSeconds(5).Ticks);
    }

Что приводит к...

OnNext 0
OnNext 1
OnNext 2
OnNext 3
OnCompleted

... это именно то, что я хочу, кроме того факта, что я хотел бы зарегистрировать исключение, которое возникает между 2 и 3.

Есть ли способ позволить подписчику увидеть исключение в OnError (и зарегистрировать его), а затем повторно подписаться, чтобы он увидел 3?

Спасибо!


person inthegarden    schedule 06.06.2017    source источник


Ответы (1)


Вы можете добиться этого с помощью этого:

source
    .Do(_ => { }, exception => Console.WriteLine(exception.ToString()), () => {})
    .Retry()
    .Subscribe(
        l => Console.WriteLine($"OnNext {l}"),
        //      exception => Console.WriteLine(exception.ToString()), // Would be logging this in production
        () => Console.WriteLine("OnCompleted")
    );

Просто чтобы прояснить, что здесь происходит: OnError — это завершающий сигнал. Если бы ошибка достигла подписки, остальная часть потока была бы прервана. .Retry завершает подписку, проглатывает OnError, а затем повторно подписывается, объединяя две подписки вместе. Например, посмотрите на это:

source
    .StartWith(-1)
    .Retry()
    .Subscribe(
        l => Console.WriteLine($"OnNext {l}"),
        () => Console.WriteLine("OnCompleted")
    );

Ваш вывод будет

OnNext -1
OnNext 0
OnNext 1
OnNext 2
OnNext -1
OnNext 3
OnCompleted

OnNext -1 появляется дважды, потому что оно появляется всякий раз, когда вы подписываетесь (что Retry делает после OnError.

Ваш наблюдаемый тест - откровенно плохой тест. Это нарушает «Контракт Rx», который заключается в том, что уведомления следуют следующему шаблону:

OnNext* (OnCompleted | OnError)? 

То есть 0 или более уведомлений OnNext, за которыми следует необязательное OnError или необязательное OnCompleted. Никакие уведомления любого типа не должны следовать ни за OnError, ни за OnCompleted.

person Shlomo    schedule 06.06.2017
comment
Что касается плохого теста; результат попытки собрать простой пример и провала!! :-( Спасибо за пояснение, я не совсем понял, как работает Retry. - person inthegarden; 06.06.2017
comment
То, что я на самом деле имею в своем приложении, — это горячее наблюдаемое, управляемое FromEventPattern. Я получаю событие, содержащее byte[], затем расшифровываю byte[], устанавливаю тип сообщения и соответствующим образом десериализую; в Observer.OnNext я выполняю действие на основе этого сообщения. Чего я не хочу, так это прекращения потока, если у меня возникнут какие-либо проблемы с этими операциями (расшифровка/сериализация и т. д.). Я просто хочу зарегистрировать ошибку и продолжить обработку. Это возможно? - person inthegarden; 06.06.2017
comment
Чего я не хочу, так это прекращения потока. Я не хочу, чтобы подписка прекращалась. - person inthegarden; 06.06.2017
comment
Да, именно это и делает .Retry. Он завершается и повторно подписывается, но прозрачным образом для нижестоящих подписчиков. - person Shlomo; 06.06.2017
comment
Спасибо. Таким образом, повторная попытка является правильным подходом, но регистрация любого сбоя невозможна. Надеюсь я правильно понимаю!! - person inthegarden; 06.06.2017
comment
Вы можете войти, просто это нужно сделать до повторной попытки. - person Shlomo; 06.06.2017