【问题标题】:Async CTP - How can I use async/await to call a wcf service?Async CTP - 如何使用 async/await 调用 wcf 服务?
【发布时间】:2011-05-04 23:02:48
【问题描述】:

如果我调用 WCF 服务方法,我会这样做:

proxy.DoSomethingAsync();
proxy.DoSomethingAsyncCompleted += OnDoSomethingAsyncCompleted;

如何使用新的async ctp 做同样的事情? 我想我需要proxy.DoSomethingTaskAsyncproxy.DoSomethingAsync().ToTask() 之类的东西? Web 服务调用需要返回一个Task<T> 才能使用await 关键字,但是如何使用??

【问题讨论】:

  • 更正。 +1 只是为了向我介绍一些闪亮的新事物。

标签: c# asynchronous async-await


【解决方案1】:

在 CTP 中,有一些工厂方法可以将常规 APM 函数(Begin/End)转换为与新的 async 关键字兼容的函数,例如:

Stream s = new FileStream("C:\test.txt", FileMode.CreateNew);
byte []buffer = new byte[100];
int numBytesRead = await Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);

所以在你的情况下,你可以做同样的事情,然后你会这样称呼它:

async proxy.DoSomethingTaskAsync()

请参阅 CTP 讨论组上的 this thread 了解更多信息

【讨论】:

  • 谢谢你的回答,但我还是不明白。正如您在示例中所示,我了解这对于流读取是如何工作的,但是如何将其应用于 wcf 服务调用?
  • 生成的代理将具有 BeginDoSomething/EndDoSomething 方法(前提是您将正确的选项传递给 svcutil/wizards)。
【解决方案2】:

使用 async-await 的异步服务响应速度非常快,因为它可以交错许多客户端调用并并行执行它们 (2)。 尽管如此,服务可以在一个线程 (3) 上完全线程安全地运行,并且可以是单例服务 (1) 或由框架为会话或调用创建的服务对象。

在实现服务时,请注意ServiceBehaviourAttributes(1)...(3):

    [ServiceContract( Namespace="X", Name="TheContract" )]
    public interface IAsyncContractForClientAndService
    {
        [OperationContract]
        Task<TResponse> SendReceiveAsync( TRequest req );
    }



    [ServiceBehavior (InstanceContextMode = InstanceContextMode.Single, // (1)
                      // also works with InstanceContextMode.PerSession or PerCall
                      ConcurrencyMode     = ConcurrencyMode.Multiple,   // (2)
                      UseSynchronizationContext = true)]                // (3)

    public MyService : IAsyncContractForClientAndService
    {
        public async Task<TResponse> SendReceiveAsync( TRequest req )
        {
            DoSomethingSynchronous();
            await SomethingAsynchronous(); 
            // await lets other clients call the service here or at any await in
            // subfunctions. Calls from clients execute 'interleaved'.
            return new TResponse( ... );
        }
    }

要在一个线程上运行每个调用,System.Threading.SynchronizationContext.Current != null 在您 Open() ServiceHost 时必须存在。 使用 SynchronizationContext,您无需关心锁。原子的、不可中断的代码段大致从一个 await 延伸到下一个。 请注意共享服务数据在每次等待时都处于一致状态,并注意来自一个客户端的连续请求可能不会按照发送顺序进行响应。

在客户端,异步服务操作是等待的:

   var response = await client.Channel.SendReceiveAsync( request );

操作合约中不能使用outref参数。所有响应数据都必须通过返回值Task(T)来传递。
我在AsyncWcfLib使用这个接口,它支持Actor based programming model

【讨论】:

    【解决方案3】:

    Async CTP 中有一个 WCF 示例,它将向您展示如何在 WCF 中使用 async/await 模型。

    关于在 WCF 中支持该模型的计划,你可以看看下面的帖子:

    http://blogs.msdn.com/b/endpoint/archive/2010/11/13/simplified-asynchronous-programming-model-in-wcf-with-async-await.aspx

    希望这会有所帮助。

    阿玛迪欧

    【讨论】:

    • 可惜这些功能还不可用。
    【解决方案4】:

    正如 Matt 所提到的,有一个 TaskFactory.FromAsync 方法允许您从 Begin/End 对创建 Task。您需要在添加 WCF 引用时启用异步终结点,然后您可以使用扩展方法自己包装它们。

    正如 Amadeo 所提到的,在 Async CTP 中有一个示例,位于 (C# WCF) Stock Quotes 目录下。

    同样在那个目录中,有一个TaskWsdlImportExtension 项目。添加对该 dll 的引用并修改您的 .config:

    <configuration>
     <system.serviceModel>
      <client>
       <metadata>
        <wsdlImporters>
         <extension type="TaskWsdlImportExtension.TaskAsyncWsdlImportExtension, TaskWsdlImportExtension" />
        </wsdlImporters>
       </metadata>
      </client>
     </system.serviceModel>
    </configuration>
    

    那么您根本不必自己进行包装; TaskWsdlImportExtension 会为你做这件事。

    【讨论】:

      【解决方案5】:

      异步客户端调用同步服务是很常见的。
      以下客户端和服务合同匹配(在幕后使用了一点魔法):

          [ServiceContract( Namespace="X", Name="TheContract" )]
          public interface IClientContractAsynchronous
          {
              [OperationContract]
              Task<TResponse> SendReceiveAsync( TRequest req );
          }
      
          [ServiceContract( Namespace="X", Name="TheContract" )]
          public interface IServiceContractSynchronous
          {
              [OperationContract]
              TResponse SendReceive( TRequest req );
          }
      

      客户端接口直接等待:

         var response = await client.Channel.SendReceiveAsync( request );
      

      在操作合约中不能使用 outref 参数。所有响应数据都必须在返回值中传递。这对我来说实际上是一个重大变化。
      我在AsyncWcfLib使用这个接口,它支持Actor based programming model

      【讨论】:

      • 您是否应该将 2 个答案合并为 1 个?
      【解决方案6】:

      您正确地指出 async/await 是围绕返回 Task 和 Task 的方法构建的(有关 async/await 工作原理的详细说明,请参阅 here)。以下是为 WCF 服务生成基于任务的方法的方法:

      1. Visual Studio 2012 RC 在“配置服务参考”对话框中有一个附加复选框:“生成基于任务的操作”。虽然我没有在MSDN 中看到该选项,但我希望它的存在专门允许 WCF 调用上的无缝异步/等待。

      2. 对于早期版本,请查看this post,它描述了一个扩展,即使使用 CTP,它也允许在 WCF 上生成基于 Task 的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-18
        • 2021-11-18
        • 2020-06-06
        相关资源
        最近更新 更多