异步CTP - 如何使用async / await来调用wcf服务?

时间:2010-11-04 09:22:28

标签: c# asynchronous async-await

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

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

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

6 个答案:

答案 0 :(得分:10)

在CTP中,有一些工厂方法可以将常规APM功能(开始/结束)转换为与新的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

答案 1 :(得分:6)

使用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( ... );
        }
    }

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

在客户端,异步服务操作是可以接受的:

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

在操作合同中无法使用 out ref 参数。所有响应数据必须由返回值Task(T)传递 我在AsyncWcfLib中使用此界面,它支持Actor based programming model

答案 2 :(得分:4)

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

希望这有帮助。

阿马德奥

答案 3 :(得分:2)

如Matt所述,有一种TaskFactory.FromAsync方法可让您从Task / Begin对中创建End。您需要在添加WCF引用时启用异步端点,然后可以使用扩展方法自行封装它们。

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

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

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

然后你根本不需要做自己的包装; TaskWsdlImportExtension将为您完成。

答案 4 :(得分:2)

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

    [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 );

在operaton合同中无法使用 out ref 参数。必须在返回值中传递所有响应数据。这对我来说实际上是一个突破性的变化 我在AsyncWcfLib中使用此界面,它支持Actor based programming model

答案 5 :(得分:1)

你正确地指出async / await是围绕返回Task和Task的方法构建的(有关async / await工作的一个很好的解释,请参阅here)。以下是如何为WCF服务生成基于任务的方法:

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

  2. 对于早期版本,请查看this post,描述允许生成任务&lt;&gt;的扩展程序。基于CTP的基于WCF的方法。