这是我对WebClient的异步下载反应式扩展。 在操作成功之前,一次又一次地回忆“DownloadStringAsync”的最佳方法是什么?
这样的东西,但是以反应的方式:
while (true)
{
var result = DownloadStringAsync();
if (result)
{
return;
}
}
我的代码:
[Serializable]
public class WebClientException : Exception
{
public WebClientResponse Response { get; set; }
public WebClientException()
{
}
public WebClientException(string message)
: base(message)
{
}
public WebClientException(string message, Exception innerException)
: base(message, innerException)
{
}
protected WebClientException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
public class WebClientResponse
{
public WebHeaderCollection Headers { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string Result { get; set; }
public WebException Exception { get; set; }
}
public static IObservable<WebClientResponse> DownloadStringAsync(this WebClient webClient, Uri address, WebHeaderCollection requestHeaders)
{
var asyncResult =
Observable.FromEventPattern<DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs>
(ev => webClient.DownloadStringCompleted += ev, ev => webClient.DownloadStringCompleted -= ev)
.ObserveOn(Scheduler.TaskPool)
.Select(o =>
{
var ex = o.EventArgs.Error as WebException;
if (ex == null)
{
var wc = (WebClient) o.Sender;
return new WebClientResponse {Headers = wc.ResponseHeaders, Result = o.EventArgs.Result};
}
var wcr = new WebClientResponse {Exception = ex};
var r = ex.Response as HttpWebResponse;
if (r != null)
{
wcr.Headers = r.Headers;
wcr.StatusCode = r.StatusCode;
var s = r.GetResponseStream();
if (s != null)
{
using (TextReader tr = new StreamReader(s))
{
wcr.Result = tr.ReadToEnd();
}
}
}
throw new WebClientException {Response = wcr};
})
.Take(1);
if (requestHeaders != null)
{
foreach (var key in requestHeaders.AllKeys)
{
webClient.Headers.Add(key, requestHeaders[key]);
}
}
webClient.DownloadStringAsync(address);
return asyncResult;
}
答案 0 :(得分:1)
您的方法生成一个热的observable,这意味着它在返回时已经开始加载,并且每个新订阅都不会向Web服务器创建新请求。你需要将你的方法包装在另一个中并使用Observable.Create(为了创建一个冷的observable,它会在每次订阅时创建一个新的请求):
public static IObservable<WebClientResponse> DownloadStringAsync(this WebClient webClient, Uri address, WebHeaderCollection requestHeaders)
{
return Observable
.Create(observer =>
{
DownloadStringAsyncImpl(webClient, address, requestHeaders)
.Subscribe(observer);
return () => { webClient.CancelAsync(); };
});
}
这里,DownloadStringAsyncImpl是您之前的DownloadStringAsync实现,而公共方法已被替换。
现在您可以重试异步方法,直到成功,如下所示:
myWebClient
.DownloadStringAsync( /* args... */)
.Retry()
.Subscribe(result => {
/* now I've got a result! */
});
答案 1 :(得分:1)
我认为你至少有一个不错的“这里是一些代码”的答案,所以我将专注于更普遍的手持。
我要看的第一件事是design guidelines for Rx。这是一个简短的(34页)PDF文档,有助于将范式从拉“订阅”更改为推送,或从IEnumerable转移到IObservable。
如果您想进一步了解,.NET和JavaScript都有PDF HOL(动手练习)。您可以在Rx页面上找到其他资源(start here)。
答案 2 :(得分:0)
如果是异步功能。进行重复检查意味着您将其转换为同步函数调用。这是你真正想做的事吗?
您可以让一个专用线程调用此异步函数,并在调用此函数后阻止自身。创建此线程时,传递一个应该在异步函数返回后调用的委托。完成后,使用错误代码调用代理。
希望这能回答你的问题。