递归异步 HttpWebRequest
假设我有以下类:
Public class FooBar
{
List<Items> _items = new List<Items>();
public List<Items> FetchItems(int parentItemId)
{
FetchSingleItem(int itemId);
return _items
}
private void FetchSingleItem(int itemId)
{
Uri url = new Uri(String.Format("http://SomeURL/{0}.xml", itemId);
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(url);
webRequest.BeginGetResponse(ReceiveResponseCallback, webRequest);
}
void ReceiveResponseCallback(IAsyncResult result)
{
// End the call and extract the XML from the response and add item to list
_items.Add(itemFromXMLResponse);
// If this item is linked to another item then fetch that item
if (anotherItemIdExists == true)
{
FetchSingleItem(anotherItemId);
}
}
}
可能有任意数量的链接项,我只能在运行时知道。
我想要做的是对 FetchSingleItem
进行初始调用,然后等待所有调用完成,然后将 List
返回到调用代码。
有人能指出我正确的方向吗?如果需要的话,我非常乐意重构整个事情(我怀疑情况会是这样!)
Suppose I have the following class:
Public class FooBar
{
List<Items> _items = new List<Items>();
public List<Items> FetchItems(int parentItemId)
{
FetchSingleItem(int itemId);
return _items
}
private void FetchSingleItem(int itemId)
{
Uri url = new Uri(String.Format("http://SomeURL/{0}.xml", itemId);
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(url);
webRequest.BeginGetResponse(ReceiveResponseCallback, webRequest);
}
void ReceiveResponseCallback(IAsyncResult result)
{
// End the call and extract the XML from the response and add item to list
_items.Add(itemFromXMLResponse);
// If this item is linked to another item then fetch that item
if (anotherItemIdExists == true)
{
FetchSingleItem(anotherItemId);
}
}
}
There could be any number of linked items that I will only know about at runtime.
What I want to do is make the initial call to FetchSingleItem
and then wait until all calls have completed then return List<Items>
to the calling code.
Could someone point me in the right direction? I more than happy to refactor the whole thing if need be (which I suspect will be the case!)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
掌握异步编码的窍门并不容易,尤其是当一个操作与下一个操作之间存在某种顺序依赖性时。这正是我编写 AsyncOperationService 来处理的问题,它是一段非常短的代码。
首先为您简单阅读一下: 简单的异步操作运行器 - 第 2 部分。无论如何,请阅读第 1 部分,但它比我预想的要重一些。您真正需要的只是其中的
AsyncOperationService
代码。现在,在您的情况下,您可以将获取代码转换为如下所示。
请注意,该博客还有一个
DownloadString
的实现,它又使用 WebClient 来简化事情。然而,如果由于某种原因您必须坚持使用 HttpWebRequest,这些原则仍然适用。 (如果您在为此创建AsyncOperation
时遇到问题,请告诉我)然后您可以像这样使用此代码:-
请注意,对
Run
的调用是异步的,代码执行在获取所有项目之前似乎会跳过它。如果 UI 对用户来说无用甚至危险,那么您需要以友好的方式阻止它。执行此操作的最佳方法 (IMO) 是使用工具包中的BusyIndicator
控件,在调用Run
后设置其IsBusy
属性并清除它在Run
回调中。Getting the hang of asynchronous coding is not easy especially when there is some sequential dependency between one operation and the next. This is the exact sort of problem that I wrote the
AsyncOperationService
to handle, its a cunningly short bit of code.First a little light reading for you: Simple Asynchronous Operation Runner – Part 2. By all means read part 1 but its a bit heavier than I had intended. All you really need is the
AsyncOperationService
code from it.Now in your case you would convert your fetch code to something like the following.
Note the blog also has an implementation of
DownloadString
which in turn uses WebClient which simplifies things. However the principles still apply if for some reason you must stick with HttpWebRequest. (Let me know if you are having trouble creating anAsyncOperation
for this)You would then use this code like this:-
Note that the call to
Run
is asynchronous, code execution will appear to jump past it before all the items are fetched. If the UI is useless to the user or even dangerous then you need to block it in a friendly way. The best way (IMO) to do this is with theBusyIndicator
control from the toolkit, setting itsIsBusy
property after the call toRun
and clearing it in theRun
callback.你所需要的只是一个线程同步的东西。我选择了ManualResetEvent。
但是,我不明白使用异步 IO 的意义,因为您总是等待请求完成后再开始新的请求。但这个例子可能并不能说明整个故事?
All you need is a thread sync thingy. I chose
ManualResetEvent
.However, I don't see the point of using asynchronous IO since you always wait for the request to finish before starting a new one. But the example might not show the whole story?