使用 Web 客户端接收参数异常

发布于 2024-12-08 23:40:09 字数 1109 浏览 1 评论 0原文

我正在使用反应式扩展,以便使用 Windows Phone 上的 WebClient 轻松下载网页。 当我运行以下代码时,我在 Subscribe 调用中收到 ArgumentExceptoin。

参数名称:{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)

            );

I am working with the Reactive Extensions, to easy download webpages with the WebClient on Windows Phone.
When I run the following code, I get an ArgumentExceptoin on the Subscribe call.

Parameter name: {0}

Parameter name: type

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)

            );

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

送君千里 2024-12-15 23:40:09

您的问题是您使用的是 FromEvent 扩展,而不是 FromEventPattern 扩展。

前者适用于非标准事件,后者适用于遵循“sender/eventargs”模式的事件。

也就是说,您的可观察值仍然存在一些问题。

  • WebClient 的实例永远不会被释放。你应该做
    当然会发生这种情况,但因为你正在使用可观察的你需要
    使用 Using 扩展方法。

  • Take(1) 之前放置 Where 可能意味着您的可观察值
    永远不会结束。

  • 重试也不会为您做正确的事情。如果
    “DownloadStringCompleted” observable 有错误然后重试
    将重新附加到事件,但事件将永远不会返回新的
    值,因为您不会再次调用 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 参数)到方法调用)。

Your issue is that you're using the FromEvent extension rather than the FromEventPattern extension.

The former is for non-standard events and the latter for those that follow the "sender/eventargs" pattern.

That said, you still have a few more issues with your observable.

  • The instance of WebClient is never disposed of. You should make
    sure that happens, but because you're using an observable you need to
    use the Using extension method.

  • Putting a Where before the Take(1) may mean that your observable
    will never end.

  • The Retry is also not going to do the right thing for you. If the
    "DownloadStringCompleted" observable has an error then retrying it
    will re-attach to the event, but the event will never return a new
    value because you're not calling DownloadStringAsync again.

What you want to do with this kind of observable is make it re-execute the web call every time a new observer subscribes, but, when a subscription is shared (if the observable is published, for example) then you don't want the web call to re-execute. You kind of want to have your cake and eat it too.

Here's how to do it:

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));
    });
}

Now you can call it like this:

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

Notice that the Retry is now outside of the request method where it should belong (unless you add a retry argument to the method call).

赠意 2024-12-15 23:40:09

您不能将 Retry 与 FromEvent 结合使用,因为 FromEvent 是一个热门可观察对象 - 您只会获得 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,而是为每个 Subscribe() 的 Observable 的人创建它,这是重试所需的。

You can't use Retry with FromEvent, because FromEvent is a hot observable - you'll just get the same result 3 times. The easiest way to fix your existing code is via 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 means that instead of WebClient being created immediately, it will be created for everyone who Subscribe()'s to the Observable, which is what is required for Retry.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文