如何使用异步调用从一个中央WCF服务调用许多远程WCF服务?

时间:2014-03-05 21:36:52

标签: c# .net wcf asynchronous

简单场景:

  • 客户端向中央WCF服务器发出AJAX同步调用==> url:“svc / About.svc / GetAboutInfo”;
  • WCF“GetAboutInfo()”将在80个远程服务器中调用“GetSiteInfo()”;
  • 我得到了结果,但这需要一段时间,因为这些不是异步调用;
  • 考虑到这一点,我有两件事需要解决(但我不知道如何):

    1 - 使GetSiteInfo()成为异步调用;

    2 - 仅在返回来自GetSiteInfo()的所有异步调用后将GetAboutInfo()返回给客户端;

注意:由于我们仍然使用.Net 3.5,因此无法使用“任务”。 目前我正在研究IAsyncResult(使用Begin / End方法),但找不到任何可以让我适应下面的当前代码的内容。 (我在循环中从中央WCF调用远程服务器。)

请记住,除了仅存在于“CENTRAL WCF服务器”(调用远程服务器的WCF)中的循环之外,下面的WCF在所有远程服务器中都是相同的。这是部分WCF代码:

    [ServiceContract]
    public interface IAbout
    {
      [OperationContract(Name = "About_GetAboutInfo")]
      [WebGet(UriTemplate = "/GetAboutInfo", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
      About.AboutInfo GetAboutInfo();

      [OperationContract(Name = "About_GetSiteInfo")]
      [WebGet(UriTemplate = "/GetSiteInfo", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
      About.SiteInfo GetSiteInfo();
    }


      public SiteInfo GetSiteInfo()
      {
        SiteInfo siteInfo = new SiteInfo();
        //...code stripped out...
        return (siteInfo);
      }

      public AboutInfo GetAboutInfo()
      {
          AboutInfo aboutInfo = new AboutInfo();

          SiteInfo siteInfo = new SiteInfo()
          {
             ID                = site.ID
            ,Name              = site.Name
            ,DatabaseVersion   = "Unavailable"
          };

          foreach (Site site in sites)
          {
            try
            {
              string uri = Utilities.CombineUri(site.Uri, "svc/About.svc/ws");
              AboutServiceClient wcfClient = new AboutServiceClient("About");
              wcfClient.Endpoint.Address   = new EndpointAddress(uri);
              SiteInfo childSiteInfo       = wcfClient.GetSiteInfo();  <== call is blocked here until I get a response from remote server
              siteInfo.DatabaseVersion     = childSiteInfo.DatabaseVersion;
            }
            catch (Exception e)
            { //...code stripped out... }

            aboutInfo.Sites.Add(siteInfo); <== this should only be returned after we receive response from all calls
      }

...

    public class AboutServiceClient : ClientBase<IAbout>
    {
      public AboutServiceClient(Binding Binding, EndpointAddress Address) : base(Binding, Address)
      {
        if (Binding is WebHttpBinding)
        {
          this.Endpoint.Behaviors.Add(new WebHttpBehavior());
        }
      }

      public AboutServiceClient(string endpointConfigurationName) : base(endpointConfigurationName)
      { }

      public About.SiteInfo GetSiteInfo()
      { return base.Channel.GetSiteInfo(); }

      public About.AboutInfo GetAboutInfo()
      { return base.Channel.GetAboutInfo(); }
    }

谢谢

2 个答案:

答案 0 :(得分:1)

使用.NET 3.5是一个主要的限制。您将无法保持线性代码流。这是新的工作流程:

  • 您需要按照"How to: Implement an Asynchronous Service Operation"中的说明实施BeginGetAboutInfo / EndGetAboutInfo
  • BeginGetAboutInfo中,您将使用wcfClient.GetSiteInfoBegin(并行)向远程WCF服务启动80个异步请求,并跟踪每个IAsyncResult
  • 当这些异步操作完成时(将为每个操作调用您的回调),使用wcfClient.EndSiteInfoBegin来检索并存储每个操作的结果。
  • 一旦完成所有这些操作,请调用客户在调用BeginGetAboutInfo时提供的回调。
  • 现在希望客户致电您的EndGetAboutInfo,在那里您可以提供所有80项操作的综合结果。

答案 1 :(得分:0)

您可以从NuGEt安装“.NET 3.5任务并行库”并以此方式使用任务。然后,您可以使用Task.Factory.FromAsync包裹wcfClient.GetSiteInfoBeginwcfClient.EndSiteInfoBegin方法。

这是未经测试的,但也许是这样的:

public AboutInfo GetAboutInfo()
{
 AboutInfo aboutInfo = new AboutInfo();

 SiteInfo siteInfo = new SiteInfo()
 {
    ID                = site.ID
   ,Name              = site.Name
   ,DatabaseVersion   = "Unavailable"
 };

  var tasks = new List<Task<SiteInfo>>();
 foreach (Site site in sites)
 {
   try
   {
     string uri = Utilities.CombineUri(site.Uri, "svc/About.svc/ws");
     AboutServiceClient wcfClient = new AboutServiceClient("About");
     wcfClient.Endpoint.Address   = new EndpointAddress(uri);
      tasks.Add(Task<SiteInfo>.Factory.FromAsync(wcfClient.GetSiteInfoBegin, wcfClient.EndSiteInfoBegin, null)
       .ContinueWith(t =>
       {                    
            siteInfo.DatabaseVersion = t.Result.DatabaseVersion.DatabaseVersion;
       }, TaskScheduler.FromCurrentSynchronizationContext()));

   }
   catch (Exception e)
   { //...code stripped out... 
    }
}

Task.WhenAll(tasks.ToArray()).ContinueWith
    ( ts =>
    {
        ts.ForEach( t => aboutInfo.Sites.Add(t.Rrsult); 
    });

return aboutInfo;   

}