Rx Retry()无法按预期工作

时间:2012-10-18 08:59:38

标签: c# system.reactive

我正在尝试使用Reactive Extensions来获取一堆RSS项目。我基于蒂姆格林菲尔德的博客文章:Silverlight Rx DataClient within MVVM

我在桌面应用程序中使用它,但代码类似。

我遇到的问题是了解Retry()函数。它似乎没有按照我的期望和我期待的那样做。

var items = new List<RssItem>();
WebHelper.DownloadXmlFileAsync<RssItem>(new Uri(URI), "item")
    .Retry(2)
    .Finally(PublishResults)
    .Subscribe(items.Add, ProcessError, () => ProcessCompleted(items));

当我传入有效的URI时,这没有任何问题。当我在URI中输入拼写错误时,它会通过ProcessError()函数报告404错误,正如人们所预料的那样,但它只报告一次。我原以为它会两次显示这个错误。

所以似乎Retry()函数没有在我的Web请求上运行,但看起来它实际上适用于传递给Subscribe()的函数。我可能在这里错了。

如何确保Retry()来电适用于网络请求?

额外代码:

public static class WebHelper
{
    public static HttpWebRequest CreateHttp(Uri uri)
    {
        return CreateHttp(uri, "GET");
    }

    public static HttpWebRequest CreateHttp(Uri uri, string method)
    {
        if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
        {
            throw new ArgumentException("The specified URI does not use HTTP or HTTPS.", "uri");
        }

        var request = (HttpWebRequest)WebRequest.Create(uri);
        request.Method = method;

        return request;
    }

    public static IObservable<T> DownloadXmlFileAsync<T>(Uri uri, string elementName) where T : class
    {
        return (from request in Observable.Return(CreateHttp(uri))
                from response in Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()
                let stream = response.GetResponseStream()
                where stream != null
                from item in XmlReader.Create(stream).GetXmlItem<T>(elementName).ToObservable()
                select item);
    }
}

public static class XmlExtensions
{
    public static IEnumerable<T> GetXmlItem<T>(this XmlReader reader, string elementName) where T : class
    {
        var serializer = new XmlSerializer(typeof (T));
        while (reader.GoToElement(elementName))
        {
            yield return serializer.Deserialize(reader) as T;
        }
    }

    public static bool GoToElement(this XmlReader reader, string elementName)
    {
        do
        {
            if (reader.NodeType == XmlNodeType.Element && reader.Name == elementName)
            {
                return true;
            }
        } while (reader.Read());

        return false;
    }
}

XmlRoot("item")]
public class RssItem
{
    [XmlElement("description")]
    public string Description { get; set; }

    [XmlElement("link")]
    public string Link { get; set; }

    [XmlElement("pubDate")]
    public string PublishDate { get; set; }

    [XmlElement("title")]
    public string Title { get; set; }

    public override string ToString()
    {
        return string.Format("Title: {0}", Title);
    }
}

2 个答案:

答案 0 :(得分:13)

序列的Rx语法定义为:

  

OnNext *(OnError | OnCompleted)?

接收OnErrorOnCompleted信号表示序列的结束和管道上的订阅预计会被拆除。

在运营商的背景下:

observable.Retry(n)是:收到observable时重新订阅OnError,最多n次。

observable.Finally(action)是:在收到action

时执行OnError|OnCompleted

重试意味着与冷观察者一起使用(Lee Campbella good post on this),其中订阅主要导致源开始。

同样RepeatRetry完全相同,只是在收到OnCompleted后重新订阅。

为了看到这一点,我们可以创建一个observable,它将在前n次“失败”,然后成功。 现在有些代码:

    private static IObservable<int> ErrorProducer(int i)
    {
        int count = 0;
        return Observable.Create<int>(observer =>
        {
            Console.WriteLine("Doing work");

            if (count++ < i)
            {
                Console.WriteLine("Failed");
                observer.OnError(new Exception());
            }
            else
            {
                Console.WriteLine("Done");
                observer.OnNext(count);
                observer.OnCompleted();                    
            }
            return Disposable.Empty;
        });
    }

对于总是失败的制作人:

      print(ErrorProducer(3).Retry(2));

给出:

Doing work <-- Subscription
Failed
Doing work <-- Resubscription
Failed
OnError(System.Exception)
Finally

对于最终成功的制作人:

print(ErrorProducer(2).Retry(3));

Doing work
Failed
Doing work
Failed
Doing work
Done
OnNext(3) <-- Succeeded
OnCompleted()
Finally

如果您希望在重试时多次调用过程错误函数,则应将其置于Retry之前。

即,seq.Do(value => { }, exception => { }).Retry(n)

您可以阅读使用热/冷可观察量,并使用与Rx的异步模式来阐明您的理解。

答案 1 :(得分:4)

阿斯蒂的回答很明显。我只是想添加一些额外的信息,以防您想知道如何为单个逻辑序列公开多个错误。

Asti指出,你只能终止序列一次。此终止可以是错误或完成(OnError | OnCompleted)。

然而,没有什么可以阻止你嵌套可观察序列!如果您确实想要查看多条错误消息,请考虑返回IObservable<IObservable<T>>的方案。内部序列是数据序列(您当前拥有的序列)。当此序列出错时,它就不能再使用了,因此外部序列可以产生一个新的内部数据序列。

这可能看起来有点奇怪,但它是Rx支持的概念,因为像Merge和Switch这样的运算符已经满足了这些嵌套序列。我在Nested Sequences段中的IntroToRx书中详细介绍了这种Rx风格,然后在Sequences of Coincidence章节中再次详细介绍

我希望这有助于您了解未来如何使用Rx的其他可能性。