递归异步 HttpWebRequest

发布于 2024-10-26 17:49:03 字数 1059 浏览 1 评论 0原文

假设我有以下类:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情丝乱 2024-11-02 17:49:03

掌握异步编码的窍门并不容易,尤其是当一个操作与下一个操作之间存在某种顺序依赖性时。这正是我编写 AsyncOperationService 来处理的问题,它是一段非常短的代码。

首先为您简单阅读一下: 简单的异步操作运行器 - 第 2 部分。无论如何,请阅读第 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

     }
 }

请注意,该博客还有一个 DownloadString 的实现,它又使用 WebClient 来简化事情。然而,如果由于某种原因您必须坚持使用 HttpWebRequest,这些原则仍然适用。 (如果您在为此创建 AsyncOperation 时遇到问题,请告诉我)

然后您可以像这样使用此代码:-

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 

请注意,对 Run 的调用是异步的,代码执行在获取所有项目之前似乎会跳过它。如果 UI 对用户来说无用甚至危险,那么您需要以友好的方式阻止它。执行此操作的最佳方法 (IMO) 是使用工具包中的 BusyIndi​​cator 控件,在调用 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.

 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

     }
 }

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 an AsyncOperation for this)

You would then use this code like this:-

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 

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 the BusyIndicator control from the toolkit, setting its IsBusy property after the call to Run and clearing it in the Run callback.

誰ツ都不明白 2024-11-02 17:49:03

你所需要的只是一个线程同步的东西。我选择了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();
   }
}

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?

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();
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文