Обработка исключений в RX. Net при использовании ToEventPattern и Timeout - PullRequest
1 голос
/ 29 января 2020

Я пишу некоторый код с использованием RX в C#, который должен взаимодействовать со старой системой, генерируя события.

В целом, у меня есть наблюдаемое и мне нужно генерировать одно событие, когда наблюдаемое завершается, а другое событие, если обнаружено исключение тайм-аута. Основная проблема в том, как лучше всего обработать исключение.

Я относительно новичок в RX, поэтому, хотя я нашел решение, я не могу быть уверен, что нет лучшего или более подходящего способа который лучше использует расширения RX.

Это не настоящий код, но указывает на структуру моего мышления:

    public delegate void SuccessHandler(object sender, SuccessEventArgs e);
    public event SuccessHandler OnSuccess;

    public delegate void TimeoutHandler(object sender, TimeoutEventArgs e);
    public event TimeoutHandler OnTimeout;

    var id;
    var o = Observable.Return()     // <- this would be a fetch from an asynchronous source
        .Where(r=>r.status=="OK")
        .Timeout(new Timespan(0,0,30)
        .Do(r=> {
             id=r.Id           // <-- Ugh! I know this shouldn't be done!
         }
        .Subscribe(r => {

             var statusResponse= new StatusResponse()
             {
               Id = r.Id
               Name = r.Name
                  Message = "The operation completed successfully",
                  Status = Status.Success
             };

             if (OnSuccess == null) return;
                OnSuccess (this, new SuccessEventArgs(statusResponse);
           },
           e =>
           {
             _logger.LogError(e, "A matching response was not returned in a timely fashion");

             if (OnTimeout == null) return;
             OnTimeout(this, new TimeoutEventArgs(id));
           });

Если мне не нужно было обнаруживать тайм-аут и действовать по нему было бы хорошо; Я уже разработал, как заменить Subscribe для ToEventPattern:

         ...
    .Select(r =>
    {
         var statusResponse= new StatusResponse()
         {
           Id = r.Id
           Name = r.Name
           Message = "The operation completed successfully",
           Status = Status.Success
         };
         return new EventPattern<SuccessEventArgs>(this, new SuccessEventArgs(statusResponse));
    })
    .ToEventPattern();

Однако я хотел бы иметь возможность определить время ожидания (и, возможно, другие исключения). мои эксперименты с Catch не увенчались успехом, потому что я не могу правильно выстроить типы, возможно потому, что я не совсем понимаю, что происходит.

Я очень ценю мнения по этому поводу. , Это приемлемое решение? Как я могу улучшить это? Может ли кто-нибудь указать мне некоторые хорошие онлайн-ссылки, которые объяснят, как можно осуществлять управление потоком данных и обработку исключений (все примеры, которые я видел до сих пор, похоже, не дотягивают до реального случая, когда вы хотите выполнить излучение). событие и объединяют это с обработкой исключений).

Заранее спасибо

1 Ответ

0 голосов
/ 22 февраля 2020

Вы можете легко переходить от наблюдаемых, например,

var a = Observable.Range(0, 10);
var b = a.Select(x => x * x);
var c = a.Select(x => x * 10);

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

Я создал решение, которое создает две ветви из наблюдаемой источника и поворачивает каждый в событие:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var service = new Service();
        var apiCall = service.CallApi();
        apiCall.OnSuccess.OnNext += (_, __) => Console.WriteLine("Success!");
        apiCall.OnTimeout.OnNext += (_, __) => Console.WriteLine("Timeout!");
        Console.ReadLine();
    }
}
class SuccessEventArgs{}
class TimeoutEventArgs{}

class ApiCall
{
    public IEventPatternSource<SuccessEventArgs> OnSuccess {get;}

    public IEventPatternSource<TimeoutEventArgs> OnTimeout {get;}

    public ApiCall(IEventPatternSource<SuccessEventArgs> onSuccess, IEventPatternSource<TimeoutEventArgs> onTimeout)
    {
        OnSuccess = onSuccess;
        OnTimeout = onTimeout;
    }
}
class Service
{
    public ApiCall CallApi()
    {    
        var apiCall = Observable
            .Timer(TimeSpan.FromSeconds(3))
            .Do(_ => Console.WriteLine("Api Called"))
            .Select(_ => new EventPattern<SuccessEventArgs>(null, new SuccessEventArgs()))
            // .Timeout(TimeSpan.FromSeconds(2)) // uncomment to time out
            .Timeout(TimeSpan.FromSeconds(4))
            // the following two lines turn the "cold" observable "hot"
            // comment them out and see how often "Api Called" is logged
            .Publish()
            .RefCount();

        var success = apiCall
            // ignore the TimeoutException and return an empty observable
            .Catch<EventPattern<SuccessEventArgs>, TimeoutException>(_ => Observable.Empty<EventPattern<SuccessEventArgs>>())
            .ToEventPattern();

        var timeout = apiCall
            .Materialize() // turn the exception into a call to OnNext rather than OnError
            .Where(x => x.Exception is TimeoutException)
            .Select(_ => new EventPattern<TimeoutEventArgs>(null, new TimeoutEventArgs()))
            .ToEventPattern();

        return new ApiCall(success, timeout);
    }
}
...