我正在尝试使用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);
}
}
答案 0 :(得分:13)
序列的Rx语法定义为:
OnNext *(OnError | OnCompleted)?
接收OnError
或OnCompleted
信号表示序列的结束和管道上的订阅预计会被拆除。
在运营商的背景下:
observable.Retry(n)
是:收到observable
时重新订阅OnError
,最多n次。
observable.Finally(action)
是:在收到action
OnError|OnCompleted
重试意味着与冷观察者一起使用(Lee Campbell有a good post on this),其中订阅主要导致源开始。
同样Repeat
与Retry
完全相同,只是在收到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的其他可能性。