Повторите механизм с наблюдаемым - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь написать механизм повтора, используя наблюдаемые C #.

  1. Retry имеет количество повторных попыток и интервал повторных попыток
  2. Повтор должен выполнить метод "OnExecute".
  3. Для каждого исключения будет выполняться метод "OnCatch".

Вот что я пытался сделать:

public static IObservable<T> Retry(GenericRetryExecutorRequest<T> request)
{
    var source = Observable.Timer(TimeSpan.Zero, request.Interval)
        .Select(item =>
        {
          return request.GenericRetryActions.OnExecute();
        });

    var retryObservable = source
        .Retry(request.RetryCount)
        .Catch(source);

    return retryObservable;
}

public class GenericRetryExecutorRequest<T>
{
    public int RetryCount { get; set; } = 3; 
    public TimeSpan Interval { get; set; } = new TimeSpan(0,0,0,5);
    public IGenericRetryActions<T> GenericRetryActions { get; set; }
}

public interface IGenericRetryActions<out T>
{
    T OnExecute();
    void OnCatch();
}

К сожалению - это не очень хорошо работает:

  1. Я не знаю, как выполнить OnCatch при возникновении исключения. Я пробовал много способов безуспешно.
  2. OnExecute, кажется, не выполняет повторно (с запросом интервал) в случае, если он выдает исключение.

1 Ответ

0 голосов
/ 02 мая 2018

Попробуйте это:

public static IObservable<T> Retry<T>(this GenericRetryExecutorRequest<T> request)
{
    return Observable.Timer(Timespan.Zero, request.Interval)
        .Select(item =>
        {
            try
            {
                var value = request.GenericRetryActions.OnExecute();
                return Notification.CreateOnNext(value);
            }
            catch(Exception e)
            {
                request.GenericRetryActions.OnCatch();
                return Notification.CreateOnError<T>(e);
            }
        })
        .Dematerialize()
        .Retry(request.RetryCount);
}

Как правило, использование try-catch внутри наблюдаемого недовольно; предпочтительно использовать наблюдаемую обработку исключений On-Error. Тем не менее, OnExecute вашего интерфейса не возвращает IObservable<T>, а просто T. Так что вы вынуждены использовать try-catch. Если бы вы изменили интерфейс, чтобы вернуть IObservable<T>, то я думаю, что это будет работать:

public class GenericRetryExecutorRequest2<T>
{
    public int RetryCount { get; set; } = 3;
    public TimeSpan Interval { get; set; } = new TimeSpan(0, 0, 0, 5);
    public IGenericRetryActions2<T> GenericRetryActions { get; set; }
}

public interface IGenericRetryActions2<out T>
{
    IObservable<T> OnExecute();
    void OnCatch();
}

public static IObservable<T> Retry2<T>(this GenericRetryExecutorRequest2<T> request)
{
    return Observable.Timer(Timespan.Zero, request.Interval)
        .SelectMany(_ => request.GenericRetryActions.OnExecute())
        .Catch((Exception e) => Observable.Return(Unit.Default)
            .Do(_ => request.GenericRetryActions.OnCatch())
            .SelectMany(Observable.Throw<T>(e))
        )
        .Retry(request.RetryCount);
}

Это все, если вы хотите, чтобы механизм продолжал срабатывать на успех. Если нет, добавьте Take(1) в конце любого решения.

...