Em geral, podemos consumir os serviços WCF de duas formas: síncrona ou assíncrona. Na primeira opção, ao referenciar o serviço em uma aplicação cliente e invocar um das operações que ele disponibiliza, a comunicação será realizada e enquanto ela não retorna, a aplicação ficará bloqueada aguardando o resultado. Já no segundo modelo, ao realizar a chamada de forma assíncrona, a operação será realizada em um thread secundária, permitindo que a aplicação continue trabalhando enquanto o serviço é executado.
Como sabemos, o Silverlight tem cada vez mais espaço como sendo front-end de aplicações. Essa tecnologia recorre à serviços quando precisa buscar algum conteúdo remoto, como por exemplo, preencher os dados em um controle ListBox quando um botão for pressionado. Atualmente, a maioria desses serviços são construídos em WCF, e referenciados na aplicação Silverlight para permitir que a mesma interaja com o servidor para extrair os dados necessários para executar o seu trabalho.
Para aqueles que já utilizam essa técnica, já devem ter percebido que no Silverlight, só podemos consumir esses serviços de forma assíncrona, ou seja, recorrendo à uma segunda thread através do par de métodos BeginXXX/EndXXX ou através de eventos. Mas porque isso acontece ou porque é necessário?
O Silverlight possui apenas uma única thread, que é chamada de UI Thread, e como sabemos, operações de I/O bound, como é o caso da comunicação através da rede, são tarefas custosas e que podem levar um grande tempo para ser executada, e justamente por isso, se o Silverlight bloqueasse a thread de UI enquanto executa essa requisição, o host (que é o navegador), também seria bloqueado. Mesmo que você tente emular uma chamada síncrona, utilizando algum recurso primitivo de sincronização (como o ManualResetEvent), você teria problemas do mesmo jeito, já que quando invocar o método WaitOne desta classe, ele bloqueará a thread de UI para esperar o resultado, que nunca chegará, pois o resultado somente será entregue para o Silverlight quando a thread de UI não estiver executando nenhum código, que não é o caso aqui, e como já era de se esperar, teremos um deadlock.
Desde o proxy de serviços WCF até classes de baixo nível, como é o caso da WebClient, terão o mesmo comportamento, ou seja, deverão ser sempre acionadas através do modelo assíncrono. Tudo isso se deve ao fato de que os navegadores atuais implementam a NPAPI (Netscape Plugin Application Programming Interface). A NPAPI trata-se de uma API multi-plataforma desenvolvida pela Netscape que permite que plugins sejam utilizados dentro dos navegadores. Para que os plugins sejam considerados multi-plataforma, eles precisam seguir rigorosamente essa API, o que determina que métodos remotos sejam executados assincronamente, e como já percebemos, o Silverlight segue o que foi definido por ela.
Entendi, a implementação da interface INotifyPropertyChange é que propiciou o controle ser atualizado após o término da query.
Verifiquei na documentação que a classe DomainClient possui vários métodos BeginXXX/EndXXX. Bem interessante!
Obrigado pela explicação.
Um abraço!
Olá Israel,
Veja o exemplo desse link http://bit.ly/90jWEy. Como eu disse essas operações "aparentemente" parecem ser síncronas. Veja que não foi necessário os pares BeginXXX/EndXXX ou até mesmo um XXXAsync/XXXCompleted. Existe algo no DomainContext que encapsula essa chamada assíncrona?
Obs: Como eu disse não conheço muito bem o WCF RIA Services.
Um abraço.
Boas Ari,
Aparentemente, você tem razão, já que invocamos o método Load sem ver explicitamente o processo assíncrono.
Na verdade, toda a complexidade está abstraída pela classe DomainContext. Internamente, em um overload específico deste mesmo método, você notará que ele criará e retornará a instância de uma classe do tipo LoadOperation.
Essa classe recebe a query a ser disparada e, internamente, faz uso de uma terceira classe, chamada de DomainClient, que expõe dois métodos autoexplicativos: BeginQuery e EndQuery, que são utilizados para realizar o processamento (query) assincronamente.
Definimos o DataSource de um controle com a propriedade Entities da LoadOperation, que retorna a instância de uma coleção do tipo ObservableCollection<T>, com as itens da serão/foram retornados pela query. A classe LoadOperation implementa indiretamente a interface INotifyPropertyChanged, que como sabemos, notifica de que alguma propriedade dentro dela foi alterada. Com isso, o controle será notificado da mudança, e irá recarregar o mesmo.