Como comentei anteriormente, a Microsoft está fazendo grandes melhorias para a próxima versão do C#, com o intuito de facilitar a programação assíncrona dentro do .NET Framework. No artigo mencionado, lá temos como proceder para invocar um código de forma assíncrona neste novo modelo, que é através da utilização da keyword async no método que deverá ser invocado de forma assíncrona, e dentro dele, utilizamos a keyword await, para dizer que a região a seguir trata-se de um callback.
Como sabemos, os delegates já trazem nativamente suporte para que eles sejam invocados de forma síncrona ou assíncrona. Mas como o modelo de programação muda partir destas novas funcionalidades que estão sendo criadas no C#, ou seja, passaremos a utilizar a classe Task<T> para representar o processo assíncrono, então podemos recorrer a um método chamado FromAsync da classe TaskFactory, que foi incluído no .NET Framework a partir da versão 4.0. Fazendo uso deste método, podemos invocar um delegate de forma assíncrona utilizando o seguinte código:
Func<string, string> ping = s => s + ” ping!”;
var task =
Task.Factory.FromAsync<string, string>(
ping.BeginInvoke, ping.EndInvoke, “Teste”, null);
Como podemos perceber no código acima, informamos para o método FromAsync o par de métodos Begin/End que compõem a chamada assíncrona para uma tarefa, dando origem assim à uma instância da classe Task, que daqui em diante, podemos utilizar em conjunto com o novo modelo que está sendo proposto pela Microsoft.
Mas como podemos utilizar esta técnica para consumir serviços WCF? Como já comentei neste artigo, podemos optar durante a referência para o serviço na aplicação cliente (Add Service Reference), por marcar a opção “Generate asynchronous operations”, e com isso, para cada operação existente no serviço, serão criadas três métodos, sendo um para a chamada síncrona, e os outros dois irão compor a chamada assíncrona (BeginNomDaOperacao/EndNomeDaOperacao). Sabendo que há como transformar o modelo de Begin/End em Tasks, poderíamos utilizar o código acima para consumir o serviço WCF de forma assíncrona e recorrer ao novo modelo. Mas ao invés de termos todo esse trabalho, já há uma forma de fazer com que o proxy também seja gerado com as opções de Tasks.
Para isso, utilizamos uma implementação que a Microsoft criou chamada de TaskAsyncWsdlImportExtension. Essa classe intercepta a referência do serviço na aplicação cliente, e cria a versão assíncrona baseando-se em Tasks para todas as operações que ele encontra no documento WSDL. Para isso funcionar, devemos referenciar a DLL que consta essa classe no projeto cliente (ela está incluída nos exemplos do CTP). Depois disso, devemos modificar o arquivo de configuração para definirmos essa estensão, assim como é mostrado através do código abaixo:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
<system.serviceModel>
<client>
<metadata>
<wsdlImporters>
<extension type=”TaskWsdlImportExtension.TaskAsyncWsdlImportExtension, TaskWsdlImportExtension…” />
</wsdlImporters>
</metadata>
</client>
</system.serviceModel>
</configuration>
Em seguida, devemos efetuar a referência para o serviço e marcar a opção para a geração das operações assíncronas. Isso já é o suficiente para a classe TaskAsyncWsdlImportExtension entrar em ação e construir a nova versão deste método.
public partial class ContratoClient : ClientBase<IContrato>, IContrato
{
public Task<string> PingAsync(string value)
{
return Task<string>.Factory.FromAsync(
new Func<string, AsyncCallback, object, IAsyncResult>(((IContrato)(this)).BeginPing),
new Func<IAsyncResult, string>(((IContrato)(this)).EndPing), value, null);
}
}
A partir de agora, podemos efetivamente recorrer ao novo modelo de programação assíncrona do C#, ou seja, com a instância da classe Task em mãos, podemos fazer uso dela dentro de métodos marcados como async e, consequentemente, utilizar a keyword await para determinar o que queremos fazer com o resultado quando ele for devolvido. Note que o código fica bem mais sucinto quando comparado ao modelo Begin/End que fazemos atualmente.
private async static void InvocarServico()
{
var temp = await proxy.PingAsync(“Algum Valor”);
Console.WriteLine(“O resultado foi: {0}”, temp);
}