异步调用同步Web服务方法

时间:2016-09-19 18:09:21

标签: c# web-services asynchronous task

我必须每次在一个循环中使用不同的参数调用web服务方法大约一百次。 Web服务只有同步方法。目前我正在一个控制台应用程序中对此进行测试,并且在同步执行时需要十几分钟才能获取数据!

我想要的: 并行运行10个请求。完成后,执行接下来的十个呼叫。 这当然应该是异步的。

该功能将托管在IIS托管的wcf服务中。

概述: 客户端使用params调用wcf服务一次。 wcf服务方法应该异步调用另一个Web服务一百次并将最终数据保存到Excel。

我读过,在网络应用程序中使用 Task.Run 并不是一个好主意。

那么,如何在Web上下文中异步调用同步Web服务方法?

我使用的是CRM Microsoft.Xrm.Sdk,Version = 7.0.0.0,Culture = neutral,PublicKeyToken = 31bf3856ad364e35

bind .note <Configure> {
  set nw [winfo width .note]
  set tc [llength [winfo children .note]]
  ttk::style configure TNotebook.Tab -width [expr {$nw/6/$tc}]
}

RetrieveMultiple获取一个xml片段(strXmlFetch),其中定义了查询并执行请求。 方法就在这里:

  

命名空间 Microsoft.Xrm.Sdk.Client
   OrganizationServiceProxy

  var orders = _serviceProxy.RetrieveMultiple( new FetchExpression(strXmlFetch));

在调用RetrieveMultiple时,客户端SDk会执行以下操作。

public EntityCollection RetrieveMultiple(QueryBase query);

我还没有实现任何东西,因为我需要一个异步执行请求的起点。

2 个答案:

答案 0 :(得分:1)

  

网络服务只有同步方法

默认情况下,Web服务会生成与Asynchronous相关的BeginXX, EndXX方法,这些方法属于Asynchronous Programming Model,但它们不是Async-Await可以使用的方法,因为他们不会返回Task

  

我想要的是:并行运行10个请求。完成后,执行接下来的十个呼叫。这当然应该是异步的。

在真正的Async调用中,因为没有threads被调用,因为它可以在IO完成端口上运行,因此您可以启动更多的调用,而不仅仅是10.另外,调度10个异步请求的逻辑一个时间必须是自定义的,没有开箱即用机制,即使使用Threadpool,也无法保证并行请求数,尽管您可以设置Max Degree of Parallelism,以获得更高的限制用于并行API。

  

概述:客户端使用params调用wcf服务一次。 wcf服务方法应该异步调用另一个Web服务一百次并将最终数据保存到Excel。

您希望所有调用都是Async,这样就不会阻止Ui线程,无论是wcf服务还是Web服务。

  

我读到Task.Run在网络应用程序中使用时并不是一个好主意。

是的,因为调用了ThreadPool线程,在IO调用Web Service之后无法执行任何操作

  

那么,如何在Web上下文中异步调用同步Web服务方法?

需要使用Sync方法的异步包装器,即反模式,但没有其他选项。这需要一个ThreadPool线程,例如:

public class AsyncWrapper
{   
  public async Task CallWcfAsync(<Parameters>)
  {  
       SynchronizationContext _synchronizationContext = 
       SynchronizationContext.Current;

    try
    {
      SynchronizationContext.SetSynchronizationContext(null);
      await Task.Run(() => CallWcfMethod(<Parameters>));
    }
    finally
    {             
      SynchronizationContext.SetSynchronizationContext
      (_synchronizationContext);
    }   
  }
}

重点:

  1. CallWcfAsync是您需要的Async wrapper method

  2. 注意我在执行之前已将同步上下文设置为Null,然后重置它,这与ConfigureAwait(false)的行为类似,否则在Web应用程序中它会导致死锁,如等待的Sychronization Context被阻止。

答案 1 :(得分:1)

  

这当然应该是异步的。

嗯,那将是理想的。但是,OrganizationServiceProxy does not have asynchronous methodsneither does IOrganizationService,因此直接使用ServiceChannel.Channel也无济于事。)

因此,要做的第一件事就是要求Microsoft向该客户端库添加异步API。

  

我读过,在Web应用程序中使用时,Task.Run不是一个好主意。

通常这是真的;你想要调用异步API。在这种情况下无法使用。

  

那么,如何在Web上下文中异步调用同步Web服务方法?

这是不可能的。您的选择是:

  1. 让它保持同步,一次一个。是的,它需要更长的时间。
  2. 让它并行
  3. 服务器端并行处理的问题在于,您现在正在为单个请求使用N个线程,这可以非常快使您的Web服务器瘫痪。我不建议在生产代码中使用这种方法,特别是如果它通过Internet公开。

    但是,如果您确定对WCF服务的调用次数过多,则可以并行实施。

    由于您需要从并行查询中收集结果,因此我建议您使用Parallel LINQAsParallel)指定WithDegreeOfParallelism(10),例如:

    IEnumerable<EntityCollection> GetAll(OrganizationServiceProxy proxy, IEnumerable<QueryBase> queries)
    {
      return queries.AsParallel().WithDegreeOfParallelism(10)
          .Select(query => proxy.RetrieveMultiple(query));
    }