Rx аргументException с веб-клиентом - PullRequest
0 голосов
/ 08 октября 2011

Я работаю с Reactive Extensions, чтобы легко загружать веб-страницы с помощью WebClient на Windows Phone.Когда я запускаю следующий код, я получаю ArgumentExceptoin при вызове Subscribe.

Имя параметра: {0}

Имя параметра: тип

public IObservable<DownloadStringCompletedEventArgs> StartRequest(string uri)
    {

        var _browser = new WebClient();
        var obs = Observable.FromEvent<DownloadStringCompletedEventHandler,
            DownloadStringCompletedEventArgs>(

                h => _browser.DownloadStringCompleted += h,
                h => _browser.DownloadStringCompleted -= h)
            .Where(e => !e.Cancelled)
            .Retry(3)
            .SelectMany(
                e => (e.Error == null && !string.IsNullOrEmpty(e.Result))
                         ? Observable.Return(e)
                         : Observable.Throw<DownloadStringCompletedEventArgs>(e.Error))
            .Take(1);



        _browser.DownloadStringAsync(new Uri(uri));

        return obs;
    }

 var obs = StartRequest("http://www.google.com");

        obs.Subscribe(
            x => Console.WriteLine(x.Result)

            );

Ответы [ 2 ]

2 голосов
/ 09 октября 2011

Ваша проблема в том, что вы используете расширение FromEvent, а не расширение FromEventPattern.

Первый предназначен для нестандартных событий, а второй - для тех, которые следуют шаблону «отправитель / eventargs».

Тем не менее, у вас все еще есть несколько проблем с вашим наблюдаемым.

  • Экземпляр WebClient никогда не удаляется. Вы должны сделать конечно, это происходит, но поскольку вы используете наблюдаемое, вам нужно используйте метод расширения Using.

  • Помещение Where перед Take(1) может означать, что ваша наблюдаемая никогда не закончится.

  • Retry также не будет делать правильные вещи для вас. Если Наблюдаемая "DownloadStringCompleted" имеет ошибку, затем повторяет попытку повторно присоединится к событию, но событие никогда не вернет новое значение, потому что вы больше не звоните DownloadStringAsync.

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

Вот как это сделать:

public IObservable<string> CreateDownloadStringObservable(string uri)
{
    return Observable.Create<string>(o =>
    {
        var result = new ReplaySubject<string>();
        var inner = Observable.Using(() => new WebClient(), wc =>
        {
            var obs = Observable
                .FromEventPattern<
                    DownloadStringCompletedEventHandler,
                    DownloadStringCompletedEventArgs>(
                        h => wc.DownloadStringCompleted += h,
                        h => wc.DownloadStringCompleted -= h)
                .Take(1);
            wc.DownloadStringAsync(new Uri(uri));
            return obs;
        }).Subscribe(ep =>
        {
            if (ep.EventArgs.Cancelled)
            {
                result.OnCompleted();
            }
            else
            {
                if (ep.EventArgs.Error != null)
                {
                    result.OnError(ep.EventArgs.Error);
                }
                else
                {
                    result.OnNext(ep.EventArgs.Result);
                    result.OnCompleted();
                }
            }
        }, ex =>
        {
            result.OnError(ex);
        });
        return new CompositeDisposable(inner, result.Subscribe(o));
    });
}

Теперь вы можете назвать это так:

IObservable<string> obs =
    CreateDownloadStringObservable("http://www.google.com")
        .Retry(3);

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

1 голос
/ 09 октября 2011

Вы не можете использовать Retry с FromEvent, потому что FromEvent является наблюдаемым hot - вы просто получите тот же результат 3 раза.Самый простой способ исправить существующий код - через Observable.Defer:

string uri = "http://www.whatever.com";

var webClient = Observable.Defer(() => {
    var browser = new WebClient();
    var ret =  Observable.FromEvent<DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs>(
        h => _browser.DownloadStringCompleted += h,
        h => _browser.DownloadStringCompleted -= h);

    browser.DownloadStringAsync(new Uri(uri));
    return ret;
});

webClient
    .Timeout(TimeSpan.FromSeconds(15))
    .Retry(3);

Defer означает, что вместо немедленного создания WebClient он будет создан для всех, кто подписан () на Observable, чтоэто то, что требуется для Retry.

...