2009-12-11 39 views
6

Tengo una aplicación WPF en la que estoy trabajando actualmente para separarme del lado del cliente y del servidor: uso de WCF. No me gustó el lío que inicialmente recibí con la solución directa, así que ahora estoy reestructurando siguiendo las recomendaciones en el screencast de Miguel Castro, WCF Extreme. Si no está familiarizado con el video, básicamente configura toda la comunicación de forma manual, sin utilizar referencias de servicio. Esto incluye:Implementación de un servicio WCF asíncrono

  • un contrato común con todos los contratos de servicios y de datos - referenciado por el cliente y el servidor
  • Una aplicación de consola que aloja el servicio
  • clases de Proxy en la asignación de cliente hasta el servicio, y las llamadas que pasan (usando ClientBase o ClientFactory)

He seguido todos sus pasos, y realmente me gusta a dónde va esto. Sin embargo, no atiende llamadas de servicio asíncronas, y esto es lo que me gustaría usar.

Al agregar una referencia de servicio, puedo marcar la casilla "Generar operaciones asíncronas" y obtengo MyServiceCompleted y MyServiceAsync. Sin embargo, supongo que esto es algo que se generó al agregar la referencia del servicio, y no algo de magia en las clases en las que se basa.

Entonces, ¿puedo obtener operaciones asincrónicas de ClientBase o ClientFactory de alguna manera? ¿O tengo que definir los servicios del lado del servidor real para ser asíncrono? Si es así, ¿podría alguien darme algunos consejos o ejemplos sobre cómo comenzar con un servicio asincrónico simple? He estado leyendo en MSDN sobre el tema, pero me ha dejado confundido, sintiéndome como un idiota por no haberlo conseguido ya ...

Respuesta

5

La implementación de las operaciones asincrónicas en el lado del servidor es bastante simple. Asegúrese de que los nombres de los métodos coincidan y tengan el prefijo Begin y End. GetImageAsyncResult es una implementación personalizada de IAsyncResult (muchos ejemplos en la web).

public class MapProvider : IMapProvider //implementation - belongs to server 
    { 
     public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state) 
     { 
       GetImageAsyncResult asyncResult = new GetImageAsyncResult(level, x, y, layers, callback, state); 
       ThreadPool.QueueUserWorkItem(Callback, asyncResult); 
       return asyncResult; 
     } 

     private void Callback(object state) 
     { 

       GetImageAsyncResult asyncResult = state as GetImageAsyncResult; 
       asyncResult.Image = TileProvider.GetImage(asyncResult.Level, asyncResult.X, asyncResult.Y, asyncResult.Layers); 
       asyncResult.Complete(); 
     } 

     public System.Drawing.Bitmap EndGetImage(IAsyncResult result) 
     { 
       using (GetImageAsyncResult asyncResult = result as GetImageAsyncResult) 
       { 
        asyncResult.AsyncWait.WaitOne(); 
        return asyncResult.Image; 
       } 
     } 
    } 

    public class MapProviderProxy : ClientBase<IMapProvider>, IMapProvider, IDisposable 
    { 
     public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state) 
     { 
       return Channel.BeginGetImage(level, x, y, layers, callback, state); 
     } 

     public System.Drawing.Bitmap EndGetImage(IAsyncResult result) 
     { 
       return Channel.EndGetImage(result); 
     } 

     public void Dispose() 
     { 
       if (State == CommunicationState.Faulted) 
       { 
        Abort(); 
       } 
       else 
       { 
        try 
        { 
         Close(); 
        } 
        catch 
        { 
         Abort(); 
        } 
       } 
     } 
    } 
+0

¡Gracias! Y el IMapProvider tendrá las funciones BeginGetImage y EndGetImage definidas, etiquetadas con "[OperationContract (AsyncPattern = true)]"? Y necesito definir el resultado específico de esto, GetImageAsyncResult? – stiank81

+0

Correcto - Encontré una clase base AsyncResult en la web. GetImageAsyncResult se deriva de eso. – Goran

