WCF è un framework concepito per sviluppare scenari di comunicazione completamente disaccoppiati sotto ogni punto di vista. In particolare, per quanto riguarda la sincronizzazione dei messaggi, è prevista internamente una rigida implementazione di pattern asincroni per ottenere chiamate non bloccanti tra i vari livelli strutturali. Esternamente, invece, WCF espone possibilità di programmazione asincrona sia client-side che server-side. L'aspetto interessante da approfondire in merito è come il programming model asincrono client-side non abbia nulla a che vedere con quello server-side: ovvero, un client può sostanzialmente invocare in modo asincrono un servizio indipendentemente dal fatto che esso sia implementato con un pattern sincrono piuttosto che asincrono.
Il disaccoppiamento avviene infatti a livello di trasporto quando i dati vengono serializzati/deserializzati.
Per capire l’importanza di tale feature pensiamo semplicemente ad un’ architettura N-tier che poggia su WCF: potremmo avere diverse “operation” che a loro volta utilizzano proxy client verso altre operation WCF, rendendo vitale l’utilizzo di pattern di comunicazione asincroni.
Vediamo un semplice esempio. Supponiamo di avere il seguente ServiceContract:
[ServiceContract]
public interface IAuthentication
{
[OperationContract]
UserInfo Login(string username, string password);
[OperationContract]
void Logout(string username);
}
Se lato client utilizzassimo Svcutil ( con gli opportuni parametri ) o il solito “Add Service Reference” di VisualStudio, otterremmo la generazione automatica del seguente object model:
Come si può osservare abbiamo una versione asincrona client-side del ServiceContract (IAuthentication), un' interfaccia <ServiceContract>Channel (IAuthenticationChannel) ed una classe Client (AuthenticationClient) che espone metodi di invocazione sia asincroni che sincroni. Nello specifico, si prospettano due possibili modalità di invocazione asincrona client-side di un’ operation:
1. Invocazione asincrona “event-based” (o “event-driven”)
E' la modalità più semplice e raccomandata poiché richede solamente l’aggiunta di un EventHandler per ricevere una notifica all’occorrenza di una risposta. Questa modalità ( disponibile solo nel framework 3.5 e comunque solo per questo tipo di invocazione ) permette di sfruttare l’approccio asincrono event-based tramite un metodo nella forma <operationName>Async e l’intercettazione di un evento <operationName>Completed al cui interno si ha accesso al risultato dell’invocazione <operationName>CompletedEventArgs.
...
ServiceReference.AuthenticationClient client = new ServiceReference.AuthenticationClient();
client.LoginCompleted += new EventHandler<ServiceReference.LoginCompletedEventArgs>(client_LoginCompleted);
client.LoginAsync("dario.santarelli", "password");
Console.ReadLine();
client.Close();
...
protected void client_LoginCompleted(object sender, ServiceReference.LoginCompletedEventArgs e)
{
UserInfo info = e.Result as UserInfo;
Console.WriteLine(string.Format("UserName: {0} - Password: {1}",info.UserName,info.Email));
}
2. Invocazione asincrona via ChannelFactory
Il pattern asincrono previsto in questa modalità è quello classico previsto a partire dal framework 1.1: tramite lo split di un operation in due metodi ( Begin<operationName> e End<operationName> ), si sfruttano oggetti che implementano l’interfaccia System.IAsyncResult per rappresentare lo stato di una operazione asincrona.
...
EndpointAddress endpointAddress = new EndpointAddress("http://.../AuthenticationService.svc");
ServiceReference.IAuthenticationChannel channelClient = ChannelFactory<ServiceReference.IAuthenticationChannel>.CreateChannel(new BasicHttpBinding(), endpointAddress);
IAsyncResult result = channelClient.BeginLogin("dario.santarelli", "password", LoginCallBack, channelClient);
Console.ReadLine();
channelClient.Close();
channelClient.Dispose();
...
protected void LoginCallBack(IAsyncResult ar)
{
UserInfo result = ((ServiceReference.IAuthenticationChannel)ar.AsyncState).EndLogin(ar);
Console.WriteLine("Result: {0}", result.UserName);
}
Conclusioni
In entrambe le soluzioni presentate, una nostra application può invocare un’operazione in maniera asincrona anche se il servizio è implementato in maniera sincrona, allo stesso modo con cui una applicazione può usare lo stesso pattern per invocare in maniera asincrona un metodo sincrono locale. “Come” è poi implementato l’ OperationContract è assolutamente insignificante per il client.
Risorse MSDN:
Technorati Tag:
WCF,
Async