2010-11-04 19 views
15

Si llamo a un método de servicio WCF me gustaría hacer algo como esto:Async CTP: ¿cómo puedo usar async/await para llamar a un servicio wcf?

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

¿Cómo podría hacer lo mismo con el nuevo async CTP? Supongo que necesitaría algo como proxy.DoSomethingTaskAsync o proxy.DoSomethingAsync().ToTask()? La llamada al servicio web debe devolver un Task<T> para poder utilizar la palabra clave await, pero ¿cómo?

+0

Corr. +1 simplemente por presentarme algo brillante y nuevo. – spender

Respuesta

10

En el CTP existen métodos de fábrica que hacen el trabajo de convertir funciones APM regulares (Begin/End) en los que son compatibles con la nueva palabra clave asíncrono, por ejemplo:

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

Así, en su caso puede hacer lo equivalente y entonces lo que luego llamaría así:

async proxy.DoSomethingTaskAsync() 

ver this thread en el grupo de discusión CTP para obtener más información

+0

Gracias por responder, pero todavía no lo entiendo. Entiendo cómo funciona esto para la lectura de flujo como se muestra en su ejemplo, pero ¿cómo lo aplico a una llamada de servicio wcf? – Roger

+0

El proxy generado tendrá métodos BeginDoSomething/EndDoSomething (siempre que pase las opciones correctas a svcutil/wizards). – Brian

2

Como se ha mencionado por Matt, hay un método TaskFactory.FromAsync que le permite crear un Task partir de un par Begin/End. Debe habilitar los puntos finales asíncronos cuando agrega su referencia WCF, y luego puede envolverlos usted mismo usando métodos de extensión.

Según lo mencionado por Amadeo, hay una muestra de esto en el Async CTP, bajo el directorio (C# WCF) Stock Quotes.

También en ese directorio, hay un proyecto TaskWsdlImportExtension. Agregue una referencia a la DLL y modificar su .config como tal:

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

entonces no tiene que hacer su propia envoltura en absoluto; el TaskWsdlImportExtension lo hará por usted.

2

Es bastante común tener clientes asíncronos llamando a un servicio síncrono.
El siguiente partido de cliente y los contratos de servicio (una magia poco utilizado detrás de las escenas):

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

La interfaz de cliente es directamente awaitable:

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

No es posible utilizar cabo o ref parámetros en el contrato de operación. Todos los datos de respuesta se deben pasar en el valor de retorno. Esto realmente fue un cambio radical para mí.
Uso esta interfaz en AsyncWcfLib, es compatible con Actor based programming model.

+0

¿Debería combinar 2 de sus respuestas en 1? –

6

Un servicio asincrónico que usa async-await es muy sensible ya que puede intercalar muchas llamadas de cliente y ejecutarlas en paralelo (2). A pesar de esto, el servicio puede ejecutarse completamente seguro para subprocesos en un subproceso (3) y puede ser un servicio singleton (1) o un objeto de servicio creado por el marco para una sesión o una llamada solamente.

Al implementar el servicio, tenga en cuenta los 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(...); 
     } 
    } 

Para ejecutar todas las llamadas en un hilo, un System.Threading.SynchronizationContext.Current = null debe! estar presente en el momento en que abra() el ServiceHost. Usando SynchronizationContext, no necesita preocuparse por los bloqueos. Las secciones de código atómicas no interrumpibles se extienden aproximadamente de una en espera a la siguiente. Tenga cuidado de que los datos de servicios compartidos estén en un estado constante en cada estado de alerta y tenga en cuenta que las solicitudes sucesivas de un cliente pueden responderse no en el orden en que se enviaron.

En el lado del cliente, la operación del servicio asíncrono es awaitable:

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

No es posible utilizar fuera o ref parámetros en el contrato de operación. Todos los datos de respuesta deben pasar por el valor devuelto Tarea (T).
Uso esta interfaz en AsyncWcfLib, es compatible con Actor based programming model.

1

Usted correctamente señala que async/await se construyen alrededor de los métodos que devuelven tarea y tarea (para una gran explicación del funcionamiento de async/await, vea here). Aquí es cómo generar métodos basados ​​en tareas para un servicio WCF:

  1. Visual Studio 2012 RC tiene una casilla de verificación adicional en el cuadro de diálogo "Configuración de Servicio de Referencia": "Generar operaciones basadas en tareas". Si bien no veo esa opción documentada en el MSDN, espero que exista específicamente para permitir llamadas asincrónicas/espera ininterrumpidas en WCF.

  2. Para versiones anteriores, echar un vistazo a this post, que describe una extensión que permite la generación de tareas <> Los métodos basados ​​en WCF incluso con la CTP.

Cuestiones relacionadas