+0

Bien, esto responde mi pregunta. Sin embargo, me he dado cuenta de que no quiero que el servicio sea asincrónico, pero quiero llamar al servicio síncrono de forma asíncrona. Es decir. Lo manejaré en el lado del cliente. Resuelvo esto fácilmente usando AsyncMethodCaller. ¡Gracias de cualquier manera! – stiank81

3

Cuando seleccionas "Generar operaciones asíncronas", como has notado, no hay Magia involucrada: Begin... iniciará su comunicación, el servidor comienza a procesar cosas, pero no puede usar nada hasta que llame al End....

Todo este comportamiento ocurre en el lado del cliente, por lo que no necesita cambiar nada en la implementación de su servicio.

Probablemente esté pensando que esto debe ser compleja, pero no lo hace;)

EDIT: Aquí van un ejemplo:

using (Service.SampleClient client = new Service.SampleClient()) 
{ 
    client.AddCompleted += 
     (object sender, Service.AddCompletedEventArgs e) 
      { 
       Console.WriteLine(e.Result); // 2 
      }; 
    client.AddAsync(1, 1); 

    // wait for async callback 
    Console.ReadLine(); 
} 

[ServiceContract] 
public interface ISample 
{ 
    [OperationContract] 
    int Add(int a, int b); 
} 

También puede programar de forma explícita su servicio para trabajar asíncrono , como se describe aquí: Synchronous and Asynchronous Operations, mediante el uso de [OperationContract(AsyncPattern=true)]

+0

Ehr .. ¿Podrías por favor dar más detalles? ¿Estás diciendo que puedo dejar los servicios intactos e implementar el manejo asíncrono en el lado del cliente? Sí, hay un alto riesgo de ver esto como más complejo, entonces es :-P – stiank81

+0

ok, estoy trabajando en un ejemplo, espera =) –

+0

Gracias, lo agradezco :-) – stiank81

3

Una forma alternativa para lograr operaciones asíncronas en el lado del cliente sin utilizar el svcutil es la creación de una interfaz (ServiceContract) localmente en el lado del cliente que implementa el funcionamiento asíncrono.

del contrato en el lado del servidor:

[ServiceContract] 
public interface IServerContract 
{ 
    [OperationContract] 
    string GetData(int value); 
} 

contrato asíncrono en el lado del cliente

[ServiceContract(Namespace = "http://tempuri.org/", Name = "IServerContract")] 
public interface IClientContractAsync 
{ 
     [OperationContract] 
     Task<string> GetDataAsync(int value); 
} 

Tenga en cuenta que el nombre y el espacio de nombres por defecto tiene que ser establecido en el contrato del cliente con el fin de igualar el espacio de nombres del contrato del servidor. Entonces ahora tienes una operación asíncrona (sin comenzar ningún nuevo subproceso con suerte). De esta forma, no tiene que hacer ninguna implementación específica en el lado del servidor. Por supuesto, esto es similar a lo que hace el SvcUtil pero SvcUtil genera mucho código adicional y, a veces, veo que svcutil causa problemas, p. con la reutilización de clases.

El ClientProxy

public class ClientProxy : ClientBase<IClientContractAsync> 
{ 
    public IClientContractAsync ChannelOut 
    { 
     get 
     { 
      return Channel; 
     } 
    } 
} 

El uso del cliente:

static void Main(string[] args) 
{ 
    var client = new ClientProxy(); 
    client.Open(); 
    var asyncReadValue = client.ChannelOut.GetDataAsync(45).Result; 
    Console.WriteLine(asyncReadValue); 
    Console.ReadLine(); 
} 

El centro de servicios de clase

public class ServerService : IServerContract 
{ 
    public string GetData(int value) 
    { 
     return string.Format("You entered: {0}", value); 
    } 
} 

El servidor

static void Main(string[] args) 
{ 
    var server = new ServiceHost(typeof(ServerService)); 
    server.Open(); 
    Console.WriteLine("started"); 
    Console.ReadLine(); 
}