C# 递归异步HttpWebRequests
假设我有以下课程:C# 递归异步HttpWebRequests,c#,silverlight-4.0,asynchronous,recursion,httpwebrequest,C#,Silverlight 4.0,Asynchronous,Recursion,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 it
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);
}
}
}
公共类FooBar
{
列表_items=新列表();
公共列表获取项(int parentItemId)
{
FetchSingleItem(int itemId);
退货项目
}
私有void FetchSingleItem(int-itemId)
{
Uri url=新的Uri(String.Format(“http://SomeURL/{0}.xml”,itemId);
HttpWebRequestWebRequest=(HttpWebRequest)HttpWebRequest.Create(url);
webRequest.BeginGetResponse(接收方回复,webRequest);
}
无效接收方响应回拨(IAsyncResult结果)
{
//结束调用并从响应中提取XML,然后将项添加到列表中
_items.Add(itemFromXMLResponse);
//如果此项链接到另一项,则获取该项
if(anotherItemIdExists==true)
{
FetchSingleItem(另一个项目ID);
}
}
}
可能有任意数量的链接项,我只能在运行时知道
我要做的是对FetchSingleItem
进行初始调用,然后等待所有调用完成,然后将List
返回调用代码
有人能给我指出正确的方向吗?如果需要的话,我非常乐意重构整个过程(我怀疑会是这样!)您所需要的只是一个线程同步的东西。我选择了
ManualResetEvent
但是,我不认为使用异步IO有什么意义,因为您总是在启动新的请求之前等待请求完成
Public class FooBar
{
private ManualResetEvent _completedEvent = new ManualResetEvent(false);
List<Items> _items = new List<Items>();
public List<Items> FetchItems(int parentItemId)
{
FetchSingleItem(itemId);
_completedEvent.WaitOne();
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);
}
else
_completedEvent.Set();
}
}
公共类FooBar
{
private ManualResetEvent _completedEvent=新的ManualResetEvent(错误);
列表_items=新列表();
公共列表获取项(int parentItemId)
{
FetchSingleItem(itemId);
_completedEvent.WaitOne();
退货项目
}
私有void FetchSingleItem(int-itemId)
{
Uri url=新的Uri(String.Format(“http://SomeURL/{0}.xml”,itemId);
HttpWebRequestWebRequest=(HttpWebRequest)HttpWebRequest.Create(url);
webRequest.BeginGetResponse(接收方回复,webRequest);
}
无效接收方响应回拨(IAsyncResult结果)
{
//结束调用并从响应中提取XML,然后将项添加到列表中
_items.Add(itemFromXMLResponse);
//如果此项链接到另一项,则获取该项
if(anotherItemIdExists==true)
{
FetchSingleItem(另一个项目ID);
}
其他的
_completedEvent.Set();
}
}
掌握异步编码的诀窍并不容易,尤其是当一个操作和下一个操作之间存在某种顺序依赖关系时。这正是我编写的AsyncOperationService
要处理的问题,这是一段巧妙的短代码
首先让您读一点简单的内容:。当然,请阅读第1部分,但它比我预期的要重一些。您真正需要的是它提供的AsyncOperationService
代码
现在,在您的例子中,您可以将您的获取代码转换为如下内容
private IEnumerable<AsyncOperation> FetchItems(int startId)
{
XDocument itemDoc = null;
int currentId = startId;
while (currentID != 0)
{
yield return DownloadString(new Uri(String.Format("http://SomeURL/{0}.xml", currentId), UriKind.Absolute),
itemXml => itemDoc = XDocument.Parse(itemXml) );
// Do stuff with itemDoc like creating your item and placing it in the list.
// Assign the next linked ID to currentId or if no other items assign 0
}
}
请注意,对
Run
的调用是异步的,在获取所有项目之前,代码执行似乎会跳过它。如果用户界面对用户没有用处,甚至是危险的,那么您需要以友好的方式阻止它。最佳方式(IMO)要做到这一点,需要使用工具箱中的BusyIndicator
控件,在调用Run
后设置其IsBusy
属性,并在Run
回调中清除它。我猜您的意思是\u completedEvent.WaitOne()
为Wait()
不存在?我已经尝试过了,它会一直等待。通过代码点击WaitOne()
然后再也不会到达ReceiveResponseCallBack
方法。是的。我的意思是WaitOne()
。如果web服务器存在并且正在响应,则您所说的是不可能的。在事件开始等待之前会调用BeginGetResponse
。操作IO是在线程池线程中执行的,不应受到等待线程的影响。您的代码中一定有其他错误。如果我删除WaitOne()
它会立即将\u items
返回给调用代码(调用FetchItems
但继续在后台运行的代码(逐步执行回调方法)。如果存在WaitOne
,它将停止所有处理并挂起。在回调的顶部设置断点。然后逐步检查代码,确保使用正确的信息调用Begin方法。我一直使用异步方法,从未遇到过您描述的问题。还要确保没有任何异常ns,这可能会阻止您报告已完成。@巴里:我不建议您使用这样的阻止方法。如果您确实觉得必须在使用WaitOne
阻止之前使用后台工作程序将其从主UI线程中移出。您还需要一种机制来处理错误情况,以便FetchItems不会发生错误无法无限期阻止,并且在未完成时似乎未正确完成。为什么不使用WebClient.DownloadStringAsync?
int startId = GetSomeIDToStartWith();
Foo myFoo = new Foo();
myFoo.FetchItems(startId).Run((err) =>
{
// Clear IsBusy
if (err == null)
{
// All items are now fetched continue doing stuff here.
}
else
{
// "Oops something bad happened" code here
}
}
// Set IsBusy